Fossil

Check-in [d0305b30]
Login

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

Overview
Comment:Merged mainline into my branch to get the newest application.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:d0305b305ad0a15a1aa38dbd1830bc9b7654eb69
User & Date: aku 2007-12-05 08:07:46
Context
2007-12-06
03:48
Fixed handling of empty revisions. check-in: bf0b70d5 user: aku tags: trunk
2007-12-05
08:07
Merged mainline into my branch to get the newest application. check-in: d0305b30 user: aku tags: trunk
07:58
Bugfix. Translation implies encoding, not the reverse. This caused problems when parsing files with mixed-mode line-endings. The generated char offsets and lengths were off. Found during expansion. check-in: 6f1c4424 user: aku tags: trunk
2007-12-04
02:47
Add the timeline display preferences page with the ability to turn on and off block markup in timeline comments and to limit the length of timeline comments. check-in: ebb27659 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added Makefile.w32.

































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/usr/bin/make
#
#### The toplevel directory of the source tree.  Fossil can be built
#    in a directory that is separate from the source tree.  Just change
#    the following to point from the build directory to the src/ folder.
#
SRCDIR = ../src

#### C Compiler and options for use in building executables that
#    will run on the platform that is doing the build.  This is used
#    to compile code-generator programs as part of the build process.
#    See TCC below for the C compiler for building the finished binary.
#
BCC = gcc -g -O2

#### The suffix to add to executable files.  ".exe" for windows.
#    Nothing for unix.
#
E =

#### C Compile and options for use in building executables that 
#    will run on the target platform.  This is usually the same
#    as BCC, unless you are cross-compiling.  This C compiler builds
#    the finished binary for fossil.  The BCC compiler above is used
#    for building intermediate code-generator tools.
#
#TCC = gcc -O6
#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
#TCC = gcc -g -Os -Wall
TCC = gcc -g -Os -Wall -DFOSSIL_I18N=0 -L/usr/local/lib -I/usr/local/include

#### Extra arguments for linking the finished binary.  Fossil needs
#    to link against the Z-Lib compression library.  There are no
#    other dependencies.  We sometimes add the -static option here
#    so that we can build a static executable that will run in a
#    chroot jail.
#
#LIB = -lz
LIB = -lz -lws2_32


#### Tcl shell for use in running the fossil testsuite.
#
TCLSH = tclsh

# You should not need to change anything below this line
###############################################################################
include $(SRCDIR)/main.mk

Changes to ideas.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

















101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147


148
149




150
151
152
153
154
155
156
157
158
159
160
161
...
182
183
184
185
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
...
223
224
225
226
227
228
229
230
231
232
233
234

235
236
237
238
239
240
241

242
243
244
245
246
247
248
249
Possible ticket file format:

   "Ticket"
   title: TEXT
   ticketid: TEXT
   exists-in: BASELINE   -- 0 or more
   fixed-in: BASELINE    -- 0 or more
   tag: TAG              -- 0 or more
   created: DATETIME
   attachment: FILENAME DESCRIPTION
   parent: UUID*
   derived-from: TICKET-FILENAME
   description: MULTILINE-TEXT
   remarks: MULTILINE-TEXT

   * Things handles with tags:
     created-by
     assigned-to
     priority
     severity
     target-release
     status
     resolution
     type
     subsystem

Wiki header format:
   "WikiPage"
   parent: UUID*
   title: TEXT
   pagename: TEXT
   mode: (readonly|appendonly|readwrite)
   attachment: UUID name description

   * Header ends with a blank line.  wiki content follows.

Cluster format:

       M+ uuid
       Z manifest-cksum

   * Cluster generated in server mode only.
   * Embargo cluster that reference phantoms or other embargoed clusters.
   * Never send or ihave an embargoed cluster

New sync algorithm based on clusters:

   * Keep a table of unclustered artifacts.  Strive to keep this table
     less than 100 entries.
   * Client sends content of unclustered table as ihaves to server
   * Server builds a new cluster if size of cluster table >100.
   * Server sends unclustered table to client
   * Server sends gimme for all unknown ihave received from client
   * Client sends gimme for all unknown ihave received from server
   * Previous two steps repeat until no more gimmes

Details of new push algorithm:

   * Table "unsent" contains all files never pushed
   * TEMP table "wanted" contains files the server does not have
   Loop:
     * Client sends login and "push" record
     * Client sends file message for all files in unsent and removes
       those files from the table.
     * Client sends file message for all files in wanted.
     * Client sends ihave messages for each entry in unclustered
     ------
     * Server receives file message
     * Server creates phantoms for unknown ihaves
     * Server sends gimme messages for all phantoms
     ------
     * Client clears its unsent table
     * For each gimme message add an entry to wanted
     * Halt if the wanted table is empty

Details on new pull algorithm:

   Loop:
     * Client sends login and "pull" record
     * Client sends "prior" message with repository id and max record number
     * Client sends "gimme" for each phantom
     --------
     * Server creates new clusters to get unclustered size below 100
     * If there is "prior" message with repository id that matches this
       server, then send file messages for all record ids greater than
       prior
     * Server sends ihave messages for each entry in unclustered
     * Server sends maxrid message
     --------
     * Client receives file records
     * Client creates phantoms for unknown ihaves
     * If no phantoms exist, record maxrid for the server and halt

Need a dephantomize algorithm


Auxiliary tables needed for new sync algorithm:

   * unsent:  files that have never been sent to another repository
   * unclustered: non-phantom files not mentioned by a cluster


















Random thoughts:

  *  Changes to manifest to support:
     +  Trees of wiki pages and tickets
     +  The ability to cap or close a branch
     +  See "Extended Manifests" below

  *  Add the concept of "clusters" to speed the transfer of "tips"
     on a sync.

  *  Auxiliary tables:
     +  tip
     +  phantom
     +  mlink
     +  plink
     +  branch
     +  tree

  * Plink.isprim changed to record:
     +  child is the principal descendent of parent. (1)
     +  child is a branch from parent (2)
     +  child uses parent as a merge (0)

  * tree records
     + type  (code, wiki, ticket)
     + name  (for wiki and ticket only)
     + treeid

  * branch records
     + treeid
     + origin_rid
     + origin_time
     + tip_rid
     + tip_time
     + color

  * website can toggle isprim between principal and branch.
     + How to preserve across rebuild.  A new record type?
     + How to share with other repositories
  * isprim guessed using userid of parent and child.  Change
    in id suggests a branch.  Same id suggests principal.
    For a tie, go with the earliest check-in as the principal'

  * Autosync mode
     + Set a preferred remote repository to use as a server
        =  Clone repository is the default


     + On commit, first pull.  If commit baseline is not a tip
       prompt user to cancel or branch.  Default is cancel.




     + Push after commit
     + Automatically pull prior to update.
     + Need an "undo" capability
     + Designed to avoid branching in highly collaborative 
       environments.

  * Archeological webpage improvements:
     + Use a small amount of CSS+javascript on timelines so that
       branching structure is displayed on mouseover.  On mouseover
       of a checkin, highlight other checkins that are direct (non-merge)
       descendents and ancestors of the mouseover checkin.
     + Timeline showing individual branches
................................................................................
       P uuid ...           -- omitted for first manifest
       R repository-md5sum
       U user-login
       Z manifest-checksum

  * Accessory:
       A uuid|* attachment-uuid description
       B (+|-)branchtag uuid
       D date-time
       E uuid new-comment
       G uuid appended-remark
       S repositoryid serial-number

       U userid
       V (+|-)versiontag uuid
       X uuid-to-surpress
       Z this-file-checksum

  * Change the comment on a version:   -- always a leaf except in cluster
       D date-time
       E new-comment
       P uuid              -- baseline whose comment is changed
................................................................................
  * Set or erase a tag    -- most recent date wins
       B* (+|-)tag uuid
       C? comment
       D date-time
       V* (+|-) tag uuid    -- + to set, - to clear.  
       Z manifest-cksum
       -- Must have at least one B or V.
       -- Tag "hidden" means do not sync
       -- Tag "closed" means do not display as a leaf
  * A cluster
       M+ uuid
       Z manifest-cksum

  * Complete set of cards in a manifest files:
       A filename uuid
       B (+|-)branch-tag uuid
       C comment
       D date-time
       E edited-comment
       F filename uuid

       N name
       P uuid ...
       R repository-md5sum
       T uuid
       U user-login
       V (+|-)version-tag uuid
       W uuid
       Z manifest-checksum
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





<
<
<
<
<
<
<
<
<
<
<
<
<








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







 







<




>

<







 







|
|



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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
















121
122
123
124
125













126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145



146
147
148
149
150
151
152
...
173
174
175
176
177
178
179

180
181
182
183
184
185

186
187
188
189
190
191
192
...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

Possible ticket file format:

   A uuid name description
   D datetime
   J field value
   K uuid
   U user
   Z md5sum
   
FIELDs:

   comment         cumulative text
   title           text
   assignedto      text
   status          enum
   resolution      enum
   subsystem       enum
   type            enum
   priority        enum
   severity        enum
   deferuntil      datetime
   duedate         datetime
   derivedfrom     add or subtract uuid
   relatedversions add or subtract associate with manifest
   presentin       add or subtract uuid
   fixedin         add or subtract uuid

Other table columns:
   origintime
   lastchange


Field Types:

   text                 width height
   enum                 width valuelist
   datetime             width
   cumulative-text      width height
   set-of-uuid          width
   set-of-checkin       width

Tables:

   tktrid(rid, tkid, mtime);  index(tkid, mtime);
   ticket(tkid, tkuuid UNIQUE, starttime, lastmod, ...);
   tktfield(fieldname UNIQUE, type, width, other);
   tktxref(tkid, mid); index(tkid); index(mid);

Tktformat in the config table.

   * Three pages:  creation, display, and edit
   * Separate global and local versions of each page.  Local overwrites
     global if it exists.
   * HTML
   * [[field]] to substitute the appropriate form or display element

Ticket Configuraiton File:

   * Format:
      ticket-configuration
      field <fieldname> <type> <param> ...
      template <type> <delimiter>
      <text>
   * Each repository selects a single ticket config for its own use.
   * Rescan all tickets following any config change
   * Use the ticket-configuration tag must be on the file

Todo:

   * Configuration file parser
       + tkt-new-template
       + tkt-view-template
       + tkt-edit-template
       + Reconstruct the ticket table schema
       + Repopulate tktfield
   * Ticket control file parser
       + Create ticket entry if necessary
       + Update fields.  Ignore unrecognized fields.
   * Transfer tkt control info to ticket table
   * Setup pages for selecting and editing ticket configuration
   * Ticket display
   * New ticket creation display
   * Ticket change screen
   * Ticket query screens


------------------------------------------------------------------------
Change to wiki:

   A uuid filename description
   D datetime
   P uuid ...
   U user
   W size \n text \n
   Z cksum

Hyperlinks:

   [lowercasehex]       /info/lowercasehex
   [attachment.gif]     inline image
   [tagname]            /info/tagname
   [wikipagename]       /wiki/wikipagename
   [/internal/page]     /internal/page
   [http:...]           external link

Markup:

   blank-line           paragraph break
   _*__                 bullet
   __                   indentation
   _#.__                enumeration
   *text*               bold
   _text_               italic


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

Random thoughts:

















  * Plink.isprim changed to record:
     +  child is the principal descendent of parent. (1)
     +  child is a branch from parent (2)
     +  child uses parent as a merge (0)














  * website can toggle isprim between principal and branch.
     + How to preserve across rebuild.  A new record type?
     + How to share with other repositories
  * isprim guessed using userid of parent and child.  Change
    in id suggests a branch.  Same id suggests principal.
    For a tie, go with the earliest check-in as the principal'

  * Autosync mode
     * Notes:
       + Designed to avoid branching in highly collaborative 
         environments.
     * Outstanding:
       + On commit, first pull.  If commit baseline is not a tip
         prompt user to cancel or branch.  Default is cancel.
       + Need an "undo" capability
     * Done:
       * Set a preferred remote repository to use as a server
          =  Clone repository is the default
       * Push after commit
       * Automatically pull prior to update.




  * Archeological webpage improvements:
     + Use a small amount of CSS+javascript on timelines so that
       branching structure is displayed on mouseover.  On mouseover
       of a checkin, highlight other checkins that are direct (non-merge)
       descendents and ancestors of the mouseover checkin.
     + Timeline showing individual branches
................................................................................
       P uuid ...           -- omitted for first manifest
       R repository-md5sum
       U user-login
       Z manifest-checksum

  * Accessory:
       A uuid|* attachment-uuid description

       D date-time
       E uuid new-comment
       G uuid appended-remark
       S repositoryid serial-number
       T (+|-)tag uuid
       U userid

       X uuid-to-surpress
       Z this-file-checksum

  * Change the comment on a version:   -- always a leaf except in cluster
       D date-time
       E new-comment
       P uuid              -- baseline whose comment is changed
................................................................................
  * Set or erase a tag    -- most recent date wins
       B* (+|-)tag uuid
       C? comment
       D date-time
       V* (+|-) tag uuid    -- + to set, - to clear.  
       Z manifest-cksum
       -- Must have at least one B or V.
       -- Branch tag "hidden" means do not sync
       -- Version tag "closed" means do not display as a leaf
  * A cluster
       M+ uuid
       Z manifest-cksum

  * Complete set of cards in a control file:
       A filename uuid             
       B (+|-)branch-tag uuid      
       C comment                   
       D date-time                 
       E uuid edited-comment       
       F filename uuid             
       M uuid                      
       N name                      
       P uuid ...                  
       R repository-md5sum         
       T uuid                      
       U user-login                
       V (+|-)version-tag uuid     
       W uuid                      
       Z manifest-checksum

Changes to src/add.c.

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



69
70
71
72
73
74
75
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  db_begin_transaction();
  for(i=2; i<g.argc; i++){
    char *zName;
    char *zPath;
    Blob pathname;
    int isDir;

    zName = mprintf("%s", g.argv[i]);
    isDir = file_isdir(zName);
    if( isDir==1 ) continue;
    if( isDir==0 ){
      fossil_fatal("not found: %s", zName);
    }
    if( isDir==2 && access(zName, R_OK) ){
      fossil_fatal("cannot open %s", zName);
    }
    file_tree_name(zName, &pathname);
    zPath = blob_str(&pathname);
    if( strcmp(zPath, "manifest")==0 || strcmp(zPath, "_FOSSIL_")==0 ){
      fossil_fatal("cannot add %s", zPath);
    }



    if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){
      db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath);
    }else{
      db_multi_exec(
        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname)"
        "VALUES(%d,0,0,0,%Q)", vid, zPath);
    }
................................................................................
  }
  db_begin_transaction();
  for(i=2; i<g.argc; i++){
    char *zName;
    char *zPath;
    Blob pathname;

    zName = mprintf("%s", g.argv[i]);
    file_tree_name(zName, &pathname);
    zPath = blob_str(&pathname);
    if( !db_exists(
             "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){
      fossil_fatal("not in the repository: %s", zName);
    }else{
      db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zPath);







|













>
>
>







 







|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  db_begin_transaction();
  for(i=2; i<g.argc; i++){
    char *zName;
    char *zPath;
    Blob pathname;
    int isDir;

    zName = mprintf("%/", g.argv[i]);
    isDir = file_isdir(zName);
    if( isDir==1 ) continue;
    if( isDir==0 ){
      fossil_fatal("not found: %s", zName);
    }
    if( isDir==2 && access(zName, R_OK) ){
      fossil_fatal("cannot open %s", zName);
    }
    file_tree_name(zName, &pathname);
    zPath = blob_str(&pathname);
    if( strcmp(zPath, "manifest")==0 || strcmp(zPath, "_FOSSIL_")==0 ){
      fossil_fatal("cannot add %s", zPath);
    }
    if( !file_is_simple_pathname(zPath) ){
      fossil_fatal("filename contains illegal characters: %s", zPath);
    }
    if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){
      db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath);
    }else{
      db_multi_exec(
        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname)"
        "VALUES(%d,0,0,0,%Q)", vid, zPath);
    }
................................................................................
  }
  db_begin_transaction();
  for(i=2; i<g.argc; i++){
    char *zName;
    char *zPath;
    Blob pathname;

    zName = mprintf("%/", g.argv[i]);
    file_tree_name(zName, &pathname);
    zPath = blob_str(&pathname);
    if( !db_exists(
             "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){
      fossil_fatal("not in the repository: %s", zName);
    }else{
      db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zPath);

Changes to src/blob.c.

47
48
49
50
51
52
53







54
55
56
57
58
59
60
...
337
338
339
340
341
342
343



























344
345
346
347
348
349
350
...
364
365
366
367
368
369
370















371
372
373
374
375



376
377
378
379
380
381
382
...
400
401
402
403
404
405
406






























407
408
409
410
411
412
413
...
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
...
569
570
571
572
573
574
575








576
577
578



579
580
581
582
583
584
585
#define blob_size(X)  ((X)->nUsed)

/*
** The buffer holding the blob data
*/
#define blob_buffer(X)  ((X)->aData)








#endif /* INTERFACE */

/*
** Make sure a blob is initialized
*/
#define blob_is_init(x) \
  assert((x)->xRealloc==blobReallocMalloc || (x)->xRealloc==blobReallocStatic)
................................................................................

/*
** Rewind the cursor on a blob back to the beginning.
*/
void blob_rewind(Blob *p){
  p->iCursor = 0;
}




























/*
** Extract a single line of text from pFrom beginning at the current 
** cursor location and use that line of text to initialize pTo.
** pTo will include the terminating \n.  Return the number of bytes
** in the line including the \n at the end.  0 is returned at
** end-of-file.
................................................................................
  if( i<n ){
    assert( aData[i]=='\n' );
    i++;
  }
  blob_extract(pFrom, i-pFrom->iCursor, pTo);
  return pTo->nUsed;
}
















/*
** Extract a single token from pFrom and use it to initialize pTo.
** Return the number of bytes in the token.  If no token is found,
** return 0.



**
** The cursor of pFrom is left pointing at the first character past
** the end of the token.
**
** pTo will be an ephermeral blob.  If pFrom changes, it might alter
** pTo as well.
*/
................................................................................
*/
int blob_tail(Blob *pFrom, Blob *pTo){
  int iCursor = pFrom->iCursor;
  blob_extract(pFrom, pFrom->nUsed-pFrom->iCursor, pTo);
  pFrom->iCursor = iCursor;
  return pTo->nUsed;
}































/*
** Return true if the blob contains a valid UUID_SIZE-digit base16 identifier.
*/
int blob_is_uuid(Blob *pBlob){
  return blob_size(pBlob)==UUID_SIZE
         && validate16(blob_buffer(pBlob), UUID_SIZE);
................................................................................
*/
int blob_tokenize(Blob *pIn, Blob *aToken, int nToken){
  int i;
  for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){}
  return i;
}

/* 
** This function implements the callback from vxprintf. 
**
** This routine add nNewChar characters of text in zNewText to
** the Blob structure pointed to by "arg".
*/
static void bout(void *arg, const char *zNewText, int nNewChar){
  Blob *pBlob = (Blob*)arg;
  blob_append(pBlob, zNewText, nNewChar);
}

/*
** Do printf-style string rendering and append the results to a blob.
*/
void blob_appendf(Blob *pBlob, const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
  vxprintf(bout, pBlob, zFormat, ap);
  va_end(ap);
}
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
  vxprintf(bout, pBlob, zFormat, ap);
}

/*
** Initalize a blob to the data on an input channel.  Return 
** the number of bytes read into the blob.  Any prior content
** of the blob is discarded, not freed.
*/
................................................................................
      zName = zBuf;
      strcpy(zName, zFilename);
    }
    nName = file_simplify_name(zName, nName);
    for(i=1; i<nName; i++){
      if( zName[i]=='/' ){
        zName[i] = 0;








        if( file_mkdir(zName, 1) ){
          fossil_panic("unable to create directory %s", zName);
        }



        zName[i] = '/';
      }
    }
    out = fopen(zName, "wb");
    if( out==0 ){
      fossil_panic("unable to open file \"%s\" for writing", zName);
    }







>
>
>
>
>
>
>







 







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







 







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





>
>
>







 







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







 







<
<
<
<
<
<
<
<
<
<
<






|



|







 







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







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
...
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
...
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
...
537
538
539
540
541
542
543











544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
...
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
#define blob_size(X)  ((X)->nUsed)

/*
** The buffer holding the blob data
*/
#define blob_buffer(X)  ((X)->aData)

/*
** Seek whence parameter values
*/
#define BLOB_SEEK_SET 1
#define BLOB_SEEK_CUR 2
#define BLOB_SEEK_END 3

#endif /* INTERFACE */

/*
** Make sure a blob is initialized
*/
#define blob_is_init(x) \
  assert((x)->xRealloc==blobReallocMalloc || (x)->xRealloc==blobReallocStatic)
................................................................................

/*
** Rewind the cursor on a blob back to the beginning.
*/
void blob_rewind(Blob *p){
  p->iCursor = 0;
}

/*
** Seek the cursor in a blob to the indicated offset.
*/
int blob_seek(Blob *p, int offset, int whence){
  if( whence==BLOB_SEEK_SET ){
    p->iCursor = offset;
  }else if( whence==BLOB_SEEK_CUR ){
    p->iCursor += offset;
  }else if( whence==BLOB_SEEK_END ){
    p->iCursor = p->nUsed + offset - 1;
  }
  if( p->iCursor<0 ){
    p->iCursor = 0;
  }
  if( p->iCursor>p->nUsed ){
    p->iCursor = p->nUsed;
  }
  return p->iCursor;
}

/*
** Return the current offset into the blob
*/
int blob_tell(Blob *p){
  return p->iCursor;
}

/*
** Extract a single line of text from pFrom beginning at the current 
** cursor location and use that line of text to initialize pTo.
** pTo will include the terminating \n.  Return the number of bytes
** in the line including the \n at the end.  0 is returned at
** end-of-file.
................................................................................
  if( i<n ){
    assert( aData[i]=='\n' );
    i++;
  }
  blob_extract(pFrom, i-pFrom->iCursor, pTo);
  return pTo->nUsed;
}

/*
** Trim whitespace off of the end of a blob.  Return the number
** of characters remaining.
**
** All this does is reduce the length counter.  This routine does
** not insert a new zero terminator.
*/
int blob_trim(Blob *p){
  char *z = p->aData;
  int n = p->nUsed;
  while( n>0 && isspace(z[n-1]) ){ n--; }
  p->nUsed = n;
  return n;
}

/*
** Extract a single token from pFrom and use it to initialize pTo.
** Return the number of bytes in the token.  If no token is found,
** return 0.
**
** A token consists of one or more non-space characters.  Leading
** whitespace is ignored.
**
** The cursor of pFrom is left pointing at the first character past
** the end of the token.
**
** pTo will be an ephermeral blob.  If pFrom changes, it might alter
** pTo as well.
*/
................................................................................
*/
int blob_tail(Blob *pFrom, Blob *pTo){
  int iCursor = pFrom->iCursor;
  blob_extract(pFrom, pFrom->nUsed-pFrom->iCursor, pTo);
  pFrom->iCursor = iCursor;
  return pTo->nUsed;
}

/*
** Copy N lines of text from pFrom into pTo.  The copy begins at the
** current cursor position of pIn.  The pIn cursor is left pointing
** at the first character past the last \n copied.
**
** If pTo==NULL then this routine simply skips over N lines.
*/
void blob_copy_lines(Blob *pTo, Blob *pFrom, int N){
  char *z = pFrom->aData;
  int i = pFrom->iCursor;
  int n = pFrom->nUsed;
  int cnt = 0;

  if( N==0 ) return;
  while( i<n ){
    if( z[i]=='\n' ){
      cnt++;
      if( cnt==N ){
        i++;
        break;
      }
    }
    i++;
  }
  if( pTo ){
    blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
  }
  pFrom->iCursor = i;
}

/*
** Return true if the blob contains a valid UUID_SIZE-digit base16 identifier.
*/
int blob_is_uuid(Blob *pBlob){
  return blob_size(pBlob)==UUID_SIZE
         && validate16(blob_buffer(pBlob), UUID_SIZE);
................................................................................
*/
int blob_tokenize(Blob *pIn, Blob *aToken, int nToken){
  int i;
  for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){}
  return i;
}












/*
** Do printf-style string rendering and append the results to a blob.
*/
void blob_appendf(Blob *pBlob, const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
  vxprintf(pBlob, zFormat, ap);
  va_end(ap);
}
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
  vxprintf(pBlob, zFormat, ap);
}

/*
** Initalize a blob to the data on an input channel.  Return 
** the number of bytes read into the blob.  Any prior content
** of the blob is discarded, not freed.
*/
................................................................................
      zName = zBuf;
      strcpy(zName, zFilename);
    }
    nName = file_simplify_name(zName, nName);
    for(i=1; i<nName; i++){
      if( zName[i]=='/' ){
        zName[i] = 0;
#ifdef __MINGW32__
        /*
        ** On Windows, local path looks like: C:/develop/project/file.txt
        ** The if stops us from trying to create a directory of a drive letter
        ** C: in this example.
        */
        if( !(i==2 && zName[1]==':') ){
#endif
          if( file_mkdir(zName, 1) ){
            fossil_panic("unable to create directory %s", zName);
          }
#ifdef __MINGW32__
        }
#endif
        zName[i] = '/';
      }
    }
    out = fopen(zName, "wb");
    if( out==0 ){
      fossil_panic("unable to open file \"%s\" for writing", zName);
    }

Added src/branch.c.















































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to create new branches within a repository.
*/
#include "config.h"
#include "branch.h"
#include <assert.h>

void branch_new(void){
  int vid, nvid, noSign;
  Stmt q;
  char *zBranch, *zUuid, *zDate, *zComment;
  const char *zColor;
  Blob manifest;
  Blob mcksum;           /* Self-checksum on the manifest */
  Blob cksum1, cksum2;   /* Before and after commit checksums */
  Blob cksum1b;          /* Checksum recorded in the manifest */
 
  noSign = find_option("nosign","",0)!=0;
  db_must_be_within_tree();
  noSign = db_get_int("omitsign", 0)|noSign;
  zColor = find_option("bgcolor","c",1);
  
  verify_all_options();
  
  /* fossil branch new name */
  if( g.argc<3 ){
    usage("branch new ?-bgcolor COLOR BRANCH-NAME");
  }
  zBranch = g.argv[3];
  if( zBranch==0 || zBranch[0]==0 ){
    fossil_panic("branch name cannot be empty");
  }

  user_select();
  db_begin_transaction();
  if( unsaved_changes() ){
    fossil_panic("there are uncommitted changes. please commit first");
  }

  vid = db_lget_int("checkout", 0);
  vfile_aggregate_checksum_disk(vid, &cksum1);
  
  /* Create our new manifest */
  blob_zero(&manifest);
  zComment = mprintf("Branch created %s", zBranch);
  blob_appendf(&manifest, "C %F\n", zComment);
  zDate = db_text(0, "SELECT datetime('now')");
  zDate[10] = 'T';
  blob_appendf(&manifest, "D %s\n", zDate);

  db_prepare(&q,
    "SELECT pathname, uuid FROM vfile JOIN blob ON vfile.mrid=blob.rid"
    " WHERE NOT deleted AND vfile.vid=%d"
    " ORDER BY 1", vid);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    blob_appendf(&manifest, "F %F %s\n", zName, zUuid);
  }
  db_finalize(&q);
  
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
  blob_appendf(&manifest, "P %s\n", zUuid);
  blob_appendf(&manifest, "R %b\n", &cksum1);
  
  if( zColor!=0 ){
    if( strcmp("bgcolor",zBranch)>=0 ){
      blob_appendf(&manifest, "T *%F *\n", zBranch);
      blob_appendf(&manifest, "T *bgcolor * %F\n", zColor);
    }else{
      blob_appendf(&manifest, "T *bgcolor * %F\n", zColor);
      blob_appendf(&manifest, "T *%F *\n", zBranch);
    }
  }else{
    blob_appendf(&manifest, "T *%F *\n", zBranch);
  }

  /* Cancel any tags that propagate */
  db_prepare(&q, 
      "SELECT tagname"
      "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
      " WHERE rid=%d AND tagtype=2", vid);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zTagname = db_column_text(&q, 0);
    blob_appendf(&manifest, "T -%s *\n", zTagname);
  }
  db_finalize(&q);
  
  blob_appendf(&manifest, "U %F\n", g.zLogin);
  md5sum_blob(&manifest, &mcksum);
  blob_appendf(&manifest, "Z %b\n", &mcksum);
  if( !noSign && clearsign(&manifest, &manifest) ){
    Blob ans;
    blob_zero(&ans);
    prompt_user("unable to sign manifest.  continue [y/N]? ", &ans);
    if( blob_str(&ans)[0]!='y' ){
      db_end_transaction(1);
      exit(1);
    }
  }
  
  /*blob_write_to_file(&manifest, "manifest.new");*/

  nvid = content_put(&manifest, 0, 0);
  if( nvid==0 ){
    fossil_panic("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
  manifest_crosslink(nvid, &manifest);
  content_deltify(vid, nvid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
  printf("Branch Version: %s\n", zUuid);
  printf("\n");
  printf("Notice: working copy not updated to the new branch. If\n");
  printf("        you wish to work on the new branch, update to\n");
  printf("        that branch first:\n");
  printf("\n");
  printf("        fossil update %s\n", zBranch);

  /* Verify that the manifest checksum matches the expected checksum */
  vfile_aggregate_checksum_repository(nvid, &cksum2);
  vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
  if( blob_compare(&cksum1, &cksum1b) ){
    fossil_panic("manifest checksum does not agree with manifest: "
                 "%b versus %b", &cksum1, &cksum1b);
  }
  
  /* Verify that the commit did not modify any disk images. */
  vfile_aggregate_checksum_disk(vid, &cksum2);
  if( blob_compare(&cksum1, &cksum2) ){
    fossil_panic("tree checksums before and after commit do not match");
  }

  /* Clear the undo/redo stack */
  undo_reset();

  /* Commit */
  db_end_transaction(0);
  
  /* Do an autosync push, if requested */
  autosync(0);
}

/*
** COMMAND: branch
**
** Usage: %fossil branch SUBCOMMAND ... ?-R|--repository FILE?
**
** Run various subcommands on the branches of the open repository o
** of the repository identified by the -R or --repository option.
**
**    %fossil branch new ?-bgcolor COLOR BRANCH-NAME
**
**        Create a new branch BRANCH-NAME. You can optionally give
**        a commit message and branch color.
**
**    %fossil branch list
**
**        List all branches
**
*/
void branch_cmd(void){
  int n;
  db_find_and_open_repository();
  if( g.argc<3 ){
    usage("new|list ...");
  }
  n = strlen(g.argv[2]);
  if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
    branch_new();
  }else if( n>=2 && strncmp(g.argv[2],"list",n)==0 ){
    fossil_panic("branch list is not yet completed");
  }else{
    fossil_panic("branch subcommand should be one of: "
                 "new list");
  }
}

Changes to src/cgi.c.

24
25
26
27
28
29
30






31
32
33
34
35
36
37



38
39
40
41
42
43
44
45
46
47
...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
...
286
287
288
289
290
291
292
293
294
295

296
297
298
299
300
301
302






303
304
305
306
307
308
309
...
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
...
343
344
345
346
347
348
349












350
351
352
353
354
355
356
...
698
699
700
701
702
703
704












705
706
707
708
709
710
711
...
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
...
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
....
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
....
1074
1075
1076
1077
1078
1079
1080




1081
1082
1083
1084
1085
1086
1087
....
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
....
1140
1141
1142
1143
1144
1145
1146

1147
1148
1149
1150
1151
1152
1153
** This file contains C functions and procedures that provide useful
** services to CGI programs.  There are procedures for parsing and
** dispensing QUERY_STRING parameters and cookies, the "mprintf()"
** formatting function and its cousins, and routines to encode and
** decode strings in HTML or HTTP.
*/
#include "config.h"






#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/times.h>
#include <sys/time.h>
#include <sys/wait.h>



#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <unistd.h>
#include "cgi.h"

#if INTERFACE
/*
** Shortcuts for cgi_parameter.  P("x") returns the value of query parameter
** or cookie "x", or NULL if there is no such parameter or cookie.  PD("x","y")
................................................................................
*/
void cgi_set_cookie(
  const char *zName,    /* Name of the cookie */
  const char *zValue,   /* Value of the cookie.  Automatically escaped */
  const char *zPath,    /* Path cookie applies to.  NULL means "/" */
  int lifetime          /* Expiration of the cookie in seconds from now */
){
  if( zPath==0 ) zPath = "/";
  if( lifetime>0 ){
    lifetime += (int)time(0);
    blob_appendf(&extraHeader,
       "Set-Cookie: %s=%t; Path=%s; expires=%s; Version=1\r\n",
        zName, zValue, zPath, cgi_rfc822_datestamp(lifetime));
  }else{
    blob_appendf(&extraHeader,
................................................................................
**
** The URL must be relative to the base of the fossil server.
*/
void cgi_redirect(const char *zURL){
  char *zLocation;
  CGIDEBUG(("redirect to %s\n", zURL));
  if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==0 || *zURL=='/' ){
    cgi_panic("invalid redirect URL: %s", zURL);
  }
  zLocation = mprintf("Location: %s/%s\r\n", g.zBaseURL, zURL);

  cgi_append_header(zLocation);
  cgi_reset_content();
  cgi_printf("<html>\n<p>Redirect to %h</p>\n</html>\n", zURL);
  cgi_set_status(302, "Moved Temporarily");
  free(zLocation);
  cgi_reply();
  exit(0);






}

/*
** Information about all query parameters and cookies are stored
** in these variables.
*/
static int nAllocQP = 0; /* Space allocated for aParamQP[] */
................................................................................
** Add another query parameter or cookie to the parameter set.
** zName is the name of the query parameter or cookie and zValue
** is its fully decoded value.
**
** zName and zValue are not copied and must not change or be
** deallocated after this routine returns.
*/
static void cgi_set_parameter_nocopy(const char *zName, const char *zValue){
  if( nAllocQP<=nUsedQP ){
    nAllocQP = nAllocQP*2 + 10;
    aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
    if( aParamQP==0 ) exit(1);
  }
  aParamQP[nUsedQP].zName = zName;
  aParamQP[nUsedQP].zValue = zValue;
................................................................................
** is its fully decoded value.
**
** Copies are made of both the zName and zValue parameters.
*/
void cgi_set_parameter(const char *zName, const char *zValue){
  cgi_set_parameter_nocopy(mprintf("%s",zName), mprintf("%s",zValue));
}













/*
** Add a query parameter.  The zName portion is fixed but a copy
** must be made of zValue.
*/
void cgi_setenv(const char *zName, const char *zValue){
  cgi_set_parameter_nocopy(zName, mprintf("%s",zValue));
................................................................................
      CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue));
      return zValue;
    }
  }
  CGIDEBUG(("no-match [%s]\n", zName));
  return zDefault;
}













/*
** Print CGI debugging messages.
*/
void cgi_debug(const char *zFormat, ...){
  va_list ap;
  if( g.fDebug ){
................................................................................
        strcmp(zVal, zD) ? "" : " selected"
      );
    }
  }
  cgi_printf("%*s</select>\n", in, "");
}

/* 
** This function implements the callback from vxprintf. 
**
** This routine sends nNewChar characters of text in zNewText to
** CGI reply content buffer.
*/
static void sout(void *NotUsed, const char *zNewText, int nNewChar){
  cgi_append_content(zNewText, nNewChar);
}

/*
** This routine works like "printf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_printf(const char *zFormat, ...){
  va_list ap;
  va_start(ap,zFormat);
  vxprintf(sout,0,zFormat,ap);
  va_end(ap);
}

/*
** This routine works like "vprintf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_vprintf(const char *zFormat, va_list ap){
  vxprintf(sout,0,zFormat,ap);
}


/*
** Send a reply indicating that the HTTP request was malformed
*/
static void malformed_request(void){
................................................................................
  cgi_reset_content();
  cgi_set_status(500, "Internal Server Error");
  cgi_printf(
    "<html><body><h1>Internal Server Error</h1>\n"
    "<plaintext>"
  );
  va_start(ap, zFormat);
  vxprintf(sout,0,zFormat,ap);
  va_end(ap);
  cgi_reply();
  exit(1);
}

/*
** Remove the first space-delimited token from a string and return
................................................................................
    malformed_request();
  }
  cgi_setenv("REQUEST_URI", zToken);
  for(i=0; zToken[i] && zToken[i]!='?'; i++){}
  if( zToken[i] ) zToken[i++] = 0;
  cgi_setenv("PATH_INFO", zToken);
  cgi_setenv("QUERY_STRING", &zToken[i]);
  if( getpeername(fileno(stdin), (struct sockaddr*)&remoteName, &size)>=0 ){
    char *zIpAddr = inet_ntoa(remoteName.sin_addr);
    cgi_setenv("REMOTE_ADDR", zIpAddr);

    /* Set the Global.zIpAddr variable to the server we are talking to.
    ** This is used to populate the ipaddr column of the rcvfrom table,
    ** if any files are received from the connected client.
    */
................................................................................
** Implement an HTTP server daemon listening on port iPort.
**
** As new connections arrive, fork a child and let child return
** out of this procedure call.  The child will handle the request.
** The parent never returns from this procedure.
*/
void cgi_http_server(int iPort){




  int listener;                /* The server socket */
  int connection;              /* A socket for each individual connection */
  fd_set readfds;              /* Set of file descriptors for select() */
  size_t lenaddr;              /* Length of the inaddr structure */
  int child;                   /* PID of the child process */
  int nchildren = 0;           /* Number of child processes */
  struct timeval delay;        /* How long to wait inside select() */
................................................................................
    }
    delay.tv_sec = 60;
    delay.tv_usec = 0;
    FD_ZERO(&readfds);
    FD_SET( listener, &readfds);
    if( select( listener+1, &readfds, 0, 0, &delay) ){
      lenaddr = sizeof(inaddr);
      connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
      if( connection>=0 ){
        child = fork();
        if( child!=0 ){
          if( child>0 ) nchildren++;
          close(connection);
        }else{
          close(0);
................................................................................
    /* Bury dead children */
    while( waitpid(0, 0, WNOHANG)>0 ){
      nchildren--;
    }
  }
  /* NOT REACHED */  
  exit(1);

}

/*
** Name of days and months.
*/
static const char *azDays[] =
    {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 0};







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


<







 







|







 







|
|
|
>







>
>
>
>
>
>







 







|







 







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







 







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







 







<
<
<
<
<
<
<
<
<
<







|








|







 







|







 







|







 







>
>
>
>







 







|







 







>







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

40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
...
930
931
932
933
934
935
936










937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
...
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
....
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
....
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
....
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
....
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
** This file contains C functions and procedures that provide useful
** services to CGI programs.  There are procedures for parsing and
** dispensing QUERY_STRING parameters and cookies, the "mprintf()"
** formatting function and its cousins, and routines to encode and
** decode strings in HTML or HTTP.
*/
#include "config.h"
#ifdef __MINGW32__
#  include <windows.h>           /* for Sleep once server works again */
#  include <winsock2.h>          /* socket operations */
#  define sleep Sleep            /* windows does not have sleep, but Sleep */
#  include <ws2tcpip.h>          
#else
#  include <sys/socket.h>
#  include <netinet/in.h>
#  include <arpa/inet.h>

#  include <sys/times.h>
#  include <sys/time.h>
#  include <sys/wait.h>
#  include <sys/select.h>
#endif
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>
#include "cgi.h"

#if INTERFACE
/*
** Shortcuts for cgi_parameter.  P("x") returns the value of query parameter
** or cookie "x", or NULL if there is no such parameter or cookie.  PD("x","y")
................................................................................
*/
void cgi_set_cookie(
  const char *zName,    /* Name of the cookie */
  const char *zValue,   /* Value of the cookie.  Automatically escaped */
  const char *zPath,    /* Path cookie applies to.  NULL means "/" */
  int lifetime          /* Expiration of the cookie in seconds from now */
){
  if( zPath==0 ) zPath = g.zTop;
  if( lifetime>0 ){
    lifetime += (int)time(0);
    blob_appendf(&extraHeader,
       "Set-Cookie: %s=%t; Path=%s; expires=%s; Version=1\r\n",
        zName, zValue, zPath, cgi_rfc822_datestamp(lifetime));
  }else{
    blob_appendf(&extraHeader,
................................................................................
**
** The URL must be relative to the base of the fossil server.
*/
void cgi_redirect(const char *zURL){
  char *zLocation;
  CGIDEBUG(("redirect to %s\n", zURL));
  if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==0 || *zURL=='/' ){
    zLocation = mprintf("Location: %s\r\n", zURL);
  }else{
    zLocation = mprintf("Location: %s/%s\r\n", g.zBaseURL, zURL);
  }
  cgi_append_header(zLocation);
  cgi_reset_content();
  cgi_printf("<html>\n<p>Redirect to %h</p>\n</html>\n", zURL);
  cgi_set_status(302, "Moved Temporarily");
  free(zLocation);
  cgi_reply();
  exit(0);
}
void cgi_redirectf(const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
  cgi_redirect(vmprintf(zFormat, ap));
  va_end(ap);
}

/*
** Information about all query parameters and cookies are stored
** in these variables.
*/
static int nAllocQP = 0; /* Space allocated for aParamQP[] */
................................................................................
** Add another query parameter or cookie to the parameter set.
** zName is the name of the query parameter or cookie and zValue
** is its fully decoded value.
**
** zName and zValue are not copied and must not change or be
** deallocated after this routine returns.
*/
void cgi_set_parameter_nocopy(const char *zName, const char *zValue){
  if( nAllocQP<=nUsedQP ){
    nAllocQP = nAllocQP*2 + 10;
    aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
    if( aParamQP==0 ) exit(1);
  }
  aParamQP[nUsedQP].zName = zName;
  aParamQP[nUsedQP].zValue = zValue;
................................................................................
** is its fully decoded value.
**
** Copies are made of both the zName and zValue parameters.
*/
void cgi_set_parameter(const char *zName, const char *zValue){
  cgi_set_parameter_nocopy(mprintf("%s",zName), mprintf("%s",zValue));
}

/*
** Replace a parameter with a new value.
*/
void cgi_replace_parameter(const char *zName, const char *zValue){
  int i;
  for(i=0; i<nUsedQP; i++){
    if( strcmp(aParamQP[i].zName,zName)==0 ){
      aParamQP[i].zValue = zValue;
    }
  }
}

/*
** Add a query parameter.  The zName portion is fixed but a copy
** must be made of zValue.
*/
void cgi_setenv(const char *zName, const char *zValue){
  cgi_set_parameter_nocopy(zName, mprintf("%s",zValue));
................................................................................
      CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue));
      return zValue;
    }
  }
  CGIDEBUG(("no-match [%s]\n", zName));
  return zDefault;
}

/*
** Return the name of the i-th CGI parameter.  Return NULL if there
** are fewer than i registered CGI parmaeters.
*/
const char *cgi_parameter_name(int i){
  if( i>=0 && i<nUsedQP ){
    return aParamQP[i].zName;
  }else{
    return 0;
  }
}

/*
** Print CGI debugging messages.
*/
void cgi_debug(const char *zFormat, ...){
  va_list ap;
  if( g.fDebug ){
................................................................................
        strcmp(zVal, zD) ? "" : " selected"
      );
    }
  }
  cgi_printf("%*s</select>\n", in, "");
}











/*
** This routine works like "printf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_printf(const char *zFormat, ...){
  va_list ap;
  va_start(ap,zFormat);
  vxprintf(&cgiContent,zFormat,ap);
  va_end(ap);
}

/*
** This routine works like "vprintf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_vprintf(const char *zFormat, va_list ap){
  vxprintf(&cgiContent,zFormat,ap);
}


/*
** Send a reply indicating that the HTTP request was malformed
*/
static void malformed_request(void){
................................................................................
  cgi_reset_content();
  cgi_set_status(500, "Internal Server Error");
  cgi_printf(
    "<html><body><h1>Internal Server Error</h1>\n"
    "<plaintext>"
  );
  va_start(ap, zFormat);
  vxprintf(&cgiContent,zFormat,ap);
  va_end(ap);
  cgi_reply();
  exit(1);
}

/*
** Remove the first space-delimited token from a string and return
................................................................................
    malformed_request();
  }
  cgi_setenv("REQUEST_URI", zToken);
  for(i=0; zToken[i] && zToken[i]!='?'; i++){}
  if( zToken[i] ) zToken[i++] = 0;
  cgi_setenv("PATH_INFO", zToken);
  cgi_setenv("QUERY_STRING", &zToken[i]);
  if( getpeername(fileno(stdin), (struct sockaddr*)&remoteName, (socklen_t*)&size)>=0 ){
    char *zIpAddr = inet_ntoa(remoteName.sin_addr);
    cgi_setenv("REMOTE_ADDR", zIpAddr);

    /* Set the Global.zIpAddr variable to the server we are talking to.
    ** This is used to populate the ipaddr column of the rcvfrom table,
    ** if any files are received from the connected client.
    */
................................................................................
** Implement an HTTP server daemon listening on port iPort.
**
** As new connections arrive, fork a child and let child return
** out of this procedure call.  The child will handle the request.
** The parent never returns from this procedure.
*/
void cgi_http_server(int iPort){
#ifdef __MINGW32__
  fprintf(stderr,"server not yet available in windows version of fossil\n");
  exit(1);
#else
  int listener;                /* The server socket */
  int connection;              /* A socket for each individual connection */
  fd_set readfds;              /* Set of file descriptors for select() */
  size_t lenaddr;              /* Length of the inaddr structure */
  int child;                   /* PID of the child process */
  int nchildren = 0;           /* Number of child processes */
  struct timeval delay;        /* How long to wait inside select() */
................................................................................
    }
    delay.tv_sec = 60;
    delay.tv_usec = 0;
    FD_ZERO(&readfds);
    FD_SET( listener, &readfds);
    if( select( listener+1, &readfds, 0, 0, &delay) ){
      lenaddr = sizeof(inaddr);
      connection = accept(listener, (struct sockaddr*)&inaddr, (socklen_t*) &lenaddr);
      if( connection>=0 ){
        child = fork();
        if( child!=0 ){
          if( child>0 ) nchildren++;
          close(connection);
        }else{
          close(0);
................................................................................
    /* Bury dead children */
    while( waitpid(0, 0, WNOHANG)>0 ){
      nchildren--;
    }
  }
  /* NOT REACHED */  
  exit(1);
#endif
}

/*
** Name of days and months.
*/
static const char *azDays[] =
    {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 0};

Changes to src/checkin.c.

42
43
44
45
46
47
48

49


50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
...
160
161
162
163
164
165
166
167
168
169
170




171
172

173
174

175
176
177
178
179
180
181
182
183
184

185










186
187
188
189
190
191
192
...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318
319

320
321
322
323





324
325
326
327
328
329
330
...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
...
356
357
358
359
360
361
362






363
364
365
366
367
368
369
...
503
504
505
506
507
508
509
510
511












    "WHERE file_is_selected(id) AND (chnged OR deleted OR rid=0) ORDER BY 1"
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
    int isDeleted = db_column_int(&q, 1);
    int isChnged = db_column_int(&q,2);
    int isNew = db_column_int(&q,3)==0;

    blob_append(report, zPrefix, nPrefix);


    if( isNew ){
      blob_appendf(report, "ADDED    %s\n", zPathname);
    }else if( isDeleted ){
      blob_appendf(report, "DELETED  %s\n", zPathname);
    }else if( isChnged==2 ){
      blob_appendf(report, "UPDATED_BY_MERGE %s\n", zPathname);
    }else if( isChnged==3 ){
      blob_appendf(report, "ADDED_BY_MERGE %s\n", zPathname);
    }else{
      blob_appendf(report, "EDITED   %s\n", zPathname);
    }

  }
  db_finalize(&q);
  db_prepare(&q, "SELECT uuid FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=0");
  while( db_step(&q)==SQLITE_ROW ){
    blob_append(report, zPrefix, nPrefix);
    blob_appendf(report, "MERGED_WITH %s\n", db_column_text(&q, 0));
................................................................................
    printf("%s\n", db_column_text(&q, 0));
  }
  db_finalize(&q);
}

/*
** COMMAND: clean
** Usage: %fossil clean
** Delete all "extra" files in the source tree.  "Extra" files are
** files that are not officially part of the checkout.  See also
** the "extra" command.




*/
void clean_cmd(void){

  Blob path;
  Stmt q;

  db_must_be_within_tree();
  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
  chdir(g.zLocalRoot);
  blob_zero(&path);
  vfile_scan(0, &path);
  db_prepare(&q, 
      "SELECT %Q || x FROM sfile"
      " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')"
      " ORDER BY 1", g.zLocalRoot);
  while( db_step(&q)==SQLITE_ROW ){

    unlink(db_column_text(&q, 0));










  }
  db_finalize(&q);
}

/*
** Prepare a commit comment.  Let the user modify it using the
** editor specified in the global_config table or either
................................................................................
  char *zComment;
  int i;
  blob_set(&text,
    "\n# Enter comments on this commit.  Lines beginning with # are ignored\n"
    "#\n"
  );
  status_report(&text, "# ");
  zEditor = db_global_get("editor", 0);
  if( zEditor==0 ){
    zEditor = getenv("VISUAL");
  }
  if( zEditor==0 ){
    zEditor = getenv("EDITOR");
  }
  if( zEditor==0 ){
    zEditor = "ed";
  }
  zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
                   g.zLocalRoot);
  blob_write_to_file(&text, zFile);
  zCmd = mprintf("%s %s", zEditor, zFile);
  printf("%s\n", zCmd);
  if( system(zCmd) ){
    fossil_panic("editor aborted");
  }
  blob_reset(&text);
  blob_read_from_file(&text, zFile);
  unlink(zFile);
................................................................................
** prompted for your GPG passphrase in order to sign the new manifest
** unless the "--nosign" options is used.  All files that have
** changed will be committed unless some subset of files is specified
** on the command line.
*/
void commit_cmd(void){
  int rc;
  int vid, nrid, nvid;
  Blob comment;
  const char *zComment;
  Stmt q;
  Stmt q2;
  char *zUuid, *zDate;
  int noSign = 0;        /* True to omit signing the manifest using GPG */
  int isAMerge = 0;      /* True if checking in a merge */

  char *zManifestFile;   /* Name of the manifest file */
  Blob manifest;
  Blob muuid;            /* Manifest uuid */
  Blob mcksum;           /* Self-checksum on the manifest */
  Blob cksum1, cksum2;   /* Before and after commit checksums */
  Blob cksum1b;          /* Checksum recorded in the manifest */
 
  noSign = find_option("nosign","",0)!=0;
  zComment = find_option("comment","m",1);

  db_must_be_within_tree();
  noSign = db_get_int("omit-ci-sig", 0)|noSign;
  verify_all_options();






  /* There are two ways this command may be executed. If there are
  ** no arguments following the word "commit", then all modified files
  ** in the checked out directory are committed. If one or more arguments
  ** follows "commit", then only those files are committed.
  **
  ** After the following function call has returned, the Global.aCommitFile[]
  ** array is allocated to contain the "id" field from the vfile table
................................................................................
  if( g.aCommitFile && isAMerge ){
    fossil_fatal("cannot do a partial commit of a merge");
  }

  user_select();
  db_begin_transaction();
  rc = unsaved_changes();
  if( rc==0 && !isAMerge ){
    fossil_panic("nothing has changed");
  }

  /* If one or more files that were named on the command line have not
  ** been modified, bail out now.
  */
  if( g.aCommitFile ){
................................................................................
    );
    if( strlen(blob_str(&unmodified)) ){
      fossil_panic("file %s has not changed", blob_str(&unmodified));
    }
  }

  vid = db_lget_int("checkout", 0);






  vfile_aggregate_checksum_disk(vid, &cksum1);
  if( zComment ){
    blob_zero(&comment);
    blob_append(&comment, zComment, -1);
  }else{
    prepare_commit_comment(&comment);
  }
................................................................................
    fossil_panic("tree checksums before and after commit do not match");
  }

  /* Clear the undo/redo stack */
  undo_reset();

  /* Commit */
  db_end_transaction(0);  
}



















>

>
>
|










>







 







|


|
>
>
>
>


>


>










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







 







|












|







 







|







>









>

|

|
>
>
>
>
>







 







|







 







>
>
>
>
>
>







 







|
|
>
>
>
>
>
>
>
>
>
>
>
>
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
...
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
...
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
...
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
    "WHERE file_is_selected(id) AND (chnged OR deleted OR rid=0) ORDER BY 1"
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
    int isDeleted = db_column_int(&q, 1);
    int isChnged = db_column_int(&q,2);
    int isNew = db_column_int(&q,3)==0;
    char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname);
    blob_append(report, zPrefix, nPrefix);
    if( access(zFullName, 0) ){
      blob_appendf(report, "MISSING  %s\n", zPathname);
    }else if( isNew ){
      blob_appendf(report, "ADDED    %s\n", zPathname);
    }else if( isDeleted ){
      blob_appendf(report, "DELETED  %s\n", zPathname);
    }else if( isChnged==2 ){
      blob_appendf(report, "UPDATED_BY_MERGE %s\n", zPathname);
    }else if( isChnged==3 ){
      blob_appendf(report, "ADDED_BY_MERGE %s\n", zPathname);
    }else{
      blob_appendf(report, "EDITED   %s\n", zPathname);
    }
    free(zFullName);
  }
  db_finalize(&q);
  db_prepare(&q, "SELECT uuid FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=0");
  while( db_step(&q)==SQLITE_ROW ){
    blob_append(report, zPrefix, nPrefix);
    blob_appendf(report, "MERGED_WITH %s\n", db_column_text(&q, 0));
................................................................................
    printf("%s\n", db_column_text(&q, 0));
  }
  db_finalize(&q);
}

/*
** COMMAND: clean
** Usage: %fossil clean ?-all
** Delete all "extra" files in the source tree.  "Extra" files are
** files that are not officially part of the checkout.  See also
** the "extra" command. This operation cannot be undone. 
**
** You will be prompted before removing each file. If you are
** sure you wish to remove all "extra" files you can specify the
** optional -all flag.
*/
void clean_cmd(void){
  int allFlag;
  Blob path;
  Stmt q;
  allFlag = find_option("all","a",0)!=0;
  db_must_be_within_tree();
  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
  chdir(g.zLocalRoot);
  blob_zero(&path);
  vfile_scan(0, &path);
  db_prepare(&q, 
      "SELECT %Q || x FROM sfile"
      " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')"
      " ORDER BY 1", g.zLocalRoot);
  while( db_step(&q)==SQLITE_ROW ){
    if( allFlag ){
      unlink(db_column_text(&q, 0));
    }else{
      Blob ans;
      char *prompt = mprintf("remove unmanaged file \"%s\" [y/N]? ",
                              db_column_text(&q, 0));
      blob_zero(&ans);
      prompt_user(prompt, &ans);
      if( blob_str(&ans)[0]=='y' ){
        unlink(db_column_text(&q, 0));
      }
    }
  }
  db_finalize(&q);
}

/*
** Prepare a commit comment.  Let the user modify it using the
** editor specified in the global_config table or either
................................................................................
  char *zComment;
  int i;
  blob_set(&text,
    "\n# Enter comments on this commit.  Lines beginning with # are ignored\n"
    "#\n"
  );
  status_report(&text, "# ");
  zEditor = db_get("editor", 0);
  if( zEditor==0 ){
    zEditor = getenv("VISUAL");
  }
  if( zEditor==0 ){
    zEditor = getenv("EDITOR");
  }
  if( zEditor==0 ){
    zEditor = "ed";
  }
  zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
                   g.zLocalRoot);
  blob_write_to_file(&text, zFile);
  zCmd = mprintf("%s \"%s\"", zEditor, zFile);
  printf("%s\n", zCmd);
  if( system(zCmd) ){
    fossil_panic("editor aborted");
  }
  blob_reset(&text);
  blob_read_from_file(&text, zFile);
  unlink(zFile);
................................................................................
** prompted for your GPG passphrase in order to sign the new manifest
** unless the "--nosign" options is used.  All files that have
** changed will be committed unless some subset of files is specified
** on the command line.
*/
void commit_cmd(void){
  int rc;
  int vid, nrid, nvid, wouldFork=0;
  Blob comment;
  const char *zComment;
  Stmt q;
  Stmt q2;
  char *zUuid, *zDate;
  int noSign = 0;        /* True to omit signing the manifest using GPG */
  int isAMerge = 0;      /* True if checking in a merge */
  int forceFlag = 0;     /* Force a fork */
  char *zManifestFile;   /* Name of the manifest file */
  Blob manifest;
  Blob muuid;            /* Manifest uuid */
  Blob mcksum;           /* Self-checksum on the manifest */
  Blob cksum1, cksum2;   /* Before and after commit checksums */
  Blob cksum1b;          /* Checksum recorded in the manifest */
 
  noSign = find_option("nosign","",0)!=0;
  zComment = find_option("comment","m",1);
  forceFlag = find_option("force", "r", 0)!=0;
  db_must_be_within_tree();
  noSign = db_get_int("omitsign", 0)|noSign;
  verify_all_options();
  
  /*
  ** Autosync if requested.
  */
  autosync(1);
  
  /* There are two ways this command may be executed. If there are
  ** no arguments following the word "commit", then all modified files
  ** in the checked out directory are committed. If one or more arguments
  ** follows "commit", then only those files are committed.
  **
  ** After the following function call has returned, the Global.aCommitFile[]
  ** array is allocated to contain the "id" field from the vfile table
................................................................................
  if( g.aCommitFile && isAMerge ){
    fossil_fatal("cannot do a partial commit of a merge");
  }

  user_select();
  db_begin_transaction();
  rc = unsaved_changes();
  if( rc==0 && !isAMerge && !forceFlag ){
    fossil_panic("nothing has changed");
  }

  /* If one or more files that were named on the command line have not
  ** been modified, bail out now.
  */
  if( g.aCommitFile ){
................................................................................
    );
    if( strlen(blob_str(&unmodified)) ){
      fossil_panic("file %s has not changed", blob_str(&unmodified));
    }
  }

  vid = db_lget_int("checkout", 0);
  if( db_exists("SELECT 1 FROM plink WHERE pid=%d", vid) ){
    wouldFork=1;
    if( forceFlag==0 && db_get_int("safemerge", 0)==0 ){
      fossil_fatal("would fork.  use -f or --force");
    }
  }
  vfile_aggregate_checksum_disk(vid, &cksum1);
  if( zComment ){
    blob_zero(&comment);
    blob_append(&comment, zComment, -1);
  }else{
    prepare_commit_comment(&comment);
  }
................................................................................
    fossil_panic("tree checksums before and after commit do not match");
  }

  /* Clear the undo/redo stack */
  undo_reset();

  /* Commit */
  db_end_transaction(0);
  
  if( wouldFork==0 ){
    /* Do an autosync push if requested. If wouldFork == 1, then they either
    ** forced this commit or safe merge is on, and this commit did indeed
    ** create a fork. In this case, we want the user to merge before sending
    ** their new commit back to the rest of the world, so do not auto-push.
    */
    autosync(0);
  }else{
    printf("Warning: commit caused a fork to occur. Please merge and push\n");
    printf("         your changes as soon as possible.\n");
  }
}

Changes to src/clearsign.c.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
** Clearsign the given blob.  Put the signed version in
** pOut.
*/
int clearsign(Blob *pIn, Blob *pOut){
  char *zRand;
  char *zIn;
  char *zOut;
  char *zBase = db_global_get("clear-sign", "gpg --clearsign -o ");
  char *zCmd;
  int rc;
  zRand = db_text(0, "SELECT hex(randomblob(10))");
  zOut = mprintf("out-%s", zRand);
  zIn = mprintf("in-%z", zRand);
  blob_write_to_file(pIn, zOut);
  zCmd = mprintf("%s %s %s", zBase, zIn, zOut);







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
** Clearsign the given blob.  Put the signed version in
** pOut.
*/
int clearsign(Blob *pIn, Blob *pOut){
  char *zRand;
  char *zIn;
  char *zOut;
  char *zBase = db_get("clear-sign", "gpg --clearsign -o ");
  char *zCmd;
  int rc;
  zRand = db_text(0, "SELECT hex(randomblob(10))");
  zOut = mprintf("out-%s", zRand);
  zIn = mprintf("in-%z", zRand);
  blob_write_to_file(pIn, zOut);
  zCmd = mprintf("%s %s %s", zBase, zIn, zOut);

Changes to src/clone.c.

37
38
39
40
41
42
43

44
45
46
47
48
49


50
51
52
53
54
55
56
57

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

77
** Make a clone of a repository specified by URL in the local
** file named FILENAME.  
*/
void clone_cmd(void){
  if( g.argc!=4 ){
    usage("FILE-OR-URL NEW-REPOSITORY");
  }

  if( file_size(g.argv[3])>0 ){
    fossil_panic("file already exists: %s", g.argv[3]);
  }
  url_parse(g.argv[2]);
  db_create_repository(g.argv[3]);
  db_open_repository(g.argv[3]);


  user_select();
  db_set("content-schema", CONTENT_SCHEMA);
  db_set("aux-schema", AUX_SCHEMA);
  if( !g.urlIsFile ){
    db_set("last-sync-url", g.argv[2]);
  }
  db_multi_exec(
    "INSERT INTO config(name,value) VALUES('server-code', hex(randomblob(20)));"

  );
  if( g.urlIsFile ){
    Stmt q;
    db_multi_exec("ATTACH DATABASE %Q AS orig", g.urlName);
    db_begin_transaction();
    db_prepare(&q, 
      "SELECT name FROM orig.sqlite_master"
      " WHERE type='table'"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zTab = db_column_text(&q, 0);
      db_multi_exec("INSERT OR IGNORE INTO %Q SELECT * FROM orig.%Q",
                    zTab, zTab);
    }
    db_finalize(&q);
    db_end_transaction(0);
  }else{
    client_sync(0,0,1);
  }

}







>






>
>

|
|

|


|
>




<










<



>

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
** Make a clone of a repository specified by URL in the local
** file named FILENAME.  
*/
void clone_cmd(void){
  if( g.argc!=4 ){
    usage("FILE-OR-URL NEW-REPOSITORY");
  }
  db_open_config();
  if( file_size(g.argv[3])>0 ){
    fossil_panic("file already exists: %s", g.argv[3]);
  }
  url_parse(g.argv[2]);
  db_create_repository(g.argv[3]);
  db_open_repository(g.argv[3]);
  db_begin_transaction();
  db_initial_setup(0, 0);
  user_select();
  db_set("content-schema", CONTENT_SCHEMA, 0);
  db_set("aux-schema", AUX_SCHEMA, 0);
  if( !g.urlIsFile ){
    db_set("last-sync-url", g.argv[2], 0);
  }
  db_multi_exec(
    "INSERT INTO config(name,value)"
    " VALUES('server-code', lower(hex(randomblob(20))));"
  );
  if( g.urlIsFile ){
    Stmt q;
    db_multi_exec("ATTACH DATABASE %Q AS orig", g.urlName);

    db_prepare(&q, 
      "SELECT name FROM orig.sqlite_master"
      " WHERE type='table'"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zTab = db_column_text(&q, 0);
      db_multi_exec("INSERT OR IGNORE INTO %Q SELECT * FROM orig.%Q",
                    zTab, zTab);
    }
    db_finalize(&q);

  }else{
    client_sync(0,0,1);
  }
  db_end_transaction(0);
}

Changes to src/construct.c.

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

  /* Create the foundation */
  db_create_repository(zRepository);
  db_open_repository(zRepository);
  db_open_config();
  db_begin_transaction();

  db_initial_setup(0);

  printf("project-id: %s\n", db_get("project-code", 0));
  printf("server-id:  %s\n", db_get("server-code", 0));
  printf("admin-user: %s (no password set yet!)\n", g.zLogin);
  printf("baseline:   %s\n", db_text(0, "SELECT uuid FROM blob"));

  /* Scan origin and insert all files found inside */
  fileCnt = import_origin (zOrigin);

  printf("imported:   %d %s\n", fileCnt, fileCnt == 1 ?
	 "file" : "files");

  /* Finalize the repository, rebuild the derived tables */
  errCnt = rebuild_db ();

  if( errCnt ){
    printf("%d %s. Rolling back changes.\n", errCnt, errCnt == 1 ?
	   "error" : "errors");
    db_end_transaction(1);
  }else{
    db_end_transaction(0);
  }
}







|













|









138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

  /* Create the foundation */
  db_create_repository(zRepository);
  db_open_repository(zRepository);
  db_open_config();
  db_begin_transaction();

  db_initial_setup(0, 1);

  printf("project-id: %s\n", db_get("project-code", 0));
  printf("server-id:  %s\n", db_get("server-code", 0));
  printf("admin-user: %s (no password set yet!)\n", g.zLogin);
  printf("baseline:   %s\n", db_text(0, "SELECT uuid FROM blob"));

  /* Scan origin and insert all files found inside */
  fileCnt = import_origin (zOrigin);

  printf("imported:   %d %s\n", fileCnt, fileCnt == 1 ?
	 "file" : "files");

  /* Finalize the repository, rebuild the derived tables */
  errCnt = rebuild_db(0, 0);

  if( errCnt ){
    printf("%d %s. Rolling back changes.\n", errCnt, errCnt == 1 ?
	   "error" : "errors");
    db_end_transaction(1);
  }else{
    db_end_transaction(0);
  }
}

Changes to src/content.c.

42
43
44
45
46
47
48

49
50
51
52
53











54
55
56
57
58
59
60
..
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78


























79
80
81
82
83
84
85
** is a phantom, zero pBlob and return 0.
*/
int content_get(int rid, Blob *pBlob){
  Stmt q;
  Blob src;
  int srcid;
  int rc = 0;


  assert( g.repositoryOpen );
  srcid = findSrcid(rid);
  blob_zero(pBlob);
  if( srcid ){











    if( content_get(srcid, &src) ){
      db_prepare(&q, "SELECT content FROM blob WHERE rid=%d AND size>=0", rid);
      if( db_step(&q)==SQLITE_ROW ){
        Blob delta;
        db_ephemeral_blob(&q, 0, &delta);
        blob_uncompress(&delta, &delta);
        blob_init(pBlob,0,0);
................................................................................
        blob_delta_apply(&src, &delta, pBlob);
        blob_reset(&delta);
        rc = 1;
      }
      db_finalize(&q);
      blob_reset(&src);
    }

  }else{
    db_prepare(&q, "SELECT content FROM blob WHERE rid=%d AND size>=0", rid);
    if( db_step(&q)==SQLITE_ROW ){
      db_ephemeral_blob(&q, 0, pBlob);
      blob_uncompress(pBlob, pBlob);
      rc = 1;
    }
    db_finalize(&q);
  }
  return rc;
}



























/*
** COMMAND:  test-content-get
**
** Extract a blob from the database and write it into a file.
*/
void test_content_get_cmd(void){







>





>
>
>
>
>
>
>
>
>
>
>







 







>











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







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
..
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
** is a phantom, zero pBlob and return 0.
*/
int content_get(int rid, Blob *pBlob){
  Stmt q;
  Blob src;
  int srcid;
  int rc = 0;
  static Bag inProcess;

  assert( g.repositoryOpen );
  srcid = findSrcid(rid);
  blob_zero(pBlob);
  if( srcid ){
    if( bag_find(&inProcess, srcid) ){
      db_multi_exec(
        "UPDATE blob SET content=NULL, size=-1 WHERE rid=%d;"
        "DELETE FROM delta WHERE rid=%d;"
        "INSERT OR IGNORE INTO phantom VALUES(%d);",
        srcid, srcid, srcid
      );
      blob_zero(pBlob);
      return 0;
    }
    bag_insert(&inProcess, srcid);
    if( content_get(srcid, &src) ){
      db_prepare(&q, "SELECT content FROM blob WHERE rid=%d AND size>=0", rid);
      if( db_step(&q)==SQLITE_ROW ){
        Blob delta;
        db_ephemeral_blob(&q, 0, &delta);
        blob_uncompress(&delta, &delta);
        blob_init(pBlob,0,0);
................................................................................
        blob_delta_apply(&src, &delta, pBlob);
        blob_reset(&delta);
        rc = 1;
      }
      db_finalize(&q);
      blob_reset(&src);
    }
    bag_remove(&inProcess, srcid);
  }else{
    db_prepare(&q, "SELECT content FROM blob WHERE rid=%d AND size>=0", rid);
    if( db_step(&q)==SQLITE_ROW ){
      db_ephemeral_blob(&q, 0, pBlob);
      blob_uncompress(pBlob, pBlob);
      rc = 1;
    }
    db_finalize(&q);
  }
  return rc;
}

/*
** Get the contents of a file within a given revision.
*/
int content_get_historical_file(const char *revision, const char *file, Blob *content){
  Blob mfile;
  Manifest m;
  int i, rid=0;
  
  rid = name_to_rid(revision);
  content_get(rid, &mfile);
  
  if( manifest_parse(&m, &mfile) ){
    for(i=0; i<m.nFile; i++){
      if( strcmp(m.aFile[i].zName, file)==0 ){
        rid = uuid_to_rid(m.aFile[i].zUuid, 0);
        return content_get(rid, content);
      }
    }
    fossil_panic("file: %s does not exist in revision: %s", file, revision);
  }else{
    fossil_panic("could not parse manifest for revision: %s", revision);
  }
  
  return 0;
}

/*
** COMMAND:  test-content-get
**
** Extract a blob from the database and write it into a file.
*/
void test_content_get_cmd(void){

Changes to src/db.c.

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



38
39
40
41
42
43
44
45
46
47
48
..
68
69
70
71
72
73
74
75
76
77
78
79






















80
81
82
83
84

85


86
87
88
89
90
91




92
93
94
95
96
97
98
99
100
101
102





























103
104
105
106
107
108
109
...
162
163
164
165
166
167
168



169
170
171
172
173
174
175
...
254
255
256
257
258
259
260






261
262
263
264
265
266
267
...
483
484
485
486
487
488
489










490

491
492
493




494

495
496
497
498
499
500
501
...
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535

536
537
538
539
540



541
542
543
544
545
546
547
...
646
647
648
649
650
651
652
653
654

655
656
657
658
659
660
661
662
663
664
665
666
667

668

669




670
671



672

673
674
675
676
677
678
679
...
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
...
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
...
799
800
801
802
803
804
805
806
807

808
809








810

811



812









813




814



815









816

817



818
819
820
821

822
823
824
825






826





827
828
829
830
831
832
833
834
835
...
845
846
847
848
849
850
851


852
853
854
855
856
857
858
859
860
861
862



863





864
865

866





















867

868
869



870
871
872
873
874



875
876


877
878
879
880
881

















882
883
884
885











886
887
888
889
890





891
892
893
894
895
896
897
898
899
900





901


902
903
904
905


906
907

908
909
910
911
912
913
914
915

916
917
918
919
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
** 
** Code for interfacing to the various databases.
**
** There are three separate database files that fossil interacts
** with:
**
**    (1)  The "user" database in ~/.fossil
**
................................................................................
**    (2)  The "repository" database
**
**    (3)  A local checkout database named "FOSSIL" and located at the
**         root of the local copy of the source tree.
**
*/
#include "config.h"



#include <sqlite3.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <unistd.h>
#include "db.h"

#if INTERFACE
/*
** An single SQL statement is represented as an instance of the following
** structure.
................................................................................
    style_footer();
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  db_force_rollback();
  exit(1);
  exit(1);
}

static int nBegin = 0;      /* Nesting depth of BEGIN */
static int doRollback = 0;  /* True to force a rollback */























/*
** Begin and end a nested transaction
*/
void db_begin_transaction(void){

  if( nBegin==0 ) db_multi_exec("BEGIN");


  nBegin++;
}
void db_end_transaction(int rollbackFlag){
  if( rollbackFlag ) doRollback = 1;
  nBegin--;
  if( nBegin==0 ){




    db_multi_exec(doRollback ? "ROLLBACK" : "COMMIT");
    doRollback = 0;
  }
}
void db_force_rollback(void){
  if( nBegin ){
    sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
  }
  nBegin = 0;
}
 





























/*
** Prepare or reprepare the sqlite3 statement from the raw SQL text.
*/
static void reprepare(Stmt *pStmt){
  sqlite3_stmt *pNew;
  if( sqlite3_prepare(g.db, blob_buffer(&pStmt->sql), -1, &pNew, 0)!=0 ){
    db_err("%s\n%s", blob_str(&pStmt->sql), sqlite3_errmsg(g.db));
................................................................................
*/
int db_bind_int(Stmt *pStmt, const char *zParamName, int iValue){
  return sqlite3_bind_int(pStmt->pStmt, paramIdx(pStmt, zParamName), iValue);
}
int db_bind_int64(Stmt *pStmt, const char *zParamName, i64 iValue){
  return sqlite3_bind_int64(pStmt->pStmt, paramIdx(pStmt, zParamName), iValue);
}



int db_bind_text(Stmt *pStmt, const char *zParamName, const char *zValue){
  return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue,
                           -1, SQLITE_STATIC);
}
int db_bind_null(Stmt *pStmt, const char *zParamName){
  return sqlite3_bind_null(pStmt->pStmt, paramIdx(pStmt, zParamName));
}
................................................................................
  return sqlite3_column_int64(pStmt->pStmt, N);
}
double db_column_double(Stmt *pStmt, int N){
  return sqlite3_column_double(pStmt->pStmt, N);
}
const char *db_column_text(Stmt *pStmt, int N){
  return (char*)sqlite3_column_text(pStmt->pStmt, N);






}
char *db_column_malloc(Stmt *pStmt, int N){
  return mprintf("%s", db_column_text(pStmt, N));
}
void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){
  blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N),
              sqlite3_column_bytes(pStmt->pStmt, N));
................................................................................

/*
** Open the user database in "~/.fossil".  Create the database anew if
** it does not already exist.
*/
void db_open_config(void){
  char *zDbName;










  const char *zHome = getenv("HOME");

  if( zHome==0 ){
    db_err("cannot local home directory");
  }




  zDbName = mprintf("%s/.fossil", zHome);

  if( g.configOpen ) return;
  if( file_size(zDbName)<1024*3 ){
    db_init_database(zDbName, zConfigSchema, (char*)0);
  }
  db_open_or_attach(zDbName, "configdb");
  g.configOpen = 1;
}
................................................................................
}

/*
** Locate the root directory of the local repository tree.  The root
** directory is found by searching for a file named "FOSSIL" that contains
** a valid repository database.
**
** If no valid FOSSIL file is found, we move up one level and try again.  
** Once the file is found, the g.zLocalRoot variable is set to the root of
** the repository tree and this routine returns 1.  If no database is
** found, then this routine return 0.
**
** This routine always opens the user database regardless of whether or
** not the repository database is found.  If the FOSSIL file is found,
** it is attached to the open database connection too.
*/
int db_open_local(void){
  int n;
  char zPwd[2000];

  if( g.localOpen) return 1;
  if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
    db_err("pwd too big: max %d", sizeof(zPwd)-20);
  }
  n = strlen(zPwd);



  while( n>0 ){
    if( access(zPwd, W_OK) ) break;
    strcpy(&zPwd[n], "/_FOSSIL_");
    if( isValidLocalDb(zPwd) ){
      /* Found a valid _FOSSIL_ file */
      zPwd[n] = 0;
      g.zLocalRoot = mprintf("%s/", zPwd);
................................................................................

/*
** Fill an empty repository database with the basic information for a
** repository. This function is shared between 'create_repository_cmd'
** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
** new repositories.
**
** The caller determines wheter the function inserts an empty root
** manifest (zRoot == TRUE), or not (zRoot == FALSE).

*/

void db_initial_setup (int zRoot){
  char *zDate;
  char *zUser;
  Blob hash;
  Blob manifest;

  db_set("content-schema", CONTENT_SCHEMA);
  db_set("aux-schema", AUX_SCHEMA);
  db_set_int("authenticate-localhost", 0);
  db_multi_exec(
    "INSERT INTO config(name,value) VALUES('server-code', hex(randomblob(20)));"

    "INSERT INTO config(name,value) VALUES('project-code',hex(randomblob(20)));"

  );




  zUser = db_global_get("default-user", 0);
  if( zUser==0 ){



    zUser = getenv("USER");

  }
  if( zUser==0 ){
    zUser = "root";
  }
  db_multi_exec(
     "INSERT INTO user(login, pw, cap, info)"
     "VALUES(%Q,'','s','')", zUser
................................................................................
     "INSERT INTO user(login,pw,cap,info)"
     "   VALUES('anonymous','anonymous','hjkorw','Anon');"
     "INSERT INTO user(login,pw,cap,info)"
     "   VALUES('nobody','','jor','Nobody');"
  );
  user_select();

  if (zRoot){
    blob_zero(&manifest);
    blob_appendf(&manifest, "C initial\\sempty\\sbaseline\n");
    zDate = db_text(0, "SELECT datetime('now')");
    zDate[10]='T';
    blob_appendf(&manifest, "D %s\n", zDate);
    blob_appendf(&manifest, "P\n");
    md5sum_init();
................................................................................
  if( g.argc!=3 ){
    usage("REPOSITORY-NAME");
  }
  db_create_repository(g.argv[2]);
  db_open_repository(g.argv[2]);
  db_open_config();
  db_begin_transaction();
  db_initial_setup (1);
  db_end_transaction(0);
  printf("project-id: %s\n", db_get("project-code", 0));
  printf("server-id:  %s\n", db_get("server-code", 0));
  printf("admin-user: %s (no password set yet!)\n", g.zLogin);
  printf("baseline:   %s\n", db_text(0, "SELECT uuid FROM blob"));
}

................................................................................
  }
}

/*
** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
** repository and local databases.
*/
char *db_get(const char *zName, const char *zDefault){
  return db_text((char*)zDefault, 

                 "SELECT value FROM config WHERE name=%Q", zName);
}








void db_set(const char *zName, const char *zValue){

  db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%Q)", zName, zValue);



}









int db_get_int(const char *zName, int dflt){




  return db_int(dflt, "SELECT value FROM config WHERE name=%Q", zName);



}









void db_set_int(const char *zName, int value){

  db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%d)", zName, value);



}
char *db_global_get(const char *zName, const char *zDefault){
  return db_text((char*)zDefault, 
                 "SELECT value FROM global_config WHERE name=%Q", zName);

}
void db_global_set(const char *zName, const char *zValue){
  db_multi_exec("REPLACE INTO global_config(name,value)"
                "VALUES(%Q,%Q)", zName, zValue);






}





char *db_lget(const char *zName, const char *zDefault){
  return db_text((char*)zDefault, 
                 "SELECT value FROM vvar WHERE name=%Q", zName);
}
void db_lset(const char *zName, const char *zValue){
  db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%Q)", zName, zValue);
}
int db_lget_int(const char *zName, int dflt){
  return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
................................................................................
**
** Open a connection to the local repository in FILENAME.  A checkout
** for the repository is created with its root at the working directory.
** See also the "close" command.
*/
void cmd_open(void){
  Blob path;


  if( g.argc!=3 ){
    usage("REPOSITORY-FILENAME");
  }
  if( db_open_local() ){
    fossil_panic("already within an open tree rooted at %s", g.zLocalRoot);
  }
  file_canonical_name(g.argv[2], &path);
  db_open_repository(blob_str(&path));
  db_init_database("./_FOSSIL_", zLocalSchema, (char*)0);
  db_open_local();
  db_lset("repository", blob_str(&path));



  db_lset_int("checkout", 1);





}


/*





















** COMMAND: config

**
** Usage: %fossil config NAME=VALUE ...



**
** List or change the global configuration settings.  With no arguments,
** all settings are listed.  Arguments of simply NAME cause that setting
** to be displayed.  Arguments of the form NAME=VALUE change the value of
** a setting.  Arguments of the form NAME= delete a setting.



**
** Recognized settings include:


**
**   editor        Text editor command used for check-in comments.
**
**   clear-sign    Command used to clear-sign manifests at check-in.
**                 The default is "gpg --clearsign -o ".

















*/
void cmd_config(void){
  db_open_config();
  if( g.argc>2 ){











    int i;
    db_begin_transaction();
    for(i=2; i<g.argc; i++){
      char *zName, *zValue;
      int j;






      zName = mprintf("%s", g.argv[i]);
      for(j=0; zName[j] && zName[j]!='='; j++){}
      if( zName[j] ){
        zName[j] = 0;
        zValue = &zName[j+1];
        if( zValue[0] ){
          db_global_set(zName, zValue);
        }else{
          db_multi_exec("DELETE FROM global_config WHERE name=%Q", zName);





        }


      }
      zValue = db_global_get(zName, 0);
      if( zValue ){
        printf("%s=%s\n", zName, zValue);


      }else{
        printf("%s is undefined\n", zName);

      }
    }
    db_end_transaction(0);
  }else{
    Stmt q;
    db_prepare(&q, "SELECT name, value FROM global_config ORDER BY name");
    while( db_step(&q)==SQLITE_ROW ){
      printf("%s=%s\n", db_column_text(&q, 0), db_column_text(&q, 1));

    }
    db_finalize(&q);
  }
}







|










|







 







>
>
>



<







 







<




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





>
|
>
>






>
>
>
>










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







 







>
>
>







 







>
>
>
>
>
>







 







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



>
>
>
>

>







 







|











>





>
>
>







 







|
|
>

<
|

|



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

>
>
>

>







 







|







 







|







 







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

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

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







 







>
>











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

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

<
>
>
>

<
<
<
<
>
>
>

<
>
>

|

<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

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

<
<
<
<
>
|
<
|
<
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
31
32
33
34
35
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
..
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
...
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
...
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
...
734
735
736
737
738
739
740
741
742
743
744

745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
...
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
...
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
...
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958



959
960
961


962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
...
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048

1049
1050
1051
1052




1053
1054
1055
1056

1057
1058
1059
1060
1061


1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079



1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091




1092
1093
1094
1095
1096
1097









1098
1099
1100
1101
1102
1103
1104
1105
1106



1107
1108
1109

1110
1111


1112




1113
1114

1115

** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** Code for interfacing to the various databases.
**
** There are three separate database files that fossil interacts
** with:
**
**    (1)  The "user" database in ~/.fossil
**
................................................................................
**    (2)  The "repository" database
**
**    (3)  A local checkout database named "FOSSIL" and located at the
**         root of the local copy of the source tree.
**
*/
#include "config.h"
#ifndef __MINGW32__
#  include <pwd.h>
#endif
#include <sqlite3.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <unistd.h>
#include "db.h"

#if INTERFACE
/*
** An single SQL statement is represented as an instance of the following
** structure.
................................................................................
    style_footer();
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  db_force_rollback();
  exit(1);

}

static int nBegin = 0;      /* Nesting depth of BEGIN */
static int doRollback = 0;  /* True to force a rollback */
static int nCommitHook = 0; /* Number of commit hooks */
static struct sCommitHook {
  int (*xHook)(void);  /* Functions to call at db_end_transaction() */
  int sequence;        /* Call functions in sequence order */
} aHook[5];

/*
** This routine is called by the SQLite commit-hook mechanism
** just prior to each omit.  All this routine does is verify
** that nBegin really is zero.  That insures that transactions
** cannot commit by any means other than by calling db_end_transaction()
** below.
**
** This is just a safety and sanity check.
*/
static int db_verify_at_commit(void *notUsed){
  if( nBegin ){
    fossil_panic("illegal commit attempt");
    return 1;
  }
  return 0;
}

/*
** Begin and end a nested transaction
*/
void db_begin_transaction(void){
  if( nBegin==0 ){
    db_multi_exec("BEGIN");
    sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
  }
  nBegin++;
}
void db_end_transaction(int rollbackFlag){
  if( rollbackFlag ) doRollback = 1;
  nBegin--;
  if( nBegin==0 ){
    int i;
    for(i=0; doRollback==0 && i<nCommitHook; i++){
      doRollback |= aHook[i].xHook();
    }
    db_multi_exec(doRollback ? "ROLLBACK" : "COMMIT");
    doRollback = 0;
  }
}
void db_force_rollback(void){
  if( nBegin ){
    sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
  }
  nBegin = 0;
}

/*
** Install a commit hook.  Hooks are installed in sequence order.
** It is an error to install the same commit hook more than once.
**
** Each commit hook is called (in order of accending sequence) at
** each commit operation.  If any commit hook returns non-zero,
** the subsequence commit hooks are omitted and the transaction
** rolls back rather than commit.  It is the responsibility of the
** hooks themselves to issue any error messages.
*/
void db_commit_hook(int (*x)(void), int sequence){
  int i;
  assert( nCommitHook < sizeof(aHook)/sizeof(aHook[1]) );
  for(i=0; i<nCommitHook; i++){
    assert( x!=aHook[i].xHook );
    if( aHook[i].sequence>sequence ){
      int s = sequence;
      int (*xS)(void) = x;
      sequence = aHook[i].sequence;
      x = aHook[i].xHook;
      aHook[i].sequence = s;
      aHook[i].xHook = xS;
    }
  }
  aHook[nCommitHook].sequence = sequence;
  aHook[nCommitHook].xHook = x;
  nCommitHook++;
}

/*
** Prepare or reprepare the sqlite3 statement from the raw SQL text.
*/
static void reprepare(Stmt *pStmt){
  sqlite3_stmt *pNew;
  if( sqlite3_prepare(g.db, blob_buffer(&pStmt->sql), -1, &pNew, 0)!=0 ){
    db_err("%s\n%s", blob_str(&pStmt->sql), sqlite3_errmsg(g.db));
................................................................................
*/
int db_bind_int(Stmt *pStmt, const char *zParamName, int iValue){
  return sqlite3_bind_int(pStmt->pStmt, paramIdx(pStmt, zParamName), iValue);
}
int db_bind_int64(Stmt *pStmt, const char *zParamName, i64 iValue){
  return sqlite3_bind_int64(pStmt->pStmt, paramIdx(pStmt, zParamName), iValue);
}
int db_bind_double(Stmt *pStmt, const char *zParamName, double rValue){
  return sqlite3_bind_double(pStmt->pStmt, paramIdx(pStmt, zParamName), rValue);
}
int db_bind_text(Stmt *pStmt, const char *zParamName, const char *zValue){
  return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue,
                           -1, SQLITE_STATIC);
}
int db_bind_null(Stmt *pStmt, const char *zParamName){
  return sqlite3_bind_null(pStmt->pStmt, paramIdx(pStmt, zParamName));
}
................................................................................
  return sqlite3_column_int64(pStmt->pStmt, N);
}
double db_column_double(Stmt *pStmt, int N){
  return sqlite3_column_double(pStmt->pStmt, N);
}
const char *db_column_text(Stmt *pStmt, int N){
  return (char*)sqlite3_column_text(pStmt->pStmt, N);
}
const char *db_column_name(Stmt *pStmt, int N){
  return (char*)sqlite3_column_name(pStmt->pStmt, N);
}
int db_column_count(Stmt *pStmt){
  return sqlite3_column_count(pStmt->pStmt);
}
char *db_column_malloc(Stmt *pStmt, int N){
  return mprintf("%s", db_column_text(pStmt, N));
}
void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){
  blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N),
              sqlite3_column_bytes(pStmt->pStmt, N));
................................................................................

/*
** Open the user database in "~/.fossil".  Create the database anew if
** it does not already exist.
*/
void db_open_config(void){
  char *zDbName;
  const char *zHome;
#ifdef __MINGW32__
  zHome = getenv("LOCALAPPDATA");
  if( zHome==0 ){
    zHome = getenv("APPDATA");
    if( zHome==0 ){
      zHome = getenv("HOMEPATH");
    }
  }
#else
  zHome = getenv("HOME");
#endif
  if( zHome==0 ){
    db_err("cannot local home directory");
  }
#ifdef __MINGW32__
  /* . filenames give some window systems problems and many apps problems */
  zDbName = mprintf("%s/_fossil", zHome);
#else
  zDbName = mprintf("%s/.fossil", zHome);
#endif
  if( g.configOpen ) return;
  if( file_size(zDbName)<1024*3 ){
    db_init_database(zDbName, zConfigSchema, (char*)0);
  }
  db_open_or_attach(zDbName, "configdb");
  g.configOpen = 1;
}
................................................................................
}

/*
** Locate the root directory of the local repository tree.  The root
** directory is found by searching for a file named "FOSSIL" that contains
** a valid repository database.
**
** If no valid FOSSIL file is found, we move up one level and try again.
** Once the file is found, the g.zLocalRoot variable is set to the root of
** the repository tree and this routine returns 1.  If no database is
** found, then this routine return 0.
**
** This routine always opens the user database regardless of whether or
** not the repository database is found.  If the FOSSIL file is found,
** it is attached to the open database connection too.
*/
int db_open_local(void){
  int n;
  char zPwd[2000];
  char *zPwdConv;
  if( g.localOpen) return 1;
  if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
    db_err("pwd too big: max %d", sizeof(zPwd)-20);
  }
  n = strlen(zPwd);
  zPwdConv = mprintf("%/", zPwd);
  strncpy(zPwd, zPwdConv, 2000-20);
  free(zPwdConv);
  while( n>0 ){
    if( access(zPwd, W_OK) ) break;
    strcpy(&zPwd[n], "/_FOSSIL_");
    if( isValidLocalDb(zPwd) ){
      /* Found a valid _FOSSIL_ file */
      zPwd[n] = 0;
      g.zLocalRoot = mprintf("%s/", zPwd);
................................................................................

/*
** Fill an empty repository database with the basic information for a
** repository. This function is shared between 'create_repository_cmd'
** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
** new repositories.
**
** The makeInitialVersion flag determines whether or not an initial
** manifest is created.  The makeServerCodes flag determines whether or
** not server and project codes are invented for this repository.
*/

void db_initial_setup (int makeInitialVersion, int makeServerCodes){
  char *zDate;
  const char *zUser;
  Blob hash;
  Blob manifest;

  db_set("content-schema", CONTENT_SCHEMA, 0);
  db_set("aux-schema", AUX_SCHEMA, 0);
  if( makeServerCodes ){
    db_multi_exec(
      "INSERT INTO config(name,value)"
      " VALUES('server-code', lower(hex(randomblob(20))));"
      "INSERT INTO config(name,value)"
      " VALUES('project-code', lower(hex(randomblob(20))));"
    );
  }
  if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
  if( !db_is_global("safemerge") ) db_set_int("safemerge", 0, 0);
  if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
  zUser = db_get("default-user", 0);
  if( zUser==0 ){
#ifdef __MINGW32__
    zUser = getenv("USERNAME");
#else
    zUser = getenv("USER");
#endif
  }
  if( zUser==0 ){
    zUser = "root";
  }
  db_multi_exec(
     "INSERT INTO user(login, pw, cap, info)"
     "VALUES(%Q,'','s','')", zUser
................................................................................
     "INSERT INTO user(login,pw,cap,info)"
     "   VALUES('anonymous','anonymous','hjkorw','Anon');"
     "INSERT INTO user(login,pw,cap,info)"
     "   VALUES('nobody','','jor','Nobody');"
  );
  user_select();

  if (makeInitialVersion){
    blob_zero(&manifest);
    blob_appendf(&manifest, "C initial\\sempty\\sbaseline\n");
    zDate = db_text(0, "SELECT datetime('now')");
    zDate[10]='T';
    blob_appendf(&manifest, "D %s\n", zDate);
    blob_appendf(&manifest, "P\n");
    md5sum_init();
................................................................................
  if( g.argc!=3 ){
    usage("REPOSITORY-NAME");
  }
  db_create_repository(g.argv[2]);
  db_open_repository(g.argv[2]);
  db_open_config();
  db_begin_transaction();
  db_initial_setup(1, 1);
  db_end_transaction(0);
  printf("project-id: %s\n", db_get("project-code", 0));
  printf("server-id:  %s\n", db_get("server-code", 0));
  printf("admin-user: %s (no password set yet!)\n", g.zLogin);
  printf("baseline:   %s\n", db_text(0, "SELECT uuid FROM blob"));
}

................................................................................
  }
}

/*
** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
** repository and local databases.
*/
char *db_get(const char *zName, char *zDefault){
  char *z = 0;
  if( g.repositoryOpen ){
    z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
  }
  if( z==0 && g.configOpen ){
    z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
  }
  if( z==0 ){
    z = zDefault;
  }
  return z;
}
void db_set(const char *zName, const char *zValue, int globalFlag){
  db_begin_transaction();
  db_multi_exec("REPLACE INTO %sconfig(name,value) VALUES(%Q,%Q)",
                 globalFlag ? "global_" : "", zName, zValue);
  if( globalFlag && g.repositoryOpen ){
    db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
  }
  db_end_transaction(0);
}
int db_is_global(const char *zName){
  if( g.configOpen ){
    return db_exists("SELECT 1 FROM global_config WHERE name=%Q", zName);
  }else{
    return 0;
  }
}
int db_get_int(const char *zName, int dflt){
  int v;
  int rc;
  if( g.repositoryOpen ){
    Stmt q;
    db_prepare(&q, "SELECT value FROM config WHERE name=%Q", zName);
    rc = db_step(&q);
    if( rc==SQLITE_ROW ){
      v = db_column_int(&q, 0);
    }
    db_finalize(&q);
  }else{
    rc = SQLITE_DONE;
  }
  if( rc==SQLITE_DONE && g.configOpen ){
    v = db_int(dflt, "SELECT value FROM global_config WHERE name=%Q", zName);
  }
  return v;
}
void db_set_int(const char *zName, int value, int globalFlag){
  db_begin_transaction();
  db_multi_exec("REPLACE INTO %sconfig(name,value) VALUES(%Q,%d)",
                globalFlag ? "global_" : "", zName, value);
  if( globalFlag && g.repositoryOpen ){
    db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
  }



  db_end_transaction(0);
}
int db_get_boolean(const char *zName, int dflt){


  static const char *azOn[] = { "on", "yes", "true", "1" };
  static const char *azOff[] = { "off", "no", "false", "0" };
  int i;
  char *zVal = db_get(zName, dflt ? "on" : "off");
  for(i=0; i<sizeof(azOn)/sizeof(azOn[0]); i++){
    if( strcmp(zVal,azOn[i])==0 ) return 1;
  }
  for(i=0; i<sizeof(azOff)/sizeof(azOff[0]); i++){
    if( strcmp(zVal,azOff[i])==0 ) return 0;
  }
  return dflt;
}
char *db_lget(const char *zName, char *zDefault){
  return db_text((char*)zDefault,
                 "SELECT value FROM vvar WHERE name=%Q", zName);
}
void db_lset(const char *zName, const char *zValue){
  db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%Q)", zName, zValue);
}
int db_lget_int(const char *zName, int dflt){
  return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
................................................................................
**
** Open a connection to the local repository in FILENAME.  A checkout
** for the repository is created with its root at the working directory.
** See also the "close" command.
*/
void cmd_open(void){
  Blob path;
  int vid;
  static char *azNewArgv[] = { 0, "update", "--latest", 0 };
  if( g.argc!=3 ){
    usage("REPOSITORY-FILENAME");
  }
  if( db_open_local() ){
    fossil_panic("already within an open tree rooted at %s", g.zLocalRoot);
  }
  file_canonical_name(g.argv[2], &path);
  db_open_repository(blob_str(&path));
  db_init_database("./_FOSSIL_", zLocalSchema, (char*)0);
  db_open_local();
  db_lset("repository", blob_str(&path));
  vid = db_int(0, "SELECT pid FROM plink y"
                  " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)");
  if( vid==0 ){
    db_lset_int("checkout", 1);
  }else{
    db_lset_int("checkout", vid);
    g.argv = azNewArgv;
    g.argc = 3;
    update_cmd();
  }
}

/*
** Print the value of a setting named zName
*/
static void print_setting(const char *zName){
  Stmt q;
  db_prepare(&q,
     "SELECT '(local)', value FROM config WHERE name=%Q"
     " UNION ALL "
     "SELECT '(global)', value FROM global_config WHERE name=%Q",
     zName, zName
  );
  if( db_step(&q)==SQLITE_ROW ){
    printf("%-20s %-8s %s\n", zName, db_column_text(&q, 0),
        db_column_text(&q, 1));
  }else{
    printf("%-20s\n", zName);
  }
  db_finalize(&q);
}


/*
** COMMAND: settings
** %fossil setting ?PROPERTY? ?VALUE? ?-global?
**

** With no arguments, list all properties and their values.  With just
** a property name, show the value of that property.  With a value
** argument, change the property for the current repository.
**




**    autosync         If enabled, automatically pull prior to
**                     commit or update and automatically push
**                     after commit or tag or branch creation.
**

**    clearsign        Command used to clear-sign manifests at check-in.
**                     The default is "gpg --clearsign -o ".
**
**    editor           Text editor command used for check-in comments.
**


**    localauth        If enabled, require that HTTP connections from
**                     127.0.0.1 be authenticated by password.  If
**                     false, all HTTP requests from localhost have
**                     unrestricted access to the repository.
**
**    omitsign         When enabled, fossil will not attempt to sign any
**                     commit with gpg. All commits will be unsigned.
**
**    safemerge        If enabled, when commit will cause a fork, the
**                     commit will not abort with warning. Also update
**                     will not be allowed if local changes exist.
**
**   diff-command      External command to run when performing a diff.
**                     If undefined, the internal text diff will be used.
**
**   gdiff-command     External command to run when performing a graphical
**                     diff. If undefined, text diff will be used.
*/



void setting_cmd(void){
  static const char *azName[] = {
    "autosync",
    "clearsign",
    "editor",
    "localauth",
    "omitsign",
    "safemerge",
    "diff-command",
    "gdiff-command",
  };
  int i;




  int globalFlag = find_option("global","g",0)!=0;
  db_find_and_open_repository();
  if( g.argc==2 ){
    for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
      print_setting(azName[i]);
    }









  }else if( g.argc==3 || g.argc==4 ){
    const char *zName = g.argv[2];
    int n = strlen(zName);
    for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
      if( strncmp(azName[i], zName, n)==0 ) break;
    }
    if( i>=sizeof(azName)/sizeof(azName[0]) ){
      fossil_fatal("no such setting: %s", zName);
    }



    if( g.argc==4 ){
      db_set(azName[i], g.argv[3], globalFlag);
    }else{

      print_setting(azName[i]);
    }


  }else{




    usage("?PROPERTY? ?VALUE?");
  }

}

Changes to src/delta.c.

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

  /* Begin scanning the target file and generating copy commands and
  ** literal sections of the delta.
  */
  base = 0;    /* We have already generated everything before zOut[base] */
  while( base+NHASH<lenOut ){
    int iSrc, iBlock;
    unsigned int bestCnt, bestOfst, bestLitsz;
    hash_init(&h, &zOut[base]);
    i = 0;     /* Trying to match a landmark against zOut[base+i] */
    bestCnt = 0;
    while( 1 ){
      int hv;
      int limit = 250;








|







332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

  /* Begin scanning the target file and generating copy commands and
  ** literal sections of the delta.
  */
  base = 0;    /* We have already generated everything before zOut[base] */
  while( base+NHASH<lenOut ){
    int iSrc, iBlock;
    unsigned int bestCnt, bestOfst=0, bestLitsz=0;
    hash_init(&h, &zOut[base]);
    i = 0;     /* Trying to match a landmark against zOut[base+i] */
    bestCnt = 0;
    while( 1 ){
      int hv;
      int limit = 250;

Changes to src/descendents.c.

145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214
215
216
217
    base = db_lget_int("checkout", 0);
  }else{
    base = name_to_rid(g.argv[2]);
  }
  if( base==0 ) return;
  compute_leaves(base);
  db_prepare(&q,
    "SELECT blob.rid, uuid, datetime(event.mtime,'localtime'), comment, 0,"
    "       (SELECT count(*) FROM plink WHERE cid=blob.rid)"
    "  FROM leaves, blob, event"
    " WHERE blob.rid=leaves.rid"
    "   AND event.objid=leaves.rid"
    " ORDER BY event.mtime DESC"

  );
  print_timeline(&q, 20);
  db_finalize(&q);
}

/*
** COMMAND:  leaves
................................................................................
** Find leaves of all branches.
*/
void branches_cmd(void){
  Stmt q;

  db_must_be_within_tree();
  db_prepare(&q,
    "SELECT blob.rid, blob.uuid, datetime(event.mtime,'localtime'),"
    "       event.comment, 0,"
    "       (SELECT count(*) FROM plink WHERE cid=blob.rid)"
    "  FROM blob, event"
    " WHERE blob.rid IN"
    "       (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
    "   AND event.objid=blob.rid"
    " ORDER BY event.mtime DESC"

  );
  print_timeline(&q, 2000);
  db_finalize(&q);
}

/*
** WEBPAGE:  leaves
................................................................................
  Stmt q;

  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }

  style_header("Leaves");
  db_prepare(&q,
    "SELECT blob.rid, blob.uuid, datetime(event.mtime,'localtime'),"
    "       event.comment, event.user, 1, 1, 0"
    "  FROM blob, event"
    " WHERE blob.rid IN"
    "       (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
    "   AND event.objid=blob.rid"
    " ORDER BY event.mtime DESC"

  );
  www_print_timeline(&q, 0, 0, 0, 0);
  db_finalize(&q);
  @ <script>
  @ function xin(id){
  @ }
  @ function xout(id){
  @ }
  @ </script>
  style_footer();
}







|
<
<
<
|
|
>







 







|
<
<
<
|

<
|
>







 







|
<
<
|

<
|
>











145
146
147
148
149
150
151
152



153
154
155
156
157
158
159
160
161
162
...
165
166
167
168
169
170
171
172



173
174

175
176
177
178
179
180
181
182
183
...
188
189
190
191
192
193
194
195


196
197

198
199
200
201
202
203
204
205
206
207
208
209
210
    base = db_lget_int("checkout", 0);
  }else{
    base = name_to_rid(g.argv[2]);
  }
  if( base==0 ) return;
  compute_leaves(base);
  db_prepare(&q,
    "%s"



    "   AND event.objid IN (SELECT rid FROM leaves)"
    " ORDER BY event.mtime DESC",
    timeline_query_for_tty()
  );
  print_timeline(&q, 20);
  db_finalize(&q);
}

/*
** COMMAND:  leaves
................................................................................
** Find leaves of all branches.
*/
void branches_cmd(void){
  Stmt q;

  db_must_be_within_tree();
  db_prepare(&q,
    "%s"



    "   AND blob.rid IN"
    "       (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"

    " ORDER BY event.mtime DESC",
    timeline_query_for_tty()
  );
  print_timeline(&q, 2000);
  db_finalize(&q);
}

/*
** WEBPAGE:  leaves
................................................................................
  Stmt q;

  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }

  style_header("Leaves");
  db_prepare(&q,
    "%s"


    "   AND blob.rid IN"
    "       (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"

    " ORDER BY event.mtime DESC",
    timeline_query_for_www()
  );
  www_print_timeline(&q, 0, 0, 0, 0);
  db_finalize(&q);
  @ <script>
  @ function xin(id){
  @ }
  @ function xout(id){
  @ }
  @ </script>
  style_footer();
}

Changes to src/diff.c.

17
18
19
20
21
22
23
24

25
26
27
28
29







30
31




32
33
34
35
36
37
38


39
40
41








42
43
44
45
46




47





48


49






50
51



52
53
54
55
56
57
58
59
60



61
62


63
64


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82
83
84
85































































































86

















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











































































































































218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to implement "diff" operators.

*/
#include "config.h"
#include "diff.h"
#include <assert.h>








/*
** Information about each line of a file being diffed.




*/
typedef struct DLine DLine;
struct DLine {
  const char *z;    /* The text of the line */
  unsigned int h;   /* Hash of the line */
};



/*
** Break a blob into lines by converting each \n into a \000 and
** creating pointers to the beginning of each line.








*/
static DLine *break_into_lines(char *z, int *pnLine){
  int nLine, i, j;
  unsigned int h;
  DLine *a;




  for(i=0, nLine=1; z[i]; i++){





    if( z[i]=='\n' ) nLine++;


  }






  a = malloc( nLine*sizeof(a[0]) );
  if( a==0 ) fossil_panic("out of memory");



  a[0].z = z;
  for(i=0, j=0, h=0; z[i]; i++){
    if( z[i]=='\n' ){
      a[j].h = h;
      j++;
      a[j].z = &z[i+1];
      z[i] = 0;
      h = 0;
    }else{



      h = h ^ (h<<2) ^ z[i];
    }


  }
  a[j].h = h;


  *pnLine = j+1;
  return a;
}

/*
** Return true if two DLine elements are identical.
*/
static int same_dline(DLine *pA, DLine *pB){
  return pA->h==pB->h && strcmp(pA->z,pB->z)==0;
}

/*
** Generate a unified diff of two blobs.  The text of the original
** two blobs is destroyed by the diffing process.
*/

void unified_diff(Blob *pA, Blob *pB, int nContext, Blob *pOut){
  DLine *pDA, *pDB, *A, *B;
  int nA, nB, nAp1;
  int x, y;
  int cnt;
  int i, iStart;































































































  int *m;


















  /* Break the two files being diffed into individual lines.
  ** Compute hashes on each line for fast comparison.
  */
  pDA = break_into_lines(blob_str(pA), &nA);
  pDB = break_into_lines(blob_str(pB), &nB);

  /* Remove common prefix and suffix to help reduce the value
  ** of N in the O(N^2) minimum edit distance algorithm.
  */
  for(i=0; i<nA && i<nB && same_dline(&pDA[i],&pDB[i]); i++){}
  i -= nContext;
  if( i<0 ) i = 0;
  iStart = i;
  A = &pDA[iStart];
  B = &pDB[iStart];
  nA -= iStart;
  nB -= iStart;
  for(i=1; i<nA && i<nB && same_dline(&A[nA-i],&B[nB-i]); i++){}
  i -= nContext;
  if( i<1 ) i = 1;
  i--;
  nA -= i;
  nB -= i;
  
  /* Create the matrix used for the minimum edit distance
  ** calculation.
  */
  nAp1 = nA + 1;
  m = malloc( sizeof(m[0])*(nB+1)*nAp1 );
# define M(X,Y) m[(Y)*nAp1+(X)]


  /* Find the minimum edit distance using Wagner's algorithm.
  */
  for(x=0; x<=nA; x++){
    M(x,0) = x;
  }
  for(y=0; y<=nB; y++){
    M(0,y) = y;
  }
  for(x=1; x<=nA; x++){
    for(y=1; y<=nB; y++){
      int e = M(x-1,y) + 1;
      if( e>M(x,y-1)+1 ){
        e = M(x,y-1)+1;
      }
      if( e<=M(x-1,y-1) ){
        M(x,y) = e;
      }else if( same_dline(&A[x-1], &B[y-1]) ){
        M(x,y) = M(x-1,y-1);
      }else{
        M(x,y) = e;
      }
    }
  }

  /* Walk backwards through the Wagner algorithm matrix to determine
  ** the specific edits that give the minimum edit distance.  Mark our
  ** path through the matrix with -1.
  */
  x = nA;
  y = nB;
  while( x>0 || y>0 ){
    int v = M(x,y);
    M(x,y) = -1;
    if( x==0 ){
      y--;
    }else if( y==0 ){
      x--;
    }else if( M(x,y-1)+1==v ){
      y--;
    }else if( M(x-1,y)+1==v ){
      x--;
    }else{
      x--;
      y--;
    }
  }

#if 0
for(y=0; y<=nB; y++){
  for(x=0; x<=nA; x++){
    printf(" %2d", M(x,y));
  }
  printf("\n");
}
#endif

  x = y = 0;
  cnt = nContext;
  while( x<nA || y<nB ){
    int t1, t2;
    if( (t1 = M(x+1,y))<0 || (t2 = M(x,y+1))<0 ){
      if( cnt>=nContext ){
        blob_appendf(pOut, "@@ -%d +%d @@\n", 
            x-nContext+iStart+2, y-nContext+iStart+2);
        for(i=x-nContext+1; i<x; i++){
          if( i<0 ) continue;
          blob_appendf(pOut, " %s\n", A[i].z);
        }
      }
    }
    if( t1<0 ){
      blob_appendf(pOut, "-%s\n", A[x].z);
      x++;
      cnt = 0;
    }else if( t2<0 ){
      blob_appendf(pOut, "+%s\n", B[y].z);
      y++;
      cnt = 0;
    }else{
      if( M(x+1,y+1)==(-1) && cnt<nContext ){
        blob_appendf(pOut, " %s\n", A[x].z);
      }
      cnt++;
      x++;
      y++;
    }
  }

  /* Cleanup allocationed memory */
  free(m);
  free(pDA);
  free(pDB);
}

/*
** COMMAND: test-diff
*/
void test_diff_cmd(void){











































































































































  Blob a, b, out;
  if( g.argc!=4 ) usage("FILE1 FILE2");
  blob_read_from_file(&a, g.argv[2]);
  blob_read_from_file(&b, g.argv[3]);
  blob_zero(&out);
  unified_diff(&a, &b, 4, &out);
  blob_reset(&a);
  blob_reset(&b);
  printf("%s", blob_str(&out));
  blob_reset(&out);
}
/*
** COMMAND: test-uuid-diff
*/
void test_uuiddiff_cmd(void){
  Blob a, b, out;
  int ridA, ridB;
  if( g.argc!=4 ) usage("UUID2 UUID1");
  db_must_be_within_tree();
  ridA = name_to_rid(g.argv[2]);
  content_get(ridA, &a);
  ridB = name_to_rid(g.argv[3]);
  content_get(ridB, &b);
  blob_zero(&out);
  unified_diff(&a, &b, 4, &out);
  blob_reset(&a);
  blob_reset(&b);
  printf("%s", blob_str(&out));
  blob_reset(&out);
}







|
>





>
>
>
>
>
>
>


>
>
>
>







>
>

<
<
>
>
>
>
>
>
>
>


|


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


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

>
>

<
>
>
|







|



|
<

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




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





|
|
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53


54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92








93
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

116
117
118
119
120



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510



511



















**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to compute a "diff" between two
** text files.
*/
#include "config.h"
#include "diff.h"
#include <assert.h>


#if 0
#define DEBUG(X) X
#else
#define DEBUG(X)
#endif

/*
** Information about each line of a file being diffed.
**
** The lower 20 bits of the hash are the length of the
** line.  If any line is longer than 1048575 characters,
** the file is considered binary.
*/
typedef struct DLine DLine;
struct DLine {
  const char *z;    /* The text of the line */
  unsigned int h;   /* Hash of the line */
};

#define LENGTH_MASK  0x000fffff

/*


** Return an array of DLine objects containing a pointer to the
** start of each line and a hash of that line.  The lower 
** bits of the hash store the length of each line.
**
** Trailing whitespace is removed from each line.
**
** Return 0 if the file is binary or contains a line that is
** longer than 1048575 bytes.
*/
static DLine *break_into_lines(char *z, int *pnLine){
  int nLine, i, j, k, x;
  unsigned int h;
  DLine *a;

  /* Count the number of lines.  Allocate space to hold
  ** the returned array.
  */
  for(i=j=0, nLine=1; z[i]; i++, j++){
    int c = z[i];
    if( c==0 ){
      return 0;
    }
    if( c=='\n' && z[i+1]!=0 ){
      nLine++;
      if( j>1048575 ){
        return 0;
      }
      j = 0;
    }
  }
  if( j>1048575 ){
    return 0;
  }
  a = malloc( nLine*sizeof(a[0]) );
  if( a==0 ) fossil_panic("out of memory");

  /* Fill in the array */
  for(i=0; i<nLine; i++){
    a[i].z = z;








    for(j=0; z[j] && z[j]!='\n'; j++){}
    for(k=j; k>0 && isspace(z[k-1]); k--){}
    for(h=0, x=0; x<k; x++){
      h = h ^ (h<<2) ^ z[x];
    }
    a[i].h = (h<<20) | k;;
    z += j+1;
  }


  /* Return results */
  *pnLine = nLine;
  return a;
}

/*
** Return true if two DLine elements are identical.
*/
static int same_dline(DLine *pA, DLine *pB){
  return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
}

/*
** Append a single line of "diff" output to pOut.

*/
static void appendDiffLine(Blob *pOut, char *zPrefix, DLine *pLine){
  blob_append(pOut, zPrefix, 1);
  blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
  blob_append(pOut, "\n", 1);



}


/*
** Generate a report of the differences between files pA and pB.
** If pOut is not NULL then a unified diff is appended there.  It
** is assumed that pOut has already been initialized.  If pOut is
** NULL, then a pointer to an array of integers is returned.  
** The integers come in triples.  For each triple,
** the elements are the number of lines copied, the number of
** lines deleted, and the number of lines inserted.  The vector
** is terminated by a triple of all zeros.
**
** This diff utility does not work on binary files.  If a binary
** file is encountered, 0 is returned and pOut is written with
** text "cannot compute difference between binary files".
**
****************************************************************************
**
** The core algorithm is a variation on the classic Wagner
** minimum edit distance with enhancements to reduce the runtime
** to be almost linear in the common case where the two files
** have a lot in common.  For additional information see
** Eugene W. Myers, "An O(ND) Difference Algorithm And Its
** Variations"
**
** Consider comparing strings A and B.  A=abcabba and B=cbabac
** We construct a "Wagner" matrix W with A along the X axis and 
** B along the Y axis:
**
**     c 6               *
**     a 5               *
**     b 4           * *
**     a 3         *
**     b 2       *
**   B c 1       *
**       0 * * * 
**         0 1 2 3 4 5 6 7
**           a b c a b b a
**           A
**
** (Note: we draw this Wagner matrix with the origin at the lower 
** left whereas Myers uses the origin at the upper left.  Otherwise,
** they are the same.)
**
** Let Y be the maximum y value or the number of characters in B.
** 6 in this example.  X is the maximum x value or the number of
** elements in A.  Here 7.
**
** Our goal is to find a path from (0,0) to (X,Y).  The path can
** use horizontal, vertical, or diagonal steps.  A diagonal from
** (x-1,y-1) to (x,y) is only allowed if A[x]==B[y].  A vertical
** steps corresponds to an insertion.  A horizontal step corresponds
** to a deletion.  We want to find the path with the fewest
** horizontal and vertical steps.
**
** Diagonal k consists of all points such that x-y==k.  Diagonal
** zero begins at the origin.  Diagonal 1 begins at (1,0).  
** Diagonal -1 begins at (0,1).  All diagonals move up and to the
** right at 45 degrees.  Diagonal number increase from upper left
** to lower right.
** 
** Myers matrix M is a lower right triangular matrix with indices d
** along the bottom and i vertically:
**
** 
**   i=4 |            +4  \
**     3 |         +3 +2   |
**     2 |      +2 +1  0   |- k values.   k = 2*i-d
**     1 |   +1  0 -1 -2   |
**     0 | 0 -1 -2 -3 -4  /
**       ---------------
**         0  1  2  3  4 = d
**
** Each element of the Myers matrix corresponds to a diagonal.
** The diagonal is k=2*i-d.  The diagonal values are shown
** in the template above.
**
** Each entry in M represents the end-point on a path from (0,0).
** The end-point is on diagonal k.  The value stored in M is
** q=x+y where (x,y) is the terminus of the path.  A path
** to M[d][i] will come through either M[d-1][i-1] or
** though M[d-1][i], whichever holds the largest value of q.
** If both elements hold the same value, the path comes
** through M[d-1][i-1].
**
** The value of d is the number of insertions and deletions
** made so far on the path.  M grows progressively.  So the
** size of the M matrix is proportional to d*d.  For the
** common case where A and B are similar, d will be small
** compared to X and Y so little memory is required.  The
** original Wagner algorithm requires X*Y memory, which for
** larger files (100K lines) is more memory than we have at
** hand.
*/
int *text_diff(
  Blob *pA_Blob,   /* FROM file */
  Blob *pB_Blob,   /* TO file */
  Blob *pOut,      /* Write unified diff here if not NULL */
  int nContext     /* Amount of context to unified diff */
){
  DLine *A, *B;    /* Files being compared */
  int X, Y;        /* Number of elements in A and B */
  int x, y;        /* Indices:  A[x] and B[y] */
  int szM = 0;     /* Number of rows and columns in M */
  int **M = 0;     /* Myers matrix */
  int i, d;        /* Indices on M.  M[d][i] */
  int k, q;        /* Diagonal number and distinct from (0,0) */
  int K, D;        /* The diagonal and d for the final solution */          
  int *R = 0;      /* Result vector */
  int r;           /* Loop variables */
  int go = 1;      /* Outer loop control */
  int MAX;         /* Largest of X and Y */

  /* Break the two files being diffed into individual lines.
  ** Compute hashes on each line for fast comparison.
  */
  A = break_into_lines(blob_str(pA_Blob), &X);
  B = break_into_lines(blob_str(pB_Blob), &Y);

  if( A==0 || B==0 ){
    free(A);
    free(B);
    if( pOut ){
      blob_appendf(pOut, "cannot compute difference between binary files\n");
    }
    return 0;
  }

  szM = 0;
  MAX = X>Y ? X : Y;
  if( MAX>2000 ) MAX = 2000;
  for(d=0; go && d<=MAX; d++){
    if( szM<d+1 ){
      szM += szM + 10;
      M = realloc(M, sizeof(M[0])*szM);
      if( M==0 ){
        fossil_panic("out of memory");
      }
    }
    M[d] = malloc( sizeof(M[d][0])*(d+1) );
    if( M[d]==0 ){
      fossil_panic("out of memory");
    }
    for(i=0; i<=d; i++){
      k = 2*i - d;
      if( d==0 ){
        q = 0;
      }else if( i==0 ){
        q = M[d-1][0];
      }else if( i<d-1 && M[d-1][i-1] < M[d-1][i] ){
        q = M[d-1][i];
      }else{
        q = M[d-1][i-1];
      }
      x = (k + q + 1)/2;
      y = x - k;
      if( x<0 || x>X || y<0 || y>Y ){
        x = y = 0;
      }else{
        while( x<X && y<Y && same_dline(&A[x],&B[y]) ){ x++; y++; }
      }
      M[d][i] = x + y;
      DEBUG( printf("M[%d][%i] = %d  k=%d (%d,%d)\n", d, i, x+y, k, x, y); )
      if( x==X && y==Y ){
        go = 0;
        break;
      }
    }
  }
  if( d>MAX ){
    R = malloc( sizeof(R[0])*7 );
    R[0] = 0;
    R[1] = X;
    R[2] = Y;
    R[3] = 0;
    R[4] = 0;
    R[5] = 0;
    R[6] = 0;
  }else{
    /* Reuse M[] as follows:
    **
    **     M[d][1] = 1 if a line is inserted or 0 if a line is deleted.
    **     M[d][0] = number of lines copied after the ins or del above.
    **
    */
    D = d - 1;
    K = X - Y;
    for(d=D, i=(K+D)/2; d>0; d--){
      DEBUG( printf("d=%d i=%d\n", d, i); )
      if( i==d || (i>0 && M[d-1][i-1] > M[d-1][i]) ){
        M[d][0] = M[d][i] - M[d-1][i-1] - 1;
        M[d][1] = 0;
        i--;
      }else{
        M[d][0] = M[d][i] - M[d-1][i] - 1;
        M[d][1] = 1;
      }
    }
    
    DEBUG(
      printf("---------------\nM[0][0] = %5d\n", M[0][0]);
      for(d=1; d<=D; d++){
        printf("M[%d][0] = %5d    M[%d][1] = %d\n",d,M[d][0],d,M[d][1]);
      }
    )
    
    /* Allocate the output vector
    */
    R = malloc( sizeof(R[0])*(D+2)*3 );
    if( R==0 ){
      fossil_fatal("out of memory");
    }
    
    /* Populate the output vector
    */
    d = r = 0;
    while( d<=D ){
      int n;
      R[r++] = M[d++][0]/2;   /* COPY */
      if( d>D ){
        R[r++] = 0;
        R[r++] = 0;
        break;
      }
      if( M[d][1]==0 ){
        n = 1;
        while( M[d][0]==0 && d<D && M[d+1][1]==0 ){
          d++;
          n++;
        }
        R[r++] = n;           /* DELETE */
        if( d==D || M[d][0]>0 ){
          R[r++] = 0;         /* INSERT */
          continue;
        }
        d++;
      }else{
        R[r++] = 0;           /* DELETE */
      }
      assert( M[d][1]==1 );
      n = 1;
      while( M[d][0]==0 && d<D && M[d+1][1]==1 ){
        d++;
        n++;
      }
      R[r++] = n;            /* INSERT */
    }
    R[r++] = 0;
    R[r++] = 0;
    R[r++] = 0;
  }
    
  /* Free the Myers matrix */
  for(d=0; d<=D; d++){
    free(M[d]);
  }
  free(M);

  /* If pOut is defined, construct a unified diff into pOut and
  ** delete R
  */
  if( pOut ){
    int a = 0;    /* Index of next line in A[] */
    int b = 0;    /* Index of next line in B[] */
    int nr;       /* Number of COPY/DELETE/INSERT triples to process */
    int mxr;      /* Maximum value for r */
    int na, nb;   /* Number of lines shown from A and B */
    int i, j;     /* Loop counters */
    int m;        /* Number of lines to output */
    int skip;     /* Number of lines to skip */

    for(mxr=0; R[mxr+1] || R[mxr+2] || R[mxr+3]; mxr += 3){}
    for(r=0; r<mxr; r += 3*nr){
      /* Figure out how many triples to show in a single block */
      for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
      DEBUG( printf("r=%d nr=%d\n", r, nr); )

      /* For the current block comprising nr triples, figure out
      ** how many lines of A and B are to be displayed
      */
      if( R[r]>nContext ){
        na = nb = nContext;
        skip = R[r] - nContext;
      }else{
        na = nb = R[r];
        skip = 0;
      }
      for(i=0; i<nr; i++){
        na += R[r+i*3+1];
        nb += R[r+i*3+2];
      }
      if( R[r+nr*3]>nContext ){
        na += nContext;
        nb += nContext;
      }else{
        na += R[r+nr*3];
        nb += R[r+nr*3];
      }
      for(i=1; i<nr; i++){
        na += R[r+i*3];
        nb += R[r+i*3];
      }
      blob_appendf(pOut,"@@ -%d,%d +%d,%d @@\n", a+skip+1, na, b+skip+1, nb);

      /* Show the initial common area */
      a += skip;
      b += skip;
      m = R[r] - skip;
      for(j=0; j<m; j++){
        appendDiffLine(pOut, " ", &A[a+j]);
      }
      a += m;
      b += m;

      /* Show the differences */
      for(i=0; i<nr; i++){
        m = R[r+i*3+1];
        for(j=0; j<m; j++){
          appendDiffLine(pOut, "-", &A[a+j]);
        }
        a += m;
        m = R[r+i*3+2];
        for(j=0; j<m; j++){
          appendDiffLine(pOut, "+", &B[b+j]);
        }
        b += m;
        if( i<nr-1 ){
          m = R[r+i*3+3];
          for(j=0; j<m; j++){
            appendDiffLine(pOut, " ", &B[b+j]);
          }
          b += m;
          a += m;
        }
      }

      /* Show the final common area */
      assert( nr==i );
      m = R[r+nr*3];
      if( m>nContext ) m = nContext;
      for(j=0; j<m; j++){
        appendDiffLine(pOut, " ", &B[b+j]);
      }
    }
    free(R);
    R = 0;
  }

  /* We no longer need the A[] and B[] vectors */
  free(A);
  free(B);

  /* Return the result */
  return R;
}

/*
** COMMAND: test-rawdiff
*/
void test_rawdiff_cmd(void){
  Blob a, b;
  int r;
  int i;
  int *R;
  if( g.argc<4 ) usage("FILE1 FILE2 ...");
  blob_read_from_file(&a, g.argv[2]);
  for(i=3; i<g.argc; i++){
    if( i>3 ) printf("-------------------------------\n");
    blob_read_from_file(&b, g.argv[i]);
    R = text_diff(&a, &b, 0, 0);
    for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
      printf(" copy %4d  delete %4d  insert %4d\n", R[r], R[r+1], R[r+2]);
    }
    /* free(R); */
    blob_reset(&b);
  }
}

/*
** COMMAND: test-udiff
*/
void test_udiff_cmd(void){
  Blob a, b, out;
  if( g.argc!=4 ) usage("FILE1 FILE2");
  blob_read_from_file(&a, g.argv[2]);
  blob_read_from_file(&b, g.argv[3]);
  blob_zero(&out);
  text_diff(&a, &b, &out, 3);
  blob_write_to_file(&out, "-");



}



















Changes to src/diffcmd.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53

54

















55
56
57
58
59
60
61
62







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92

93


94
95
96
97
98
99
100
101
102









103
104
105
106
107
108
109
110
111
112
113
114
115
116
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
................................................................................
  blob_appendf(pBlob, "\"%s\"", zIn);
  z = blob_buffer(pBlob);
  for(i=n+1; i<=n+k; i++){
    if( z[i]=='"' ) z[i] = '_';
  }
}



/*
** COMMAND: diff
** COMMAND: tkdiff
**
** Usage: %fossil diff|tkdiff FILE...

** Show the difference between the current version of a file (as it
** exists on disk) and that same file as it was checked out.  Use

** either "diff -u" or "tkdiff".

















*/
void diff_cmd(void){
  const char *zFile;
  Blob cmd;
  Blob fname;
  int i;
  char *zV1 = 0;
  char *zV2 = 0;








  if( g.argc<3 ){
    usage("?OPTIONS? FILE");
  }
  db_must_be_within_tree();
  blob_zero(&cmd);
  blob_appendf(&cmd, "%s ", g.argv[1]);
  for(i=2; i<g.argc-1; i++){
    const char *z = g.argv[i];
    if( (strcmp(z,"-v")==0 || strcmp(z,"--version")==0) && i<g.argc-2 ){
      if( zV1==0 ){
        zV1 = g.argv[i+1];
      }else if( zV2==0 ){
        zV2 = g.argv[i+1];
      }else{
        fossil_panic("too many versions");
      }
    }else{
      blob_appendf(&cmd, "%s ", z);
    }
  }
  zFile = g.argv[g.argc-1];
  if( !file_tree_name(zFile, &fname) ){
    fossil_panic("unknown file: %s", zFile);
  }
  if( zV1==0 ){

    int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname);
    Blob record;
    Blob vname;
    int cnt = 0;




    if( rid==0 ){
      fossil_panic("no history for file: %b", &fname);
    }
    blob_zero(&vname);
    do{
      blob_reset(&vname);
      blob_appendf(&vname, "%s~%d", zFile, cnt++);
    }while( access(blob_str(&vname),0)==0 );
    content_get(rid, &record);









    blob_write_to_file(&record, blob_str(&vname));
    blob_reset(&record);
    shell_escape(&cmd, blob_str(&vname));
    blob_appendf(&cmd, " ");
    shell_escape(&cmd, zFile);
    system(blob_str(&cmd));
    unlink(blob_str(&vname));
    blob_reset(&vname);
    blob_reset(&cmd);
  }else{
    fossil_panic("not yet implemented");
  }
  blob_reset(&fname);
}







|







 







<
<


|

|
>

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


|


<
<
<
>
>
>
>
>
>
>





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





<
>
|
|
|
|
>

>
>



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









<
<



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
38
39
40
41
42
43
44


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76



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


102
103
104
105
106

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142


143
144
145
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
................................................................................
  blob_appendf(pBlob, "\"%s\"", zIn);
  z = blob_buffer(pBlob);
  for(i=n+1; i<=n+k; i++){
    if( z[i]=='"' ) z[i] = '_';
  }
}



/*
** COMMAND: diff
** COMMAND: gdiff
**
** Usage: %fossil diff|gdiff ?-i? ?-r REVISION? FILE...
**
** Show the difference between the current version of a file (as it
** exists on disk) and that same file as it was checked out.
**
** diff will show a textual diff while gdiff will attempt to run a
** graphical diff command that you have setup. If the choosen command
** is not yet configured, the internal textual diff command will be
** used.
**
** If -i is supplied for either diff or gdiff, the internal textual
** diff command will be executed.
**
** Here are a few external diff command settings, for example:
**
**   %fossil setting diff-command diff
**
**   %fossil setting gdiff-command tkdiff
**   %fossil setting gdiff-command eskill22
**   %fossil setting gdiff-command tortoisemerge
**   %fossil setting gdiff-command meld
**   %fossil setting gdiff-command xxdiff
**   %fossil setting gdiff-command kdiff3
*/
void diff_cmd(void){
  const char *zFile, *zRevision;
  Blob cmd;
  Blob fname;



  Blob vname;
  Blob record;
  int cnt=0,internalDiff;

  internalDiff = find_option("internal","i",0)!=0;
  zRevision = find_option("revision", "r", 1);
  verify_all_options();

  if( g.argc<3 ){
    usage("?OPTIONS? FILE");
  }
  db_must_be_within_tree();

  if( internalDiff==0 ){
    const char *zExternalCommand;
    if( strcmp(g.argv[1], "diff")==0 ){
      zExternalCommand = db_get("diff-command", 0);
    }else{
      zExternalCommand = db_get("gdiff-command", 0);
    }
    if( zExternalCommand==0 ){
      internalDiff=1;
    }
    blob_zero(&cmd);
    blob_appendf(&cmd, "%s ", zExternalCommand);


  }
  zFile = g.argv[g.argc-1];
  if( !file_tree_name(zFile, &fname) ){
    fossil_panic("unknown file: %s", zFile);
  }


  blob_zero(&vname);
  do{
    blob_reset(&vname);
    blob_appendf(&vname, "%s~%d", zFile, cnt++);
  }while( access(blob_str(&vname),0)==0 );

  if( zRevision==0 ){
    int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname);
    if( rid==0 ){
      fossil_panic("no history for file: %b", &fname);
    }
    content_get(rid, &record);
  }else{
    historical_version_of_file(zRevision, zFile, &record);
  }
  if( internalDiff==1 ){
    Blob out;
    Blob current;
    blob_zero(&current);
    blob_read_from_file(&current, zFile);
    blob_zero(&out);
    text_diff(&record, &current, &out, 5);
    printf("%s\n", blob_str(&out));
    blob_reset(&current);
    blob_reset(&out);
  }else{
    blob_write_to_file(&record, blob_str(&vname));
    blob_reset(&record);
    shell_escape(&cmd, blob_str(&vname));
    blob_appendf(&cmd, " ");
    shell_escape(&cmd, zFile);
    system(blob_str(&cmd));
    unlink(blob_str(&vname));
    blob_reset(&vname);
    blob_reset(&cmd);


  }
  blob_reset(&fname);
}

Changes to src/encode.c.

400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
/*
** Encode a N-digit base-256 in base-16.  Return zero on success
** and non-zero if there is an error.
*/
int encode16(const unsigned char *pIn, unsigned char *zOut, int N){
  int i;
  for(i=0; i<N; i++){
    *(zOut++) = zEncode[pIn[0]>>4];
    *(zOut++) = zEncode[pIn[0]&0xf];
  }
  *zOut = 0;
  return 0;
}

/*
** An array for translating single base-16 characters into a value.







|
|







400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
/*
** Encode a N-digit base-256 in base-16.  Return zero on success
** and non-zero if there is an error.
*/
int encode16(const unsigned char *pIn, unsigned char *zOut, int N){
  int i;
  for(i=0; i<N; i++){
    *(zOut++) = zEncode[pIn[i]>>4];
    *(zOut++) = zEncode[pIn[i]&0xf];
  }
  *zOut = 0;
  return 0;
}

/*
** An array for translating single base-16 characters into a value.

Changes to src/file.c.

103
104
105
106
107
108
109



110

111
112
113
114
115
116
117
...
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
...
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
int file_mkdir(const char *zName, int forceFlag){
  int rc = file_isdir(zName);
  if( rc==2 ){
    if( !forceFlag ) return 1;
    unlink(zName);
  }
  if( rc!=1 ){



    return mkdir(zName, 0755);

  }
  return 0;
}

/*
** Return true if the filename given is a valid filename for
** a file in a repository.  Valid filenames follow all of the
................................................................................
** Compute a canonical pathname for a file or directory.
** Make the name absolute if it is relative.
** Remove redundant / characters
** Remove all /./ path elements.
** Convert /A/../ to just /
*/
void file_canonical_name(const char *zOrigName, Blob *pOut){
  if( zOrigName[0]=='/' ){

    blob_set(pOut, zOrigName);
    blob_materialize(pOut);
  }else{
    char zPwd[2000];
    if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
      fprintf(stderr, "pwd too big: max %d\n", sizeof(zPwd)-20);
      exit(1);
    }
    blob_zero(pOut);
    blob_appendf(pOut, "%s/%s", zPwd, zOrigName);
  }
  blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut)));
}

/*
** COMMAND:  test-canonical-name
**
................................................................................
  blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); 
  zPath = blob_buffer(pOut);
  if( zPath[0]=='/' ){
    int i, j;
    Blob tmp;
    char zPwd[2000];
    if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
      fprintf(stderr, "pwd too big: max %d\n", sizeof(zPwd)-20);
      exit(1);
    }
    for(i=1; zPath[i] && zPwd[i]==zPath[i]; i++){}
    if( zPath[i]==0 ){
      blob_reset(pOut);
      if( zPwd[i]==0 ){
        blob_append(pOut, ".", 1);







>
>
>

>







 







|
>





|



|







 







|







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
int file_mkdir(const char *zName, int forceFlag){
  int rc = file_isdir(zName);
  if( rc==2 ){
    if( !forceFlag ) return 1;
    unlink(zName);
  }
  if( rc!=1 ){
#ifdef __MINGW32__
    return mkdir(zName);
#else
    return mkdir(zName, 0755);
#endif
  }
  return 0;
}

/*
** Return true if the filename given is a valid filename for
** a file in a repository.  Valid filenames follow all of the
................................................................................
** Compute a canonical pathname for a file or directory.
** Make the name absolute if it is relative.
** Remove redundant / characters
** Remove all /./ path elements.
** Convert /A/../ to just /
*/
void file_canonical_name(const char *zOrigName, Blob *pOut){
  if( zOrigName[0]=='/' 
      || (strlen(zOrigName)>3 && zOrigName[1]==':' && zOrigName[2]=='\\') ){
    blob_set(pOut, zOrigName);
    blob_materialize(pOut);
  }else{
    char zPwd[2000];
    if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
      fprintf(stderr, "pwd too big: max %d\n", (int)sizeof(zPwd)-20);
      exit(1);
    }
    blob_zero(pOut);
    blob_appendf(pOut, "%//%/", zPwd, zOrigName);
  }
  blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut)));
}

/*
** COMMAND:  test-canonical-name
**
................................................................................
  blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); 
  zPath = blob_buffer(pOut);
  if( zPath[0]=='/' ){
    int i, j;
    Blob tmp;
    char zPwd[2000];
    if( getcwd(zPwd, sizeof(zPwd)-20)==0 ){
      fprintf(stderr, "pwd too big: max %d\n", (int)sizeof(zPwd)-20);
      exit(1);
    }
    for(i=1; zPath[i] && zPwd[i]==zPath[i]; i++){}
    if( zPath[i]==0 ){
      blob_reset(pOut);
      if( zPwd[i]==0 ){
        blob_append(pOut, ".", 1);

Changes to src/http.c.

21
22
23
24
25
26
27

28


29
30
31
32
33



34
35
36
37
38



39

























40
41
42
43
44
45
46
47
48
49


50

51
52
53
54
55
56
57
..
74
75
76
77
78
79
80



81
82

83
84

















































85
86
87
88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
106
107
108







































109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

140
141
142
143
144
145
146
...
258
259
260
261
262
263
264



265

266
267








268
**
*******************************************************************************
**
** This file contains code that implements the client-side HTTP protocol
*/
#include "config.h"
#include "http.h"

#include <assert.h>


#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>



#include <signal.h>

/*
** Persistent information about the HTTP connection.
*/



static FILE *pSocket = 0;   /* The socket on which we talk to the server */


























/*
** Open a socket connection to the server.  Return 0 on success and
** non-zero if an error occurs.
*/
static int http_open_socket(void){
  static struct sockaddr_in addr;  /* The server address */
  static int addrIsInit = 0;       /* True once addr is initialized */
  int s;



  if( !addrIsInit ){

    addr.sin_family = AF_INET;
    addr.sin_port = htons(g.urlPort);
    *(int*)&addr.sin_addr = inet_addr(g.urlName);
    if( -1 == *(int*)&addr.sin_addr ){
#ifndef FOSSIL_STATIC_LINK
      struct hostent *pHost;
      pHost = gethostbyname(g.urlName);
................................................................................
  s = socket(AF_INET,SOCK_STREAM,0);
  if( s<0 ){
    fossil_panic("cannot create a socket");
  }
  if( connect(s,(struct sockaddr*)&addr,sizeof(addr))<0 ){
    fossil_panic("cannot connect to host %s:%d", g.urlName, g.urlPort);
  }



  pSocket = fdopen(s,"r+");
  signal(SIGPIPE, SIG_IGN);

  return 0;
}


















































/*
** Make a single attempt to talk to the server.  Return TRUE on success
** and FALSE on a failure.
**
** pHeader contains the HTTP header.  pPayload contains the content.
** The content of the reply is written into pReply.  pReply is assumed
** to be uninitialized prior to this call.
**
** If an error occurs, this routine return false, resets pReply and
** closes the persistent connection, if any.
*/
static int http_send_recv(Blob *pHeader, Blob *pPayload, Blob *pReply){

  int rc;
  int closeConnection;
  int iLength;
  int iHttpVersion;
  int i;
  int nRead;
  char zLine[2000];

  if( pSocket==0 && http_open_socket() ){
    return 0;
  }







































  rc = fwrite(blob_buffer(pHeader), 1, blob_size(pHeader), pSocket);
  if( rc!=blob_size(pHeader) ) goto write_err;
  rc = fwrite(blob_buffer(pPayload), 1, blob_size(pPayload), pSocket);
  if( rc!=blob_size(pPayload) ) goto write_err;
  if( fflush(pSocket) ) goto write_err;
  if( fgets(zLine, sizeof(zLine), pSocket)==0 ) goto write_err;
  if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
  if( rc!=200 ) goto write_err;
  if( iHttpVersion==0 ){
    closeConnection = 1;
  }else{
    closeConnection = 0;
  }
  iLength = -1;
  while( fgets(zLine, sizeof(zLine), pSocket) ){
    for(i=0; zLine[i] && zLine[i]!='\n' && zLine[i]!='\r'; i++){}
    if( i==0 ) break;
    zLine[i] = 0;
    if( strncasecmp(zLine, "content-length:",15)==0 ){
      iLength = atoi(&zLine[16]);
    }else if( strncasecmp(zLine, "connection:", 11)==0 ){
      for(i=12; isspace(zLine[i]); i++){}
      if( zLine[i]=='c' || zLine[i]=='C' ){
        closeConnection = 1;   /* Connection: close */
      }else if( zLine[i]=='k' || zLine[i]=='K' ){
        closeConnection = 0;   /* Connection: keep-alive */
      }
    }
  }
  if( iLength<0 ) goto write_err;
  nRead = blob_read_from_channel(pReply, pSocket, iLength);

  if( nRead!=iLength ){
    blob_reset(pReply);
    goto write_err;
  }
  if( closeConnection ){
    http_close();
  }
................................................................................


/*
** Make sure the socket to the HTTP server is closed 
*/
void http_close(void){
  if( pSocket ){



    fclose(pSocket);

    pSocket = 0;
  }








}







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





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










>
>

>







 







>
>
>


>


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













>

<









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









|

|

<




|












>







 







>
>
>

>


>
>
>
>
>
>
>
>

21
22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
**
*******************************************************************************
**
** This file contains code that implements the client-side HTTP protocol
*/
#include "config.h"
#include "http.h"
#ifdef __MINGW32__
#  include <windows.h>
#  include <winsock2.h>
#else
#  include <arpa/inet.h>

#  include <sys/socket.h>
#  include <netdb.h>
#  include <netinet/in.h>
#endif
#include <assert.h>
#include <sys/types.h>
#include <signal.h>

/*
** Persistent information about the HTTP connection.
*/

#ifdef __MINGW32__
static WSADATA ws_info;
static int pSocket = 0;     /* The socket on which we talk to the server on */
#else
static FILE *pSocket = 0;   /* The socket filehandle on which we talk to the server */
#endif

/*
** Winsock must be initialize before use.  This helper method allows us to
** always call ws_init in our code regardless of platform but only actually
** initialize winsock on the windows platform.
*/
static void ws_init(){
#ifdef __MINGW32__
  if (WSAStartup(MAKEWORD(2,0), &ws_info) != 0){
    fossil_panic("can't initialize winsock");
  }
#endif
}

/*
** Like ws_init, winsock must also be cleaned up after.
*/
static void ws_cleanup(){
#ifdef __MINGW32__
  WSACleanup();
#endif
}

/*
** Open a socket connection to the server.  Return 0 on success and
** non-zero if an error occurs.
*/
static int http_open_socket(void){
  static struct sockaddr_in addr;  /* The server address */
  static int addrIsInit = 0;       /* True once addr is initialized */
  int s;

  ws_init();
  
  if( !addrIsInit ){

    addr.sin_family = AF_INET;
    addr.sin_port = htons(g.urlPort);
    *(int*)&addr.sin_addr = inet_addr(g.urlName);
    if( -1 == *(int*)&addr.sin_addr ){
#ifndef FOSSIL_STATIC_LINK
      struct hostent *pHost;
      pHost = gethostbyname(g.urlName);
................................................................................
  s = socket(AF_INET,SOCK_STREAM,0);
  if( s<0 ){
    fossil_panic("cannot create a socket");
  }
  if( connect(s,(struct sockaddr*)&addr,sizeof(addr))<0 ){
    fossil_panic("cannot connect to host %s:%d", g.urlName, g.urlPort);
  }
#ifdef __MINGW32__
  pSocket = s;
#else
  pSocket = fdopen(s,"r+");
  signal(SIGPIPE, SIG_IGN);
#endif
  return 0;
}

#ifdef __MINGW32__
/*
** Read the socket until a newline '\n' is found.  Return the number
** of characters read. pSockId contains the socket handel.  pOut
** contains a pointer to the buffer to write to.  pOutSize contains
** the maximum size of the line that pOut can handle.
*/
static int socket_recv_line(int pSockId, char* pOut, int pOutSize){
  int received=0;
  char letter;
  memset(pOut,0,pOutSize);
  for(; received<pOutSize-1;received++){
    if( recv(pSockId,(char*)&letter,1,0)>0 ){
      pOut[received]=letter;
      if( letter=='\n' ){
        break;
      }
    }else{
      break;
    }
  }
  return received;
}

/*
** Initialize a blob to the data on an input socket.  return
** the number of bytes read into the blob.  Any prior content
** of the blob is discarded, not freed.
**
** The function was placed here in http.c due to it's socket
** nature and we did not want to introduce socket headers into
** the socket netural blob.c file.
*/
int socket_read_blob(Blob *pBlob, int pSockId, int nToRead){
  int i=0,read=0;
  char rbuf[50];
  blob_zero(pBlob);
  while ( i<nToRead ){
    read = recv(pSockId, rbuf, 50, 0);
    i += read;
    if( read<0 ){
      return 0;
    }
    blob_append(pBlob, rbuf, read);
  }
  return blob_size(pBlob);
}
#endif

/*
** Make a single attempt to talk to the server.  Return TRUE on success
** and FALSE on a failure.
**
** pHeader contains the HTTP header.  pPayload contains the content.
** The content of the reply is written into pReply.  pReply is assumed
** to be uninitialized prior to this call.
**
** If an error occurs, this routine return false, resets pReply and
** closes the persistent connection, if any.
*/
static int http_send_recv(Blob *pHeader, Blob *pPayload, Blob *pReply){
  int closeConnection=1;   /* default to closing the connection */
  int rc;

  int iLength;
  int iHttpVersion;
  int i;
  int nRead;
  char zLine[2000];

  if( pSocket==0 && http_open_socket() ){
    return 0;
  }
  iLength = -1;
#ifdef __MINGW32__
  /*
  ** Use recv/send on the windows platform as winsock does not allow
  ** sockets to be used as FILE handles, thus fdopen, fwrite, fgets
  ** does not function on windows for sockets.
  */
  rc = send(pSocket, blob_buffer(pHeader), blob_size(pHeader), 0);
  if( rc!=blob_size(pHeader) ) goto write_err;
  rc = send(pSocket, blob_buffer(pPayload), blob_size(pPayload), 0);
  if( rc!=blob_size(pPayload) ) goto write_err;
  
  /* Read the response */
  while( socket_recv_line(pSocket, zLine, 2000) ){
    for( i=0; zLine[i] && zLine[i]!='\n' && zLine[i]!='\r'; i++ ){}
    if( i==0 ) break;
    zLine[i] = 0;
    if( strncasecmp(zLine, "http/1.", 7)==0 ){
      if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
      if( rc!=200 ) goto write_err;
      if( iHttpVersion==0 ){
        closeConnection = 1;
      }else{
        closeConnection = 0;
      }
    } else if( strncasecmp(zLine, "content-length:", 15)==0 ){
      iLength = atoi(&zLine[16]);
    }else if( strncasecmp(zLine, "connection:", 11)==0 ){
      for(i=12; isspace(zLine[i]); i++){}
      if( zLine[i]=='c' || zLine[i]=='C' ){
        closeConnection = 1;
      }else if( zLine[i]=='k' || zLine[i]=='K' ){
        closeConnection = 0;
      }
    }
  }
  if( iLength<0 ) goto write_err;
  nRead = socket_read_blob(pReply, pSocket, iLength);
#else
  rc = fwrite(blob_buffer(pHeader), 1, blob_size(pHeader), pSocket);
  if( rc!=blob_size(pHeader) ) goto write_err;
  rc = fwrite(blob_buffer(pPayload), 1, blob_size(pPayload), pSocket);
  if( rc!=blob_size(pPayload) ) goto write_err;
  if( fflush(pSocket) ) goto write_err;
  if( fgets(zLine, sizeof(zLine), pSocket)==0 ) goto write_err;
  if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
  if( rc!=200 ) goto write_err;
  if( iHttpVersion==0 ){
    closeConnection = 1;   /* Connection: close */
  }else{
    closeConnection = 0;   /* Connection: keep-alive */
  }

  while( fgets(zLine, sizeof(zLine), pSocket) ){
    for(i=0; zLine[i] && zLine[i]!='\n' && zLine[i]!='\r'; i++){}
    if( i==0 ) break;
    zLine[i] = 0;
    if( strncasecmp(zLine,"content-length:",15)==0 ){
      iLength = atoi(&zLine[16]);
    }else if( strncasecmp(zLine, "connection:", 11)==0 ){
      for(i=12; isspace(zLine[i]); i++){}
      if( zLine[i]=='c' || zLine[i]=='C' ){
        closeConnection = 1;   /* Connection: close */
      }else if( zLine[i]=='k' || zLine[i]=='K' ){
        closeConnection = 0;   /* Connection: keep-alive */
      }
    }
  }
  if( iLength<0 ) goto write_err;
  nRead = blob_read_from_channel(pReply, pSocket, iLength);
#endif
  if( nRead!=iLength ){
    blob_reset(pReply);
    goto write_err;
  }
  if( closeConnection ){
    http_close();
  }
................................................................................


/*
** Make sure the socket to the HTTP server is closed 
*/
void http_close(void){
  if( pSocket ){
#ifdef __MINGW32__
    closesocket(pSocket);
#else
    fclose(pSocket);
#endif
    pSocket = 0;
  }
  /*
  ** This is counter productive. Each time we open a connection we initialize
  ** winsock and then when closing we cleanup. It would be better to
  ** initialize winsock once at application start when we know we are going to
  ** use the socket interface and then cleanup once at application exit when
  ** we are all done with all socket operations.
  */
  ws_cleanup();
}

Changes to src/info.c.

112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

154
155
156
157
158
159
160
...
162
163
164
165
166
167
168
169

170
171
172
173
174
175
176
...
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
...
204
205
206
207
208
209
210
211

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231


















































232
233
234
235
236
237
238
239

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
...
261
262
263
264
265
266
267



268
269



270



271
272




273
274
275
276
277
278

279



280
281
282


283
284
285
286
287
288
289
...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313




























































314

315
316
317
318
319
320

321
322
323
324
325
326

327
328


329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
359
360
361
362
363
364
365

366

367
368
369
370
371
372
373
...
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
...
445
446
447
448
449
450
451

452
453
454



455
456
457
458
459
460
461
...
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
























478
479
480
481
482
483
484
485
486
487
488
489

490
491
492
493
494

495
496


497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
...
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551











552
553
554
555
556
557
558
559
560
561
562
563
564





















































































** to a depth of N.  Return true if descendents are shown and false if not.
*/
static int showDescendents(int pid, int depth, const char *zTitle){
  Stmt q;
  int cnt = 0;
  db_prepare(&q,
    "SELECT plink.cid, blob.uuid, datetime(plink.mtime, 'localtime'),"
    "       event.user, event.comment"

    "  FROM plink, blob, event"
    " WHERE plink.pid=%d"
    "   AND blob.rid=plink.cid"
    "   AND event.objid=plink.cid"
    " ORDER BY plink.mtime ASC",
    pid
  );
................................................................................
    const char *zUuid = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zUser = db_column_text(&q, 3);
    const char *zCom = db_column_text(&q, 4);
    cnt++;
    if( cnt==1 ){
      if( zTitle ){
        @ <h2>%s(zTitle)</h2>
      }
      @ <ul>
    }
    @ <li>
    hyperlink_to_uuid(zUuid);
    @ %s(zCom) (by %s(zUser) on %s(zDate))
    if( depth ){
      n = showDescendents(cid, depth-1, 0);
    }else{
      n = db_int(0, "SELECT 1 FROM plink WHERE pid=%d", cid);
    }
    if( n==0 ){
      db_multi_exec("DELETE FROM leaves WHERE rid=%d", cid);
      @ <b>leaf</b>
    }
  }

  if( cnt ){
    @ </ul>
  }
  return cnt;
}

/*
................................................................................
** to a depth of N.  Return true if ancestors are shown and false if not.
*/
static void showAncestors(int pid, int depth, const char *zTitle){
  Stmt q;
  int cnt = 0;
  db_prepare(&q,
    "SELECT plink.pid, blob.uuid, datetime(event.mtime, 'localtime'),"
    "       event.user, event.comment"

    "  FROM plink, blob, event"
    " WHERE plink.cid=%d"
    "   AND blob.rid=plink.pid"
    "   AND event.objid=plink.pid"
    " ORDER BY event.mtime DESC",
    pid
  );
................................................................................
    const char *zUuid = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zUser = db_column_text(&q, 3);
    const char *zCom = db_column_text(&q, 4);
    cnt++;
    if( cnt==1 ){
      if( zTitle ){
        @ <h2>%s(zTitle)</h2>
      }
      @ <ul>
    }
    @ <li>
    hyperlink_to_uuid(zUuid);
    @ %s(zCom) (by %s(zUser) on %s(zDate))
    if( depth ){
      showAncestors(cid, depth-1, 0);
    }
  }

  if( cnt ){
    @ </ul>
  }
}


/*
................................................................................
** Show information about versions mentioned in the "leaves" table.
*/
static void showLeaves(void){
  Stmt q;
  int cnt = 0;
  db_prepare(&q,
    "SELECT blob.uuid, datetime(event.mtime, 'localtime'),"
    "       event.user, event.comment"

    "  FROM leaves, plink, blob, event"
    " WHERE plink.cid=leaves.rid"
    "   AND blob.rid=leaves.rid"
    "   AND event.objid=leaves.rid"
    " ORDER BY event.mtime DESC"
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    const char *zDate = db_column_text(&q, 1);
    const char *zUser = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);
    cnt++;
    if( cnt==1 ){
      @ <h2>Leaves</h2>
      @ <ul>
    }
    @ <li>
    hyperlink_to_uuid(zUuid);
    @ %s(zCom) (by %s(zUser) on %s(zDate))
  }


















































  if( cnt ){
    @ </ul>
  }
}


/*
** WEBPAGE: vinfo

**
** Return information about a version.  The version number is contained
** in g.zExtra.
*/
void vinfo_page(void){
  Stmt q;
  int rid;
  int isLeaf;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("Version Information");
  rid = name_to_rid(g.zExtra);
  if( rid==0 ){
    @ No such object: %h(g.argv[2])
    style_footer();
    return;
  }
  isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
  db_prepare(&q, 
     "SELECT uuid, datetime(mtime, 'localtime'), user, comment"
................................................................................
     "  FROM blob, event"
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     rid, rid
  );
  if( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);



    @ <h2>Version %s(zUuid)</h2>
    @ <ul>



    @ <li><b>Date:</b> %s(db_column_text(&q, 1))</li>



    @ <li><b>User:</b> %s(db_column_text(&q, 2))</li>
    @ <li><b>Comment:</b> %s(db_column_text(&q, 3))</li>




    @ <li><a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a></li>
    @ <li><a href="%s(g.zBaseURL)/zip/%s(zUuid).zip">ZIP archive</a></li>
    @ <li><a href="%s(g.zBaseURL)/fview/%d(rid)">manifest</a></li>
    if( g.okSetup ){
      @ <li><b>Record ID:</b> %d(rid)</li>
    }

    @ </ul>



  }
  db_finalize(&q);
  @ <p><h2>Changes:</h2>


  @ <ul>
  db_prepare(&q, 
     "SELECT name, pid, fid"
     "  FROM mlink, filename"
     " WHERE mid=%d"
     "   AND filename.fnid=mlink.fnid",
     rid
................................................................................
    if( pid && fid ){
      @ <b>Modified:</b>
    }else if( fid ){
      @ <b>Added:</b>
    }else{
      @ <b>Deleted:</b>
    }
    @ <a href="%s(g.zBaseURL)/finfo/%T(zName)">%h(zName)</a></li>
  }
  @ </ul>
  compute_leaves(rid);
  showDescendents(rid, 2, "Descendents");
  showLeaves();
  showAncestors(rid, 2, "Ancestors");
  style_footer();
}

/*




























































** WEBPAGE: finfo

**
** Show the complete change history for a single file.  The name
** of the file is in g.zExtra
*/
void finfo_page(void){
  Stmt q;

  char zPrevDate[20];
  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("File History");

  zPrevDate[0] = 0;

  db_prepare(&q,
    "SELECT a.uuid, substr(b.uuid,1,10), datetime(event.mtime,'localtime'),"


    "       event.comment, event.user, mlink.pid, mlink.fid"
    "  FROM mlink, blob a, blob b, event"
    " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
    "   AND a.rid=mlink.mid"
    "   AND b.rid=mlink.fid"
    "   AND event.objid=mlink.mid"
    " ORDER BY event.mtime DESC",
    g.zExtra
  );
  @ <h2>History of %h(g.zExtra)</h2>
  @ <table cellspacing=0 border=0 cellpadding=0>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zVers = db_column_text(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);
    const char *zUser = db_column_text(&q, 4);
................................................................................
    @ <tr><td valign="top">%s(&zDate[11])</td>
    @ <td width="20"></td>
    @ <td valign="top" align="left">
    hyperlink_to_uuid(zVers);
    @ %h(zCom) (By: %h(zUser))
    @ Id: %s(zUuid)/%d(frid)
    @ <a href="%s(g.zBaseURL)/fview/%d(frid)">[view]</a>

    @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&amp;v2=%d(frid)">[diff]</a>

    @ </td>
  }
  db_finalize(&q);
  @ </table>
  style_footer();
}

................................................................................
** Append the difference between two RIDs to the output
*/
static void append_diff(int fromid, int toid){
  Blob from, to, out;
  content_get(fromid, &from);
  content_get(toid, &to);
  blob_zero(&out);
  unified_diff(&from, &to, 5, &out);
  @ %h(blob_str(&out))
  blob_reset(&from);
  blob_reset(&to);
  blob_reset(&out);  
}

/*
** WEBPAGE: vdiff

**
** Show all differences for a particular check-in specified by g.zExtra
*/
void vdiff_page(void){
  int rid;
  Stmt q;
  char *zUuid;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("Version Diff");

  rid = name_to_rid(g.zExtra);
  if( rid==0 ){
    cgi_redirect("index");
  }
  db_prepare(&q,
     "SELECT pid, fid, name"
     "  FROM mlink, filename"
     " WHERE mlink.mid=%d"
................................................................................
  @ <h2>All Changes In Version
  hyperlink_to_uuid(zUuid);
  @ </h2>
  while( db_step(&q)==SQLITE_ROW ){
    int pid = db_column_int(&q,0);
    int fid = db_column_int(&q,1);
    const char *zName = db_column_text(&q,2);
    @ <p><a href="%s(g.zBaseURL)/finfo/%T(zName)">%h(zName)</a></p>
    @ <blockquote><pre>
    append_diff(pid, fid);
    @ </pre></blockquote>
  }
  db_finalize(&q);
  style_footer();
}
................................................................................
**     * It's uuid
**     * date of check-in
**     * Comment & user
*/
static void object_description(int rid, int linkToView){
  Stmt q;
  int cnt = 0;

  db_prepare(&q,
    "SELECT filename.name, datetime(event.mtime), substr(a.uuid,1,10),"
    "       event.comment, event.user, b.uuid"



    "  FROM mlink, filename, event, blob a, blob b"
    " WHERE filename.fnid=mlink.fnid"
    "   AND event.objid=mlink.mid"
    "   AND a.rid=mlink.fid"
    "   AND b.rid=mlink.mid"
    "   AND mlink.fid=%d",
    rid
................................................................................
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    const char *zDate = db_column_text(&q, 1);
    const char *zFuuid = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);
    const char *zUser = db_column_text(&q, 4);
    const char *zVers = db_column_text(&q, 5);
    @ File <a href="%s(g.zBaseURL)/finfo/%T(zName)">%h(zName)</a>
    @ uuid %s(zFuuid) part of check-in
    hyperlink_to_uuid(zVers);
    @ %s(zCom) by %s(zUser) on %s(zDate).
    cnt++;
  }
  db_finalize(&q);
  db_prepare(&q,
























    "SELECT datetime(mtime), user, comment, uuid"
    "  FROM event, blob"
    " WHERE event.objid=%d"
    "   AND blob.rid=%d",
    rid, rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zUuid = db_column_text(&q, 3);
    const char *zCom = db_column_text(&q, 2);
    const char *zUser = db_column_text(&q, 1);
    @ Version

    hyperlink_to_uuid(zUuid);
    @ %s(zCom) by %s(zUser) on %s(zDate).
    cnt++;
  }
  db_finalize(&q);

  if( cnt==0 ){
    @ Empty file


  }else if( linkToView ){
    @ <a href="%s(g.zBaseURL)/fview/%d(rid)">[view]</a>
  }
}

/*
** WEBPAGE: fdiff
**
** Two arguments, v1 and v2, are integers.  Show the difference between
** the two records.
*/
void diff_page(void){
  int v1 = atoi(PD("v1","0"));
  int v2 = atoi(PD("v2","0"));
  Blob c1, c2, diff;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("Diff");
  @ <h2>Differences From:</h2>
  @ <blockquote>
................................................................................
  object_description(v2, 1);
  @ </blockquote>
  @ <hr>
  @ <blockquote><pre>
  content_get(v1, &c1);
  content_get(v2, &c2);
  blob_zero(&diff);
  unified_diff(&c1, &c2, 4, &diff);
  blob_reset(&c1);
  blob_reset(&c2);
  @ %h(blob_str(&diff))
  @ </pre></blockquote>
  blob_reset(&diff);
  style_footer();
}

/*
** WEBPAGE: fview
** URL: /fview/UUID
** 
** Show the complete content of a file identified by UUID
** as preformatted text.
*/
void fview_page(void){
  int rid;
  Blob content;

  rid = name_to_rid(g.zExtra);
  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }











  style_header("File Content");
  @ <h2>Content Of:</h2>
  @ <blockquote>
  object_description(rid, 0);
  @ </blockquote>
  @ <hr>
  @ <blockquote><pre>
  content_get(rid, &content);
  @ %h(blob_str(&content))
  @ </pre></blockquote>
  blob_reset(&content);
  style_footer();
}




























































































|
>







 







|





|










>







 







|
>







 







|





|




>







 







|
>
|
<
|










|




|

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








>

|
<








|
|
|







 







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


<
>
>







 







|










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

>

|
<



>






>


>
>
|






|

|







 







>
|
>







 







|








>

|










|







 







|







 







>


<
>
>
>







 







|


|



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

<
>
>












|
|







 







|










|








|


>
>
>
>
>
>
>
>
>
>
>













>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
208
209
210
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
...
315
316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333

334
335
336
337
338
339
340



341
342
343
344
345
346
347

348
349
350
351
352
353
354
355
356
...
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444

445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
...
579
580
581
582
583
584
585
586
587
588

589
590
591
592
593
594
595
596
597
598
...
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649

650
651
652
653
654
655
656
657

658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
...
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
** to a depth of N.  Return true if descendents are shown and false if not.
*/
static int showDescendents(int pid, int depth, const char *zTitle){
  Stmt q;
  int cnt = 0;
  db_prepare(&q,
    "SELECT plink.cid, blob.uuid, datetime(plink.mtime, 'localtime'),"
    "       coalesce(event.euser,event.user),"
    "       coalesce(event.comment,event.ecomment)"
    "  FROM plink, blob, event"
    " WHERE plink.pid=%d"
    "   AND blob.rid=plink.cid"
    "   AND event.objid=plink.cid"
    " ORDER BY plink.mtime ASC",
    pid
  );
................................................................................
    const char *zUuid = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zUser = db_column_text(&q, 3);
    const char *zCom = db_column_text(&q, 4);
    cnt++;
    if( cnt==1 ){
      if( zTitle ){
        @ <div class="section">%s(zTitle)</div>
      }
      @ <ul>
    }
    @ <li>
    hyperlink_to_uuid(zUuid);
    @ %w(zCom) (by %s(zUser) on %s(zDate))
    if( depth ){
      n = showDescendents(cid, depth-1, 0);
    }else{
      n = db_int(0, "SELECT 1 FROM plink WHERE pid=%d", cid);
    }
    if( n==0 ){
      db_multi_exec("DELETE FROM leaves WHERE rid=%d", cid);
      @ <b>leaf</b>
    }
  }
  db_finalize(&q);
  if( cnt ){
    @ </ul>
  }
  return cnt;
}

/*
................................................................................
** to a depth of N.  Return true if ancestors are shown and false if not.
*/
static void showAncestors(int pid, int depth, const char *zTitle){
  Stmt q;
  int cnt = 0;
  db_prepare(&q,
    "SELECT plink.pid, blob.uuid, datetime(event.mtime, 'localtime'),"
    "       coalesce(event.euser,event.user),"
    "       coalesce(event.comment,event.ecomment)"
    "  FROM plink, blob, event"
    " WHERE plink.cid=%d"
    "   AND blob.rid=plink.pid"
    "   AND event.objid=plink.pid"
    " ORDER BY event.mtime DESC",
    pid
  );
................................................................................
    const char *zUuid = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zUser = db_column_text(&q, 3);
    const char *zCom = db_column_text(&q, 4);
    cnt++;
    if( cnt==1 ){
      if( zTitle ){
        @ <div class="section">%s(zTitle)</div>
      }
      @ <ul>
    }
    @ <li>
    hyperlink_to_uuid(zUuid);
    @ %w(zCom) (by %s(zUser) on %s(zDate))
    if( depth ){
      showAncestors(cid, depth-1, 0);
    }
  }
  db_finalize(&q);
  if( cnt ){
    @ </ul>
  }
}


/*
................................................................................
** Show information about versions mentioned in the "leaves" table.
*/
static void showLeaves(void){
  Stmt q;
  int cnt = 0;
  db_prepare(&q,
    "SELECT blob.uuid, datetime(event.mtime, 'localtime'),"
    "       coalesce(event.euser, event.user),"
    "       coalesce(event.ecomment,event.comment)"
    "  FROM leaves, blob, event"

    " WHERE blob.rid=leaves.rid"
    "   AND event.objid=leaves.rid"
    " ORDER BY event.mtime DESC"
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    const char *zDate = db_column_text(&q, 1);
    const char *zUser = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);
    cnt++;
    if( cnt==1 ){
      @ <div class="section">Leaves</div>
      @ <ul>
    }
    @ <li>
    hyperlink_to_uuid(zUuid);
    @ %w(zCom) (by %s(zUser) on %s(zDate))
  }
  db_finalize(&q);
  if( cnt ){
    @ </ul>
  }
}

/*
** Show information about all tags on a given node.
*/
static void showTags(int rid){
  Stmt q;
  int cnt = 0;
  db_prepare(&q,
    "SELECT tag.tagid, tagname, srcid, blob.uuid, value,"
    "       datetime(tagxref.mtime,'localtime'), tagtype"
    "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
    "       LEFT JOIN blob ON blob.rid=tagxref.srcid"
    " WHERE tagxref.rid=%d"
    " ORDER BY tagname", rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zTagname = db_column_text(&q, 1);
    int srcid = db_column_int(&q, 2);
    const char *zUuid = db_column_text(&q, 3);
    const char *zValue = db_column_text(&q, 4);
    const char *zDate = db_column_text(&q, 5);
    int tagtype = db_column_int(&q, 6);
    cnt++;
    if( cnt==1 ){
      @ <div class="section">Tags And Properties</div>
      @ <ul>
    }
    @ <li>
    @ <b>%h(zTagname)</b>
    if( zValue ){
      @ = %h(zValue)<i>
    }else if( tagtype==0 ){
      @ <i>Cancelled
    }else{
      @ <i>
    }
    if( srcid==0 ){
      @ Inherited
    }else if( zUuid ){
      @ From
      hyperlink_to_uuid(zUuid);
    }
    @ on %s(zDate)</i>
  }
  db_finalize(&q);
  if( cnt ){
    @ </ul>
  }
}


/*
** WEBPAGE: vinfo
** URL:  /vinfo?name=RID|UUID
**
** Return information about a version.

*/
void vinfo_page(void){
  Stmt q;
  int rid;
  int isLeaf;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  rid = name_to_rid(PD("name","0"));
  if( rid==0 ){
    style_header("Version Information Error");
    @ No such object: %h(g.argv[2])
    style_footer();
    return;
  }
  isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
  db_prepare(&q, 
     "SELECT uuid, datetime(mtime, 'localtime'), user, comment"
................................................................................
     "  FROM blob, event"
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     rid, rid
  );
  if( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    char *zTitle = mprintf("Version: [%.10s]", zUuid);
    style_header(zTitle);
    free(zTitle);
    /*@ <h2>Version %s(zUuid)</h2>*/

    @ <div class="section">Overview</div>
    @ <p><table class="label-value">
    @ <tr><th>Version:</th><td>%s(zUuid)</td></tr>
    @ <tr><th>Date:</th><td>%s(db_column_text(&q, 1))</td></tr>
    if( g.okSetup ){
      @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
    }
    @ <tr><th>Original&nbsp;User:</th><td>%h(db_column_text(&q, 2))</td></tr>

    @ <tr><th>Original&nbsp;Comment:</th><td>%w(db_column_text(&q,3))</td></tr>
    @ </td></tr>
    @ <tr><th>Commands:</th>
    @   <td>
    @     <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a>
    @     | <a href="%s(g.zBaseURL)/zip/%s(zUuid).zip">ZIP archive</a>
    @     | <a href="%s(g.zBaseURL)/fview/%d(rid)">manifest</a>



    @   </td>
    @ </tr>
    @ </table></p>
  }else{
    style_header("Version Information");
  }
  db_finalize(&q);

  showTags(rid);
  @ <div class="section">Changes</div>
  @ <ul>
  db_prepare(&q, 
     "SELECT name, pid, fid"
     "  FROM mlink, filename"
     " WHERE mid=%d"
     "   AND filename.fnid=mlink.fnid",
     rid
................................................................................
    if( pid && fid ){
      @ <b>Modified:</b>
    }else if( fid ){
      @ <b>Added:</b>
    }else{
      @ <b>Deleted:</b>
    }
    @ <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a></li>
  }
  @ </ul>
  compute_leaves(rid);
  showDescendents(rid, 2, "Descendents");
  showLeaves();
  showAncestors(rid, 2, "Ancestors");
  style_footer();
}

/*
** WEBPAGE: winfo
** URL:  /winfo?name=RID
**
** Return information about a wiki page.
*/
void winfo_page(void){
  Stmt q;
  int rid;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  rid = name_to_rid(PD("name","0"));
  if( rid==0 ){
    style_header("Wiki Page Information Error");
    @ No such object: %h(g.argv[2])
    style_footer();
    return;
  }
  db_prepare(&q, 
     "SELECT substr(tagname, 6, 1000), uuid,"
     "       datetime(event.mtime, 'localtime'), user"
     "  FROM tagxref, tag, blob, event"
     " WHERE tagxref.rid=%d"
     "   AND tag.tagid=tagxref.tagid"
     "   AND tag.tagname LIKE 'wiki-%%'"
     "   AND blob.rid=%d"
     "   AND event.objid=%d",
     rid, rid, rid
  );
  if( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    char *zTitle = mprintf("Wiki Page %s", zName);
    style_header(zTitle);
    free(zTitle);
    @ <div class="section">Overview</div>
    @ <p><table class="label-value">
    @ <tr><th>Version:</th><td>%s(zUuid)</td></tr>
    @ <tr><th>Date:</th><td>%s(db_column_text(&q, 2))</td></tr>
    if( g.okSetup ){
      @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
    }
    @ <tr><th>Original&nbsp;User:</th><td>%s(db_column_text(&q, 3))</td></tr>
    @ <tr><th>Commands:</th>
    @   <td>
/*    @     <a href="%s(g.zBaseURL)/wdiff/%d(rid)">diff</a> | */
    @     <a href="%s(g.zBaseURL)/whistory?page=%t(zName)">history</a>
    @     | <a href="%s(g.zBaseURL)/fview/%d(rid)">raw-text</a>
    @   </td>
    @ </tr>
    @ </table></p>
  }else{
    style_header("Wiki Information");
  }
  db_finalize(&q);
  showTags(rid);
  style_footer();
}

/*
** WEBPAGE: finfo
** URL: /finfo?name=FILENAME
**
** Show the complete change history for a single file. 

*/
void finfo_page(void){
  Stmt q;
  const char *zFilename;
  char zPrevDate[20];
  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("File History");

  zPrevDate[0] = 0;
  zFilename = PD("name","");
  db_prepare(&q,
    "SELECT a.uuid, substr(b.uuid,1,10), datetime(event.mtime,'localtime'),"
    "       coalesce(event.ecomment, event.comment),"
    "       coalesce(event.euser, event.user),"
    "       mlink.pid, mlink.fid"
    "  FROM mlink, blob a, blob b, event"
    " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
    "   AND a.rid=mlink.mid"
    "   AND b.rid=mlink.fid"
    "   AND event.objid=mlink.mid"
    " ORDER BY event.mtime DESC",
    zFilename
  );
  @ <h2>History of %h(zFilename)</h2>
  @ <table cellspacing=0 border=0 cellpadding=0>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zVers = db_column_text(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);
    const char *zUser = db_column_text(&q, 4);
................................................................................
    @ <tr><td valign="top">%s(&zDate[11])</td>
    @ <td width="20"></td>
    @ <td valign="top" align="left">
    hyperlink_to_uuid(zVers);
    @ %h(zCom) (By: %h(zUser))
    @ Id: %s(zUuid)/%d(frid)
    @ <a href="%s(g.zBaseURL)/fview/%d(frid)">[view]</a>
    if( fpid ){
      @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&amp;v2=%d(frid)">[diff]</a>
    }
    @ </td>
  }
  db_finalize(&q);
  @ </table>
  style_footer();
}

................................................................................
** Append the difference between two RIDs to the output
*/
static void append_diff(int fromid, int toid){
  Blob from, to, out;
  content_get(fromid, &from);
  content_get(toid, &to);
  blob_zero(&out);
  text_diff(&from, &to, &out, 5);
  @ %h(blob_str(&out))
  blob_reset(&from);
  blob_reset(&to);
  blob_reset(&out);  
}

/*
** WEBPAGE: vdiff
** URL: /vdiff?name=RID
**
** Show all differences for a particular check-in.
*/
void vdiff_page(void){
  int rid;
  Stmt q;
  char *zUuid;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("Version Diff");

  rid = name_to_rid(PD("name",""));
  if( rid==0 ){
    cgi_redirect("index");
  }
  db_prepare(&q,
     "SELECT pid, fid, name"
     "  FROM mlink, filename"
     " WHERE mlink.mid=%d"
................................................................................
  @ <h2>All Changes In Version
  hyperlink_to_uuid(zUuid);
  @ </h2>
  while( db_step(&q)==SQLITE_ROW ){
    int pid = db_column_int(&q,0);
    int fid = db_column_int(&q,1);
    const char *zName = db_column_text(&q,2);
    @ <p><a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a></p>
    @ <blockquote><pre>
    append_diff(pid, fid);
    @ </pre></blockquote>
  }
  db_finalize(&q);
  style_footer();
}
................................................................................
**     * It's uuid
**     * date of check-in
**     * Comment & user
*/
static void object_description(int rid, int linkToView){
  Stmt q;
  int cnt = 0;
  int nWiki = 0;
  db_prepare(&q,
    "SELECT filename.name, datetime(event.mtime), substr(a.uuid,1,10),"

    "       coalesce(event.comment,event.ecomment),"
    "       coalesce(event.euser,event.user),"
    "       b.uuid"
    "  FROM mlink, filename, event, blob a, blob b"
    " WHERE filename.fnid=mlink.fnid"
    "   AND event.objid=mlink.mid"
    "   AND a.rid=mlink.fid"
    "   AND b.rid=mlink.mid"
    "   AND mlink.fid=%d",
    rid
................................................................................
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    const char *zDate = db_column_text(&q, 1);
    const char *zFuuid = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);
    const char *zUser = db_column_text(&q, 4);
    const char *zVers = db_column_text(&q, 5);
    @ File <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a>
    @ uuid %s(zFuuid) part of check-in
    hyperlink_to_uuid(zVers);
    @ %w(zCom) by %h(zUser) on %s(zDate)
    cnt++;
  }
  db_finalize(&q);
  db_prepare(&q, 
    "SELECT substr(tagname, 6, 10000), datetime(event.mtime),"
    "       coalesce(event.euser, event.user), uuid"
    "  FROM tagxref, tag, event, blob"
    " WHERE tagxref.rid=%d"
    "   AND tag.tagid=tagxref.tagid" 
    "   AND tag.tagname LIKE 'wiki-%%'"
    "   AND event.objid=tagxref.rid"
    "   AND blob.rid=tagxref.rid",
    rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPagename = db_column_text(&q, 0);
    const char *zDate = db_column_text(&q, 1);
    const char *zUser = db_column_text(&q, 2);
    const char *zUuid = db_column_text(&q, 3);
    @ Wiki page
    @ [<a href="%s(g.zBaseURL)/wiki?page=%t(zPagename)">%h(zPagename)</a>]
    @ uuid %s(zUuid) by %h(zUser) on %s(zDate)
    nWiki++;
    cnt++;
  }
  db_finalize(&q);
  if( nWiki==0 ){
    db_prepare(&q,
      "SELECT datetime(mtime), user, comment, uuid"
      "  FROM event, blob"
      " WHERE event.objid=%d"
      "   AND blob.rid=%d",
      rid, rid
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zDate = db_column_text(&q, 0);
      const char *zUuid = db_column_text(&q, 3);
      const char *zUser = db_column_text(&q, 1);
      const char *zCom = db_column_text(&q, 2);

      @ Manifest of version
      hyperlink_to_uuid(zUuid);
      @ %w(zCom) by %h(zUser) on %s(zDate)
      cnt++;
    }
    db_finalize(&q);
  }
  if( cnt==0 ){

    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    @ Control file %s(zUuid).
  }else if( linkToView ){
    @ <a href="%s(g.zBaseURL)/fview/%d(rid)">[view]</a>
  }
}

/*
** WEBPAGE: fdiff
**
** Two arguments, v1 and v2, are integers.  Show the difference between
** the two records.
*/
void diff_page(void){
  int v1 = name_to_rid(PD("v1","0"));
  int v2 = name_to_rid(PD("v2","0"));
  Blob c1, c2, diff;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("Diff");
  @ <h2>Differences From:</h2>
  @ <blockquote>
................................................................................
  object_description(v2, 1);
  @ </blockquote>
  @ <hr>
  @ <blockquote><pre>
  content_get(v1, &c1);
  content_get(v2, &c2);
  blob_zero(&diff);
  text_diff(&c1, &c2, &diff, 4);
  blob_reset(&c1);
  blob_reset(&c2);
  @ %h(blob_str(&diff))
  @ </pre></blockquote>
  blob_reset(&diff);
  style_footer();
}

/*
** WEBPAGE: fview
** URL: /fview?name=UUID
** 
** Show the complete content of a file identified by UUID
** as preformatted text.
*/
void fview_page(void){
  int rid;
  Blob content;

  rid = name_to_rid(PD("name","0"));
  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  if( g.zPath[0]=='i' ){
    if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
                  " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
      winfo_page();
      return;
    }
    if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){
      vinfo_page();
      return;
    }
  }
  style_header("File Content");
  @ <h2>Content Of:</h2>
  @ <blockquote>
  object_description(rid, 0);
  @ </blockquote>
  @ <hr>
  @ <blockquote><pre>
  content_get(rid, &content);
  @ %h(blob_str(&content))
  @ </pre></blockquote>
  blob_reset(&content);
  style_footer();
}

/*
** WEBPAGE: info
** URL: info/UUID
**
** The argument is a UUID which might be a baseline or a file or
** a ticket or something else.  It might also be a wiki page name.
** Figure out what the UUID is an jump to it.  If there is ambiguity,
** draw a page and let the user select the interpretation.
*/
void info_page(void){
  const char *zName;
  int rc, nName, cnt;
  Stmt q;
  
  zName = P("name");
  if( zName==0 ) cgi_redirect("index");
  nName = strlen(zName);
  if( nName<4 || nName>UUID_SIZE || !validate16(zName, nName) ){
    cgi_redirect("index");
  }
  db_multi_exec(
     "CREATE TEMP TABLE refs(type,link);"
     "INSERT INTO refs "
     "  SELECT 'f', rid FROM blob WHERE uuid GLOB '%s*'"
     "  UNION ALL"
     "  SELECT 'w', substr(tagname,6) FROM tag"
     "   WHERE tagname='wiki-%q'"
     "  UNION ALL"
     "  SELECT 't', tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*';",
     zName, zName, zName
  );
  cnt = db_int(0, "SELECT count(*) FROM refs");
  if( cnt==0 ){
    style_header("Broken Link");
    @ <p>No such object: %h(zName)</p>
    style_footer();
    return;
  }
  db_prepare(&q, "SELECT type, link FROM refs");
  db_step(&q);
  if( cnt==1 ){
    int type = *db_column_text(&q, 0);
    int rid = db_column_int(&q, 1);
    db_finalize(&q);
    if( type=='w' ){
      wiki_page();
    }else if( type=='t' ){
      tktview_page();
    }else{
      cgi_replace_parameter("name", mprintf("%d", rid));
      if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
        vinfo_page();
      }else{
        finfo_page();
      }
    }
    return;
  }
  /* Multiple objects */
  style_header("Ambiguous Link");
  @ <h2>Ambiguous Link: %h(zName)</h2>
  @ <ul>
  while( rc==SQLITE_ROW ){
    int type = *db_column_text(&q, 0);
    if( type=='f' ){
      @ <li><p>
      object_description(db_column_int(&q, 1), 1);
      @ </p></li>
    }else if( type=='w' ){
      @ <li><p>
      @ Wiki page <a href="%s(g.zBaseURL)/wiki?name=%s(zName)">%s(zName)</a>.
      @ </li><p>
    }else if( type=='t' ){
      const char *zUuid = db_column_text(&q, 1);
      @ <li><p>
      @ Ticket <a href="%s(g.zBaseURL)/tktview?name=%s(zUuid)">%s(zUuid)</a>.
      @ </li><p>
    }
    rc = db_step(&q);
  }
  @ </ul>
  style_footer();
  db_finalize(&q);
}

Changes to src/login.c.

42
43
44
45
46
47
48




49
50
51
52
53
54





55



56
57
58
59
60
61
62
...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
...
282
283
284
285
286
287
288
289
290
291

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

307
308
309
310
311
312
313
314



































315
316
317
318
319
320
321
322
323
324
325
** not really the point.  The anonymous login keeps search-engine
** crawlers and site download tools like wget from walking change
** logs and downloading diffs of very version of the archive that
** has ever existed, and things like that.
*/
#include "config.h"
#include "login.h"




#include <time.h>

/*
** Return the name of the login cookie
*/
static char *login_cookie_name(void){





  return "fossil_login";



}

/*
** WEBPAGE: /login
** WEBPAGE: /logout
**
** Generate the login page
................................................................................


  /* If the HTTP connection is coming over 127.0.0.1 and if
  ** local login is disabled, then there is no need to check
  ** user credentials.
  */
  zRemoteAddr = PD("REMOTE_ADDR","nil");
  if( strcmp(zRemoteAddr, "127.0.0.1")==0
        && db_get_int("authenticate-localhost",1)==0 ){
    uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
    g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
    zCap = "s";
    g.noPswd = 1;
  }

  /* Check the login cookie to see if it matches a known valid user.
................................................................................
/*
** Set the global capability flags based on a capability string.
*/
void login_set_capabilities(const char *zCap){
  int i;
  for(i=0; zCap[i]; i++){
    switch( zCap[i] ){
      case 's':   g.okSetup = g.okDelete = 1;
      case 'a':   g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery =
                              g.okRdWiki = g.okWrWiki = g.okHistory =

                              g.okNewTkt = g.okPassword = g.okClone = 1;
      case 'i':   g.okRead = g.okWrite = 1;                     break;
      case 'o':   g.okRead = 1;                                 break;

      case 'd':   g.okDelete = 1;                               break;
      case 'h':   g.okHistory = 1;                              break;
      case 'g':   g.okClone = 1;                                break;
      case 'p':   g.okPassword = 1;                             break;
................................................................................
      case 'q':   g.okQuery = 1;                                break;

      case 'j':   g.okRdWiki = 1;                               break;
      case 'k':   g.okWrWiki = g.okRdWiki = g.okApndWiki =1;    break;
      case 'm':   g.okApndWiki = 1;                             break;
      case 'f':   g.okNewWiki = 1;                              break;


      case 'r':   g.okRdTkt = 1;                                break;
      case 'n':   g.okNewTkt = 1;                               break;
      case 'w':   g.okWrTkt = g.okRdTkt = g.okNewTkt = 
                  g.okApndTkt = 1;                              break;
      case 'c':   g.okApndTkt = 1;                              break;
    }
  }
}




































/*
** Call this routine when the credential check fails.  It causes
** a redirect to the "login" page.
*/
void login_needed(void){
  const char *zUrl = PD("REQUEST_URI", "index");
  cgi_redirect(mprintf("login?g=%T", zUrl));
  /* NOTREACHED */
  assert(0);
}







>
>
>
>






>
>
>
>
>
|
>
>
>







 







|
<







 







|

|
>
|







 







>








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











42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
...
234
235
236
237
238
239
240
241

242
243
244
245
246
247
248
...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
** not really the point.  The anonymous login keeps search-engine
** crawlers and site download tools like wget from walking change
** logs and downloading diffs of very version of the archive that
** has ever existed, and things like that.
*/
#include "config.h"
#include "login.h"
#ifdef __MINGW32__
#  include <windows.h>           /* for Sleep */
#  define sleep Sleep            /* windows does not have sleep, but Sleep */
#endif
#include <time.h>

/*
** Return the name of the login cookie
*/
static char *login_cookie_name(void){
  static char *zCookieName = 0;
  if( zCookieName==0 ){
    int n = strlen(g.zTop);
    zCookieName = malloc( n*2+16 );
                      /* 0123456789 12345 */
    strcpy(zCookieName, "fossil_login_");
    encode16((unsigned char*)g.zTop, (unsigned char*)&zCookieName[13], n);
  }
  return zCookieName;
}

/*
** WEBPAGE: /login
** WEBPAGE: /logout
**
** Generate the login page
................................................................................


  /* If the HTTP connection is coming over 127.0.0.1 and if
  ** local login is disabled, then there is no need to check
  ** user credentials.
  */
  zRemoteAddr = PD("REMOTE_ADDR","nil");
  if( strcmp(zRemoteAddr, "127.0.0.1")==0 && db_get_int("localauth",0)==0 ){

    uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
    g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
    zCap = "s";
    g.noPswd = 1;
  }

  /* Check the login cookie to see if it matches a known valid user.
................................................................................
/*
** Set the global capability flags based on a capability string.
*/
void login_set_capabilities(const char *zCap){
  int i;
  for(i=0; zCap[i]; i++){
    switch( zCap[i] ){
      case 's':   g.okSetup = 1;
      case 'a':   g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery =
                              g.okRdWiki = g.okWrWiki = g.okNewWiki =
                              g.okApndWiki = g.okHistory = g.okClone = 
                              g.okNewTkt = g.okPassword = g.okRdAddr = 1;
      case 'i':   g.okRead = g.okWrite = 1;                     break;
      case 'o':   g.okRead = 1;                                 break;

      case 'd':   g.okDelete = 1;                               break;
      case 'h':   g.okHistory = 1;                              break;
      case 'g':   g.okClone = 1;                                break;
      case 'p':   g.okPassword = 1;                             break;
................................................................................
      case 'q':   g.okQuery = 1;                                break;

      case 'j':   g.okRdWiki = 1;                               break;
      case 'k':   g.okWrWiki = g.okRdWiki = g.okApndWiki =1;    break;
      case 'm':   g.okApndWiki = 1;                             break;
      case 'f':   g.okNewWiki = 1;                              break;

      case 'e':   g.okRdAddr = 1;                               break;
      case 'r':   g.okRdTkt = 1;                                break;
      case 'n':   g.okNewTkt = 1;                               break;
      case 'w':   g.okWrTkt = g.okRdTkt = g.okNewTkt = 
                  g.okApndTkt = 1;                              break;
      case 'c':   g.okApndTkt = 1;                              break;
    }
  }
}

/*
** If the current login lacks any of the capabilities listed in
** the input, then return 0.  If all capabilities are present, then
** return 1.
*/
int login_has_capability(const char *zCap, int nCap){
  int i;
  int rc = 1;
  if( nCap<0 ) nCap = strlen(zCap);
  for(i=0; i<nCap && rc && zCap[i]; i++){
    switch( zCap[i] ){
      case 'a':  rc = g.okAdmin;     break;
      case 'c':  rc = g.okApndTkt;   break;
      case 'd':  rc = g.okDelete;    break;
      case 'e':  rc = g.okRdAddr;    break;
      case 'f':  rc = g.okNewWiki;   break;
      case 'g':  rc = g.okClone;     break;
      case 'h':  rc = g.okHistory;   break;
      case 'i':  rc = g.okWrite;     break;
      case 'j':  rc = g.okRdWiki;    break;
      case 'k':  rc = g.okWrWiki;    break;
      case 'm':  rc = g.okApndWiki;  break;
      case 'n':  rc = g.okNewTkt;    break;
      case 'o':  rc = g.okRead;      break;
      case 'p':  rc = g.okPassword;  break;
      case 'q':  rc = g.okQuery;     break;
      case 'r':  rc = g.okRdTkt;     break;
      case 's':  rc = g.okSetup;     break;
      case 'w':  rc = g.okWrTkt;     break;
      default:   rc = 0;             break;
    }
  }
  return rc;
}

/*
** Call this routine when the credential check fails.  It causes
** a redirect to the "login" page.
*/
void login_needed(void){
  const char *zUrl = PD("REQUEST_URI", "index");
  cgi_redirect(mprintf("login?g=%T", zUrl));
  /* NOTREACHED */
  assert(0);
}

Changes to src/main.c.

57
58
59
60
61
62
63

64
65
66
67
68
69
70
...
100
101
102
103
104
105
106

107
108
109
110
111
112
113
...
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
...
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
...
434
435
436
437
438
439
440
441




442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485


486


487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
...
594
595
596
597
598
599
600









601
602
603
604
605
606
607
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int fSqlTrace;          /* True if -sqltrace flag is present */
  int fSqlPrint;          /* True if -sqlprint flag is present */
  int fHttpTrace;         /* Trace outbound HTTP requests */
  char *zPath;            /* Name of webpage being served */
  char *zExtra;           /* Extra path information past the webpage name */
  char *zBaseURL;         /* Full text of the URL being served */

  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */
  char *zErrMsg;          /* Text of an error message */
  Blob cgiIn;             /* Input to an xfer www method */
  int cgiPanic;           /* Write error messages to CGI */

  int *aCommitFile;
................................................................................
  int okNewWiki;          /* f: create new wiki via web */
  int okApndWiki;         /* m: append to wiki via web */
  int okWrWiki;           /* k: edit wiki via web */
  int okRdTkt;            /* r: view tickets via web */
  int okNewTkt;           /* n: create new tickets */
  int okApndTkt;          /* c: append to tickets via the web */
  int okWrTkt;            /* w: make changes to tickets via web */


  FILE *fDebug;           /* Write debug information here, if the file exists */
};

/*
** Macro for debugging:
*/
................................................................................
*/
void help_cmd(void){
  int rc, idx;
  const char *z;
  if( g.argc!=3 ){
    printf("Usage: %s help COMMAND.\nAvailable COMMANDs:\n", g.argv[0]);
    cmd_cmd_list();
    printf("You are running fossil baseline " MANIFEST_UUID "\n");
    return;
  }
  rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
  if( rc==1 ){
    fossil_fatal("unknown command: %s", g.argv[2]);
  }else if( rc==2 ){
    fossil_fatal("ambiguous command prefix: %s", g.argv[2]);
................................................................................
      z++;
    }
  }
  putchar('\n');
}

/*
** RSS feeds need to reference absolute URLs so we need to calculate
** the base URL onto which we add components. This is basically
** cgi_redirect() stripped down and always returning an absolute URL.
*/
static char *get_base_url(void){
  int i;
  const char *zHost = PD("HTTP_HOST","");
  const char *zMode = PD("HTTPS","off");
  const char *zCur = PD("REQUEST_URI","/");

  for(i=0; zCur[i] && zCur[i]!='?' && zCur[i]!='#'; i++){}
  if( g.zExtra ){
................................................................................
    i -= strlen(g.zExtra);
    while( i>0 && zCur[i-1]=='/' ){ i--; }
  }
  while( i>0 && zCur[i-1]!='/' ){ i--; }
  while( i>0 && zCur[i-1]=='/' ){ i--; }

  if( strcmp(zMode,"on")==0 ){
    return mprintf("https://%s%.*s", zHost, i, zCur);




  }
  return mprintf("http://%s%.*s", zHost, i, zCur);
}

/*
** Preconditions:
**
**    * Environment various are set up according to the CGI standard.
**    * The respository database has been located and opened.
** 
** Process the webpage specified by the PATH_INFO or REQUEST_URI
** environment variable.
*/
static void process_one_web_page(void){
  const char *zPathInfo;
................................................................................

  /* Find the page that the user has requested, construct and deliver that
  ** page.
  */
  zPathInfo = P("PATH_INFO");
  if( zPathInfo==0 || zPathInfo[0]==0 ){
    const char *zUri;
    char *zBase;
    zUri = PD("REQUEST_URI","/");
    for(i=0; zUri[i] && zUri[i]!='?' && zUri[i]!='#'; i++){}
    for(j=i; j>0 && zUri[j-1]!='/'; j--){}
    zBase = mprintf("%.*s/index", i-j, &zUri[j]);
    cgi_redirect(zBase);
    cgi_reply();
    return;
  }else{
    zPath = mprintf("%s", zPathInfo);
  }

  /* Remove the leading "/" at the beginning of the path.
  */
  g.zPath = &zPath[1];
  for(i=1; zPath[i] && zPath[i]!='/'; i++){}
  if( zPath[i]=='/' ){
    zPath[i] = 0;
    g.zExtra = &zPath[i+1];





    /* CGI parameters get this treatment elsewhere, but places like getfile
    ** will use g.zExtra directly.
    */
    dehttpize(g.zExtra);
  }else{
    g.zExtra = 0;
  }
  g.zBaseURL = get_base_url();

  /* Prevent robots from indexing this site.
  */
  if( strcmp(g.zPath, "robots.txt")==0 ){
    cgi_set_content_type("text/plain");
    @ User-agent: *
    @ Disallow: /
................................................................................
    db_open_repository(g.argv[2]);
  }else{
    db_must_be_within_tree();
  }
  cgi_handle_http_request();
  process_one_web_page();
}










/*
** COMMAND: server
**
** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY?
**
** Open a socket and begin listening and responding to HTTP requests on







>







 







>







 







|







 







|
|
|

|







 







|
>
>
>
>

<





|







 







<



|
<
<
<











>
>
|
>
>




|
<

<







 







>
>
>
>
>
>
>
>
>







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
...
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
...
436
437
438
439
440
441
442
443
444
445
446
447
448

449
450
451
452
453
454
455
456
457
458
459
460
461
...
465
466
467
468
469
470
471

472
473
474
475



476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496

497

498
499
500
501
502
503
504
...
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int fSqlTrace;          /* True if -sqltrace flag is present */
  int fSqlPrint;          /* True if -sqlprint flag is present */
  int fHttpTrace;         /* Trace outbound HTTP requests */
  char *zPath;            /* Name of webpage being served */
  char *zExtra;           /* Extra path information past the webpage name */
  char *zBaseURL;         /* Full text of the URL being served */
  char *zTop;             /* Parent directory of zPath */
  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */
  char *zErrMsg;          /* Text of an error message */
  Blob cgiIn;             /* Input to an xfer www method */
  int cgiPanic;           /* Write error messages to CGI */

  int *aCommitFile;
................................................................................
  int okNewWiki;          /* f: create new wiki via web */
  int okApndWiki;         /* m: append to wiki via web */
  int okWrWiki;           /* k: edit wiki via web */
  int okRdTkt;            /* r: view tickets via web */
  int okNewTkt;           /* n: create new tickets */
  int okApndTkt;          /* c: append to tickets via the web */
  int okWrTkt;            /* w: make changes to tickets via web */
  int okRdAddr;           /* e: read email addresses on tickets */

  FILE *fDebug;           /* Write debug information here, if the file exists */
};

/*
** Macro for debugging:
*/
................................................................................
*/
void help_cmd(void){
  int rc, idx;
  const char *z;
  if( g.argc!=3 ){
    printf("Usage: %s help COMMAND.\nAvailable COMMANDs:\n", g.argv[0]);
    cmd_cmd_list();
    printf("This is fossil version " MANIFEST_VERSION " " MANIFEST_DATE "\n");
    return;
  }
  rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
  if( rc==1 ){
    fossil_fatal("unknown command: %s", g.argv[2]);
  }else if( rc==2 ){
    fossil_fatal("ambiguous command prefix: %s", g.argv[2]);
................................................................................
      z++;
    }
  }
  putchar('\n');
}

/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zHomeURL to g.zBaseURL without the
** leading "http://" and the host and port.
*/
void set_base_url(void){
  int i;
  const char *zHost = PD("HTTP_HOST","");
  const char *zMode = PD("HTTPS","off");
  const char *zCur = PD("REQUEST_URI","/");

  for(i=0; zCur[i] && zCur[i]!='?' && zCur[i]!='#'; i++){}
  if( g.zExtra ){
................................................................................
    i -= strlen(g.zExtra);
    while( i>0 && zCur[i-1]=='/' ){ i--; }
  }
  while( i>0 && zCur[i-1]!='/' ){ i--; }
  while( i>0 && zCur[i-1]=='/' ){ i--; }

  if( strcmp(zMode,"on")==0 ){
    g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
    g.zTop = &g.zBaseURL[8+strlen(zHost)];
  }else{
    g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
    g.zTop = &g.zBaseURL[7+strlen(zHost)];
  }

}

/*
** Preconditions:
**
**    * Environment variables are set up according to the CGI standard.
**    * The respository database has been located and opened.
** 
** Process the webpage specified by the PATH_INFO or REQUEST_URI
** environment variable.
*/
static void process_one_web_page(void){
  const char *zPathInfo;
................................................................................

  /* Find the page that the user has requested, construct and deliver that
  ** page.
  */
  zPathInfo = P("PATH_INFO");
  if( zPathInfo==0 || zPathInfo[0]==0 ){
    const char *zUri;

    zUri = PD("REQUEST_URI","/");
    for(i=0; zUri[i] && zUri[i]!='?' && zUri[i]!='#'; i++){}
    for(j=i; j>0 && zUri[j-1]!='/'; j--){}
    cgi_redirectf("%.*s/index", i, zUri);



  }else{
    zPath = mprintf("%s", zPathInfo);
  }

  /* Remove the leading "/" at the beginning of the path.
  */
  g.zPath = &zPath[1];
  for(i=1; zPath[i] && zPath[i]!='/'; i++){}
  if( zPath[i]=='/' ){
    zPath[i] = 0;
    g.zExtra = &zPath[i+1];
  }else{
    g.zExtra = 0;
  }
  set_base_url();
  if( g.zExtra ){
    /* CGI parameters get this treatment elsewhere, but places like getfile
    ** will use g.zExtra directly.
    */
    dehttpize(g.zExtra);
    cgi_set_parameter_nocopy("name", g.zExtra);

  }


  /* Prevent robots from indexing this site.
  */
  if( strcmp(g.zPath, "robots.txt")==0 ){
    cgi_set_content_type("text/plain");
    @ User-agent: *
    @ Disallow: /
................................................................................
    db_open_repository(g.argv[2]);
  }else{
    db_must_be_within_tree();
  }
  cgi_handle_http_request();
  process_one_web_page();
}

/*
** COMMAND: test-http
** Works like the http command but gives setup permission to all users.
*/
void cmd_test_http(void){
  login_set_capabilities("s");
  cmd_http();
}

/*
** COMMAND: server
**
** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY?
**
** Open a socket and begin listening and responding to HTTP requests on

Changes to src/main.mk.

12
13
14
15
16
17
18

19
20
21
22
23
24
25
..
41
42
43
44
45
46
47

48
49
50
51

52

53



54
55
56
57
58
59
60
..
62
63
64
65
66
67
68

69
70
71
72
73
74
75
..
91
92
93
94
95
96
97

98
99
100
101

102

103



104
105
106
107
108
109
110
...
112
113
114
115
116
117
118

119
120
121
122
123
124
125
...
141
142
143
144
145
146
147

148
149
150
151

152

153



154
155
156
157
158
159
160
...
182
183
184
185
186
187
188
189
190


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232










233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522










523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562










563
564
565
566
567
568
569
570
571
572










573
574
575
576
577
578
579
580
581
582






























583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)


SRC = \
  $(SRCDIR)/add.c \
  $(SRCDIR)/bag.c \
  $(SRCDIR)/blob.c \

  $(SRCDIR)/cgi.c \
  $(SRCDIR)/checkin.c \
  $(SRCDIR)/checkout.c \
  $(SRCDIR)/clearsign.c \
  $(SRCDIR)/clone.c \
  $(SRCDIR)/comformat.c \
  $(SRCDIR)/construct.c \
................................................................................
  $(SRCDIR)/merge.c \
  $(SRCDIR)/merge3.c \
  $(SRCDIR)/name.c \
  $(SRCDIR)/pivot.c \
  $(SRCDIR)/pqueue.c \
  $(SRCDIR)/printf.c \
  $(SRCDIR)/rebuild.c \

  $(SRCDIR)/schema.c \
  $(SRCDIR)/setup.c \
  $(SRCDIR)/sha1.c \
  $(SRCDIR)/style.c \

  $(SRCDIR)/sync.c \

  $(SRCDIR)/timeline.c \



  $(SRCDIR)/undo.c \
  $(SRCDIR)/update.c \
  $(SRCDIR)/url.c \
  $(SRCDIR)/user.c \
  $(SRCDIR)/verify.c \
  $(SRCDIR)/vfile.c \
  $(SRCDIR)/wiki.c \
................................................................................
  $(SRCDIR)/xfer.c \
  $(SRCDIR)/zip.c

TRANS_SRC = \
  add_.c \
  bag_.c \
  blob_.c \

  cgi_.c \
  checkin_.c \
  checkout_.c \
  clearsign_.c \
  clone_.c \
  comformat_.c \
  construct_.c \
................................................................................
  merge_.c \
  merge3_.c \
  name_.c \
  pivot_.c \
  pqueue_.c \
  printf_.c \
  rebuild_.c \

  schema_.c \
  setup_.c \
  sha1_.c \
  style_.c \

  sync_.c \

  timeline_.c \



  undo_.c \
  update_.c \
  url_.c \
  user_.c \
  verify_.c \
  vfile_.c \
  wiki_.c \
................................................................................
  xfer_.c \
  zip_.c

OBJ = \
  add.o \
  bag.o \
  blob.o \

  cgi.o \
  checkin.o \
  checkout.o \
  clearsign.o \
  clone.o \
  comformat.o \
  construct.o \
................................................................................
  merge.o \
  merge3.o \
  name.o \
  pivot.o \
  pqueue.o \
  printf.o \
  rebuild.o \

  schema.o \
  setup.o \
  sha1.o \
  style.o \

  sync.o \

  timeline.o \



  undo.o \
  update.o \
  url.o \
  user.o \
  verify.o \
  vfile.o \
  wiki.o \
................................................................................

# WARNING. DANGER. Running the testsuite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(APPNAME)
	$(TCLSH) test/tester.tcl $(APPNAME)

VERSION.h:	$(SRCDIR)/../manifest.uuid
	awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}'  $(SRCDIR)/../manifest.uuid >VERSION.h



$(APPNAME):	headers $(OBJ) sqlite3.o
	$(TCC) -o $(APPNAME) $(OBJ) sqlite3.o $(LIB)

clean:	
	rm -f *.o *_.c $(APPNAME) VERSION.h
	rm -f translate makeheaders mkindex page_index.h headers
	rm -f add.h bag.h blob.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h construct.h content.h db.h delta.h deltacmd.h descendents.h diff.h diffcmd.h encode.h file.h http.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h schema.h setup.h sha1.h style.h sync.h timeline.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h xfer.h zip.h

headers:	makeheaders mkindex $(TRANS_SRC) ./VERSION.h
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	./mkindex $(TRANS_SRC) >page_index.h
	touch headers

add_.c:	$(SRCDIR)/add.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/add.c | sed -f $(SRCDIR)/VERSION >add_.c

add.o:	add_.c add.h  $(SRCDIR)/config.h
	$(XTCC) -o add.o -c add_.c

add.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

bag_.c:	$(SRCDIR)/bag.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/bag.c | sed -f $(SRCDIR)/VERSION >bag_.c

bag.o:	bag_.c bag.h  $(SRCDIR)/config.h
	$(XTCC) -o bag.o -c bag_.c

bag.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

blob_.c:	$(SRCDIR)/blob.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/blob.c | sed -f $(SRCDIR)/VERSION >blob_.c

blob.o:	blob_.c blob.h  $(SRCDIR)/config.h
	$(XTCC) -o blob.o -c blob_.c

blob.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h










	touch headers

cgi_.c:	$(SRCDIR)/cgi.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/cgi.c | sed -f $(SRCDIR)/VERSION >cgi_.c

cgi.o:	cgi_.c cgi.h  $(SRCDIR)/config.h
	$(XTCC) -o cgi.o -c cgi_.c

cgi.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

checkin_.c:	$(SRCDIR)/checkin.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/checkin.c | sed -f $(SRCDIR)/VERSION >checkin_.c

checkin.o:	checkin_.c checkin.h  $(SRCDIR)/config.h
	$(XTCC) -o checkin.o -c checkin_.c

checkin.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

checkout_.c:	$(SRCDIR)/checkout.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/checkout.c | sed -f $(SRCDIR)/VERSION >checkout_.c

checkout.o:	checkout_.c checkout.h  $(SRCDIR)/config.h
	$(XTCC) -o checkout.o -c checkout_.c

checkout.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

clearsign_.c:	$(SRCDIR)/clearsign.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/clearsign.c | sed -f $(SRCDIR)/VERSION >clearsign_.c

clearsign.o:	clearsign_.c clearsign.h  $(SRCDIR)/config.h
	$(XTCC) -o clearsign.o -c clearsign_.c

clearsign.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

clone_.c:	$(SRCDIR)/clone.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/clone.c | sed -f $(SRCDIR)/VERSION >clone_.c

clone.o:	clone_.c clone.h  $(SRCDIR)/config.h
	$(XTCC) -o clone.o -c clone_.c

clone.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

comformat_.c:	$(SRCDIR)/comformat.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/comformat.c | sed -f $(SRCDIR)/VERSION >comformat_.c

comformat.o:	comformat_.c comformat.h  $(SRCDIR)/config.h
	$(XTCC) -o comformat.o -c comformat_.c

comformat.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

construct_.c:	$(SRCDIR)/construct.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/construct.c | sed -f $(SRCDIR)/VERSION >construct_.c

construct.o:	construct_.c construct.h  $(SRCDIR)/config.h
	$(XTCC) -o construct.o -c construct_.c

construct.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

content_.c:	$(SRCDIR)/content.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/content.c | sed -f $(SRCDIR)/VERSION >content_.c

content.o:	content_.c content.h  $(SRCDIR)/config.h
	$(XTCC) -o content.o -c content_.c

content.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

db_.c:	$(SRCDIR)/db.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/db.c | sed -f $(SRCDIR)/VERSION >db_.c

db.o:	db_.c db.h  $(SRCDIR)/config.h
	$(XTCC) -o db.o -c db_.c

db.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

delta_.c:	$(SRCDIR)/delta.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/delta.c | sed -f $(SRCDIR)/VERSION >delta_.c

delta.o:	delta_.c delta.h  $(SRCDIR)/config.h
	$(XTCC) -o delta.o -c delta_.c

delta.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

deltacmd_.c:	$(SRCDIR)/deltacmd.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/deltacmd.c | sed -f $(SRCDIR)/VERSION >deltacmd_.c

deltacmd.o:	deltacmd_.c deltacmd.h  $(SRCDIR)/config.h
	$(XTCC) -o deltacmd.o -c deltacmd_.c

deltacmd.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

descendents_.c:	$(SRCDIR)/descendents.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/descendents.c | sed -f $(SRCDIR)/VERSION >descendents_.c

descendents.o:	descendents_.c descendents.h  $(SRCDIR)/config.h
	$(XTCC) -o descendents.o -c descendents_.c

descendents.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

diff_.c:	$(SRCDIR)/diff.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/diff.c | sed -f $(SRCDIR)/VERSION >diff_.c

diff.o:	diff_.c diff.h  $(SRCDIR)/config.h
	$(XTCC) -o diff.o -c diff_.c

diff.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

diffcmd_.c:	$(SRCDIR)/diffcmd.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/diffcmd.c | sed -f $(SRCDIR)/VERSION >diffcmd_.c

diffcmd.o:	diffcmd_.c diffcmd.h  $(SRCDIR)/config.h
	$(XTCC) -o diffcmd.o -c diffcmd_.c

diffcmd.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

encode_.c:	$(SRCDIR)/encode.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/encode.c | sed -f $(SRCDIR)/VERSION >encode_.c

encode.o:	encode_.c encode.h  $(SRCDIR)/config.h
	$(XTCC) -o encode.o -c encode_.c

encode.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

file_.c:	$(SRCDIR)/file.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/file.c | sed -f $(SRCDIR)/VERSION >file_.c

file.o:	file_.c file.h  $(SRCDIR)/config.h
	$(XTCC) -o file.o -c file_.c

file.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

http_.c:	$(SRCDIR)/http.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/http.c | sed -f $(SRCDIR)/VERSION >http_.c

http.o:	http_.c http.h  $(SRCDIR)/config.h
	$(XTCC) -o http.o -c http_.c

http.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

info_.c:	$(SRCDIR)/info.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/info.c | sed -f $(SRCDIR)/VERSION >info_.c

info.o:	info_.c info.h  $(SRCDIR)/config.h
	$(XTCC) -o info.o -c info_.c

info.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

login_.c:	$(SRCDIR)/login.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/login.c | sed -f $(SRCDIR)/VERSION >login_.c

login.o:	login_.c login.h  $(SRCDIR)/config.h
	$(XTCC) -o login.o -c login_.c

login.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

main_.c:	$(SRCDIR)/main.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/main.c | sed -f $(SRCDIR)/VERSION >main_.c

main.o:	main_.c main.h page_index.h $(SRCDIR)/config.h
	$(XTCC) -o main.o -c main_.c

main.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

manifest_.c:	$(SRCDIR)/manifest.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/manifest.c | sed -f $(SRCDIR)/VERSION >manifest_.c

manifest.o:	manifest_.c manifest.h  $(SRCDIR)/config.h
	$(XTCC) -o manifest.o -c manifest_.c

manifest.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

md5_.c:	$(SRCDIR)/md5.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/md5.c | sed -f $(SRCDIR)/VERSION >md5_.c

md5.o:	md5_.c md5.h  $(SRCDIR)/config.h
	$(XTCC) -o md5.o -c md5_.c

md5.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

merge_.c:	$(SRCDIR)/merge.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/merge.c | sed -f $(SRCDIR)/VERSION >merge_.c

merge.o:	merge_.c merge.h  $(SRCDIR)/config.h
	$(XTCC) -o merge.o -c merge_.c

merge.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

merge3_.c:	$(SRCDIR)/merge3.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/merge3.c | sed -f $(SRCDIR)/VERSION >merge3_.c

merge3.o:	merge3_.c merge3.h  $(SRCDIR)/config.h
	$(XTCC) -o merge3.o -c merge3_.c

merge3.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

name_.c:	$(SRCDIR)/name.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/name.c | sed -f $(SRCDIR)/VERSION >name_.c

name.o:	name_.c name.h  $(SRCDIR)/config.h
	$(XTCC) -o name.o -c name_.c

name.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

pivot_.c:	$(SRCDIR)/pivot.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/pivot.c | sed -f $(SRCDIR)/VERSION >pivot_.c

pivot.o:	pivot_.c pivot.h  $(SRCDIR)/config.h
	$(XTCC) -o pivot.o -c pivot_.c

pivot.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

pqueue_.c:	$(SRCDIR)/pqueue.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/pqueue.c | sed -f $(SRCDIR)/VERSION >pqueue_.c

pqueue.o:	pqueue_.c pqueue.h  $(SRCDIR)/config.h
	$(XTCC) -o pqueue.o -c pqueue_.c

pqueue.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

printf_.c:	$(SRCDIR)/printf.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/printf.c | sed -f $(SRCDIR)/VERSION >printf_.c

printf.o:	printf_.c printf.h  $(SRCDIR)/config.h
	$(XTCC) -o printf.o -c printf_.c

printf.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

rebuild_.c:	$(SRCDIR)/rebuild.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/rebuild.c | sed -f $(SRCDIR)/VERSION >rebuild_.c

rebuild.o:	rebuild_.c rebuild.h  $(SRCDIR)/config.h
	$(XTCC) -o rebuild.o -c rebuild_.c

rebuild.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h










	touch headers

schema_.c:	$(SRCDIR)/schema.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/schema.c | sed -f $(SRCDIR)/VERSION >schema_.c

schema.o:	schema_.c schema.h  $(SRCDIR)/config.h
	$(XTCC) -o schema.o -c schema_.c

schema.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

setup_.c:	$(SRCDIR)/setup.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/setup.c | sed -f $(SRCDIR)/VERSION >setup_.c

setup.o:	setup_.c setup.h  $(SRCDIR)/config.h
	$(XTCC) -o setup.o -c setup_.c

setup.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

sha1_.c:	$(SRCDIR)/sha1.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/sha1.c | sed -f $(SRCDIR)/VERSION >sha1_.c

sha1.o:	sha1_.c sha1.h  $(SRCDIR)/config.h
	$(XTCC) -o sha1.o -c sha1_.c

sha1.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

style_.c:	$(SRCDIR)/style.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/style.c | sed -f $(SRCDIR)/VERSION >style_.c

style.o:	style_.c style.h  $(SRCDIR)/config.h
	$(XTCC) -o style.o -c style_.c

style.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h










	touch headers

sync_.c:	$(SRCDIR)/sync.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/sync.c | sed -f $(SRCDIR)/VERSION >sync_.c

sync.o:	sync_.c sync.h  $(SRCDIR)/config.h
	$(XTCC) -o sync.o -c sync_.c

sync.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h










	touch headers

timeline_.c:	$(SRCDIR)/timeline.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/timeline.c | sed -f $(SRCDIR)/VERSION >timeline_.c

timeline.o:	timeline_.c timeline.h  $(SRCDIR)/config.h
	$(XTCC) -o timeline.o -c timeline_.c

timeline.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h






























	touch headers

undo_.c:	$(SRCDIR)/undo.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/undo.c | sed -f $(SRCDIR)/VERSION >undo_.c

undo.o:	undo_.c undo.h  $(SRCDIR)/config.h
	$(XTCC) -o undo.o -c undo_.c

undo.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

update_.c:	$(SRCDIR)/update.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/update.c | sed -f $(SRCDIR)/VERSION >update_.c

update.o:	update_.c update.h  $(SRCDIR)/config.h
	$(XTCC) -o update.o -c update_.c

update.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

url_.c:	$(SRCDIR)/url.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/url.c | sed -f $(SRCDIR)/VERSION >url_.c

url.o:	url_.c url.h  $(SRCDIR)/config.h
	$(XTCC) -o url.o -c url_.c

url.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

user_.c:	$(SRCDIR)/user.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/user.c | sed -f $(SRCDIR)/VERSION >user_.c

user.o:	user_.c user.h  $(SRCDIR)/config.h
	$(XTCC) -o user.o -c user_.c

user.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

verify_.c:	$(SRCDIR)/verify.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/verify.c | sed -f $(SRCDIR)/VERSION >verify_.c

verify.o:	verify_.c verify.h  $(SRCDIR)/config.h
	$(XTCC) -o verify.o -c verify_.c

verify.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

vfile_.c:	$(SRCDIR)/vfile.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/vfile.c | sed -f $(SRCDIR)/VERSION >vfile_.c

vfile.o:	vfile_.c vfile.h  $(SRCDIR)/config.h
	$(XTCC) -o vfile.o -c vfile_.c

vfile.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

wiki_.c:	$(SRCDIR)/wiki.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/wiki.c | sed -f $(SRCDIR)/VERSION >wiki_.c

wiki.o:	wiki_.c wiki.h  $(SRCDIR)/config.h
	$(XTCC) -o wiki.o -c wiki_.c

wiki.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

wikiformat_.c:	$(SRCDIR)/wikiformat.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/wikiformat.c | sed -f $(SRCDIR)/VERSION >wikiformat_.c

wikiformat.o:	wikiformat_.c wikiformat.h  $(SRCDIR)/config.h
	$(XTCC) -o wikiformat.o -c wikiformat_.c

wikiformat.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

xfer_.c:	$(SRCDIR)/xfer.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/xfer.c | sed -f $(SRCDIR)/VERSION >xfer_.c

xfer.o:	xfer_.c xfer.h  $(SRCDIR)/config.h
	$(XTCC) -o xfer.o -c xfer_.c

xfer.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

zip_.c:	$(SRCDIR)/zip.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/zip.c | sed -f $(SRCDIR)/VERSION >zip_.c

zip.o:	zip_.c zip.h  $(SRCDIR)/config.h
	$(XTCC) -o zip.o -c zip_.c

zip.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h sync_.c:sync.h timeline_.c:timeline.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

sqlite3.o:	$(SRCDIR)/sqlite3.c
	$(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_PRIVATE= -DTHREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -c $(SRCDIR)/sqlite3.c -o sqlite3.o








>







 







>




>

>

>
>
>







 







>







 







>




>

>

>
>
>







 







>







 







>




>

>

>
>
>







 







|

>
>







|


|










|









|









|
>
>
>
>
>
>
>
>
>
>









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|









|
>
>
>
>
>
>
>
>
>
>









|









|









|









|
>
>
>
>
>
>
>
>
>
>









|
>
>
>
>
>
>
>
>
>
>









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









|









|









|









|









|









|









|









|









|









|



|

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
..
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
..
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
...
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)


SRC = \
  $(SRCDIR)/add.c \
  $(SRCDIR)/bag.c \
  $(SRCDIR)/blob.c \
  $(SRCDIR)/branch.c \
  $(SRCDIR)/cgi.c \
  $(SRCDIR)/checkin.c \
  $(SRCDIR)/checkout.c \
  $(SRCDIR)/clearsign.c \
  $(SRCDIR)/clone.c \
  $(SRCDIR)/comformat.c \
  $(SRCDIR)/construct.c \
................................................................................
  $(SRCDIR)/merge.c \
  $(SRCDIR)/merge3.c \
  $(SRCDIR)/name.c \
  $(SRCDIR)/pivot.c \
  $(SRCDIR)/pqueue.c \
  $(SRCDIR)/printf.c \
  $(SRCDIR)/rebuild.c \
  $(SRCDIR)/rss.c \
  $(SRCDIR)/schema.c \
  $(SRCDIR)/setup.c \
  $(SRCDIR)/sha1.c \
  $(SRCDIR)/style.c \
  $(SRCDIR)/subscript.c \
  $(SRCDIR)/sync.c \
  $(SRCDIR)/tag.c \
  $(SRCDIR)/timeline.c \
  $(SRCDIR)/tkt.c \
  $(SRCDIR)/tktconfig.c \
  $(SRCDIR)/tktsetup.c \
  $(SRCDIR)/undo.c \
  $(SRCDIR)/update.c \
  $(SRCDIR)/url.c \
  $(SRCDIR)/user.c \
  $(SRCDIR)/verify.c \
  $(SRCDIR)/vfile.c \
  $(SRCDIR)/wiki.c \
................................................................................
  $(SRCDIR)/xfer.c \
  $(SRCDIR)/zip.c

TRANS_SRC = \
  add_.c \
  bag_.c \
  blob_.c \
  branch_.c \
  cgi_.c \
  checkin_.c \
  checkout_.c \
  clearsign_.c \
  clone_.c \
  comformat_.c \
  construct_.c \
................................................................................
  merge_.c \
  merge3_.c \
  name_.c \
  pivot_.c \
  pqueue_.c \
  printf_.c \
  rebuild_.c \
  rss_.c \
  schema_.c \
  setup_.c \
  sha1_.c \
  style_.c \
  subscript_.c \
  sync_.c \
  tag_.c \
  timeline_.c \
  tkt_.c \
  tktconfig_.c \
  tktsetup_.c \
  undo_.c \
  update_.c \
  url_.c \
  user_.c \
  verify_.c \
  vfile_.c \
  wiki_.c \
................................................................................
  xfer_.c \
  zip_.c

OBJ = \
  add.o \
  bag.o \
  blob.o \
  branch.o \
  cgi.o \
  checkin.o \
  checkout.o \
  clearsign.o \
  clone.o \
  comformat.o \
  construct.o \
................................................................................
  merge.o \
  merge3.o \
  name.o \
  pivot.o \
  pqueue.o \
  printf.o \
  rebuild.o \
  rss.o \
  schema.o \
  setup.o \
  sha1.o \
  style.o \
  subscript.o \
  sync.o \
  tag.o \
  timeline.o \
  tkt.o \
  tktconfig.o \
  tktsetup.o \
  undo.o \
  update.o \
  url.o \
  user.o \
  verify.o \
  vfile.o \
  wiki.o \
................................................................................

# WARNING. DANGER. Running the testsuite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(APPNAME)
	$(TCLSH) test/tester.tcl $(APPNAME)

VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest
	awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}'  $(SRCDIR)/../manifest.uuid >VERSION.h
	awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}'  $(SRCDIR)/../manifest.uuid >>VERSION.h
	awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n", substr($$2,1,10),substr($$2,12)}'  $(SRCDIR)/../manifest >>VERSION.h

$(APPNAME):	headers $(OBJ) sqlite3.o
	$(TCC) -o $(APPNAME) $(OBJ) sqlite3.o $(LIB)

clean:	
	rm -f *.o *_.c $(APPNAME) VERSION.h
	rm -f translate makeheaders mkindex page_index.h headers
	rm -f add.h bag.h blob.h branch.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h construct.h content.h db.h delta.h deltacmd.h descendents.h diff.h diffcmd.h encode.h file.h http.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h rss.h schema.h setup.h sha1.h style.h subscript.h sync.h tag.h timeline.h tkt.h tktconfig.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h xfer.h zip.h

headers:	makeheaders mkindex $(TRANS_SRC) ./VERSION.h
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	./mkindex $(TRANS_SRC) >page_index.h
	touch headers

add_.c:	$(SRCDIR)/add.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/add.c | sed -f $(SRCDIR)/VERSION >add_.c

add.o:	add_.c add.h  $(SRCDIR)/config.h
	$(XTCC) -o add.o -c add_.c

add.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

bag_.c:	$(SRCDIR)/bag.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/bag.c | sed -f $(SRCDIR)/VERSION >bag_.c

bag.o:	bag_.c bag.h  $(SRCDIR)/config.h
	$(XTCC) -o bag.o -c bag_.c

bag.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

blob_.c:	$(SRCDIR)/blob.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/blob.c | sed -f $(SRCDIR)/VERSION >blob_.c

blob.o:	blob_.c blob.h  $(SRCDIR)/config.h
	$(XTCC) -o blob.o -c blob_.c

blob.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

branch_.c:	$(SRCDIR)/branch.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/branch.c | sed -f $(SRCDIR)/VERSION >branch_.c

branch.o:	branch_.c branch.h  $(SRCDIR)/config.h
	$(XTCC) -o branch.o -c branch_.c

branch.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

cgi_.c:	$(SRCDIR)/cgi.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/cgi.c | sed -f $(SRCDIR)/VERSION >cgi_.c

cgi.o:	cgi_.c cgi.h  $(SRCDIR)/config.h
	$(XTCC) -o cgi.o -c cgi_.c

cgi.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

checkin_.c:	$(SRCDIR)/checkin.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/checkin.c | sed -f $(SRCDIR)/VERSION >checkin_.c

checkin.o:	checkin_.c checkin.h  $(SRCDIR)/config.h
	$(XTCC) -o checkin.o -c checkin_.c

checkin.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

checkout_.c:	$(SRCDIR)/checkout.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/checkout.c | sed -f $(SRCDIR)/VERSION >checkout_.c

checkout.o:	checkout_.c checkout.h  $(SRCDIR)/config.h
	$(XTCC) -o checkout.o -c checkout_.c

checkout.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

clearsign_.c:	$(SRCDIR)/clearsign.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/clearsign.c | sed -f $(SRCDIR)/VERSION >clearsign_.c

clearsign.o:	clearsign_.c clearsign.h  $(SRCDIR)/config.h
	$(XTCC) -o clearsign.o -c clearsign_.c

clearsign.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

clone_.c:	$(SRCDIR)/clone.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/clone.c | sed -f $(SRCDIR)/VERSION >clone_.c

clone.o:	clone_.c clone.h  $(SRCDIR)/config.h
	$(XTCC) -o clone.o -c clone_.c

clone.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

comformat_.c:	$(SRCDIR)/comformat.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/comformat.c | sed -f $(SRCDIR)/VERSION >comformat_.c

comformat.o:	comformat_.c comformat.h  $(SRCDIR)/config.h
	$(XTCC) -o comformat.o -c comformat_.c

comformat.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

construct_.c:	$(SRCDIR)/construct.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/construct.c | sed -f $(SRCDIR)/VERSION >construct_.c

construct.o:	construct_.c construct.h  $(SRCDIR)/config.h
	$(XTCC) -o construct.o -c construct_.c

construct.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

content_.c:	$(SRCDIR)/content.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/content.c | sed -f $(SRCDIR)/VERSION >content_.c

content.o:	content_.c content.h  $(SRCDIR)/config.h
	$(XTCC) -o content.o -c content_.c

content.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

db_.c:	$(SRCDIR)/db.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/db.c | sed -f $(SRCDIR)/VERSION >db_.c

db.o:	db_.c db.h  $(SRCDIR)/config.h
	$(XTCC) -o db.o -c db_.c

db.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

delta_.c:	$(SRCDIR)/delta.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/delta.c | sed -f $(SRCDIR)/VERSION >delta_.c

delta.o:	delta_.c delta.h  $(SRCDIR)/config.h
	$(XTCC) -o delta.o -c delta_.c

delta.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

deltacmd_.c:	$(SRCDIR)/deltacmd.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/deltacmd.c | sed -f $(SRCDIR)/VERSION >deltacmd_.c

deltacmd.o:	deltacmd_.c deltacmd.h  $(SRCDIR)/config.h
	$(XTCC) -o deltacmd.o -c deltacmd_.c

deltacmd.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

descendents_.c:	$(SRCDIR)/descendents.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/descendents.c | sed -f $(SRCDIR)/VERSION >descendents_.c

descendents.o:	descendents_.c descendents.h  $(SRCDIR)/config.h
	$(XTCC) -o descendents.o -c descendents_.c

descendents.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

diff_.c:	$(SRCDIR)/diff.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/diff.c | sed -f $(SRCDIR)/VERSION >diff_.c

diff.o:	diff_.c diff.h  $(SRCDIR)/config.h
	$(XTCC) -o diff.o -c diff_.c

diff.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

diffcmd_.c:	$(SRCDIR)/diffcmd.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/diffcmd.c | sed -f $(SRCDIR)/VERSION >diffcmd_.c

diffcmd.o:	diffcmd_.c diffcmd.h  $(SRCDIR)/config.h
	$(XTCC) -o diffcmd.o -c diffcmd_.c

diffcmd.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

encode_.c:	$(SRCDIR)/encode.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/encode.c | sed -f $(SRCDIR)/VERSION >encode_.c

encode.o:	encode_.c encode.h  $(SRCDIR)/config.h
	$(XTCC) -o encode.o -c encode_.c

encode.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

file_.c:	$(SRCDIR)/file.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/file.c | sed -f $(SRCDIR)/VERSION >file_.c

file.o:	file_.c file.h  $(SRCDIR)/config.h
	$(XTCC) -o file.o -c file_.c

file.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

http_.c:	$(SRCDIR)/http.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/http.c | sed -f $(SRCDIR)/VERSION >http_.c

http.o:	http_.c http.h  $(SRCDIR)/config.h
	$(XTCC) -o http.o -c http_.c

http.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

info_.c:	$(SRCDIR)/info.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/info.c | sed -f $(SRCDIR)/VERSION >info_.c

info.o:	info_.c info.h  $(SRCDIR)/config.h
	$(XTCC) -o info.o -c info_.c

info.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

login_.c:	$(SRCDIR)/login.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/login.c | sed -f $(SRCDIR)/VERSION >login_.c

login.o:	login_.c login.h  $(SRCDIR)/config.h
	$(XTCC) -o login.o -c login_.c

login.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

main_.c:	$(SRCDIR)/main.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/main.c | sed -f $(SRCDIR)/VERSION >main_.c

main.o:	main_.c main.h page_index.h $(SRCDIR)/config.h
	$(XTCC) -o main.o -c main_.c

main.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

manifest_.c:	$(SRCDIR)/manifest.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/manifest.c | sed -f $(SRCDIR)/VERSION >manifest_.c

manifest.o:	manifest_.c manifest.h  $(SRCDIR)/config.h
	$(XTCC) -o manifest.o -c manifest_.c

manifest.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

md5_.c:	$(SRCDIR)/md5.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/md5.c | sed -f $(SRCDIR)/VERSION >md5_.c

md5.o:	md5_.c md5.h  $(SRCDIR)/config.h
	$(XTCC) -o md5.o -c md5_.c

md5.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

merge_.c:	$(SRCDIR)/merge.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/merge.c | sed -f $(SRCDIR)/VERSION >merge_.c

merge.o:	merge_.c merge.h  $(SRCDIR)/config.h
	$(XTCC) -o merge.o -c merge_.c

merge.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

merge3_.c:	$(SRCDIR)/merge3.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/merge3.c | sed -f $(SRCDIR)/VERSION >merge3_.c

merge3.o:	merge3_.c merge3.h  $(SRCDIR)/config.h
	$(XTCC) -o merge3.o -c merge3_.c

merge3.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

name_.c:	$(SRCDIR)/name.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/name.c | sed -f $(SRCDIR)/VERSION >name_.c

name.o:	name_.c name.h  $(SRCDIR)/config.h
	$(XTCC) -o name.o -c name_.c

name.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

pivot_.c:	$(SRCDIR)/pivot.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/pivot.c | sed -f $(SRCDIR)/VERSION >pivot_.c

pivot.o:	pivot_.c pivot.h  $(SRCDIR)/config.h
	$(XTCC) -o pivot.o -c pivot_.c

pivot.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

pqueue_.c:	$(SRCDIR)/pqueue.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/pqueue.c | sed -f $(SRCDIR)/VERSION >pqueue_.c

pqueue.o:	pqueue_.c pqueue.h  $(SRCDIR)/config.h
	$(XTCC) -o pqueue.o -c pqueue_.c

pqueue.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

printf_.c:	$(SRCDIR)/printf.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/printf.c | sed -f $(SRCDIR)/VERSION >printf_.c

printf.o:	printf_.c printf.h  $(SRCDIR)/config.h
	$(XTCC) -o printf.o -c printf_.c

printf.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

rebuild_.c:	$(SRCDIR)/rebuild.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/rebuild.c | sed -f $(SRCDIR)/VERSION >rebuild_.c

rebuild.o:	rebuild_.c rebuild.h  $(SRCDIR)/config.h
	$(XTCC) -o rebuild.o -c rebuild_.c

rebuild.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

rss_.c:	$(SRCDIR)/rss.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/rss.c | sed -f $(SRCDIR)/VERSION >rss_.c

rss.o:	rss_.c rss.h  $(SRCDIR)/config.h
	$(XTCC) -o rss.o -c rss_.c

rss.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

schema_.c:	$(SRCDIR)/schema.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/schema.c | sed -f $(SRCDIR)/VERSION >schema_.c

schema.o:	schema_.c schema.h  $(SRCDIR)/config.h
	$(XTCC) -o schema.o -c schema_.c

schema.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

setup_.c:	$(SRCDIR)/setup.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/setup.c | sed -f $(SRCDIR)/VERSION >setup_.c

setup.o:	setup_.c setup.h  $(SRCDIR)/config.h
	$(XTCC) -o setup.o -c setup_.c

setup.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

sha1_.c:	$(SRCDIR)/sha1.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/sha1.c | sed -f $(SRCDIR)/VERSION >sha1_.c

sha1.o:	sha1_.c sha1.h  $(SRCDIR)/config.h
	$(XTCC) -o sha1.o -c sha1_.c

sha1.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

style_.c:	$(SRCDIR)/style.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/style.c | sed -f $(SRCDIR)/VERSION >style_.c

style.o:	style_.c style.h  $(SRCDIR)/config.h
	$(XTCC) -o style.o -c style_.c

style.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

subscript_.c:	$(SRCDIR)/subscript.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/subscript.c | sed -f $(SRCDIR)/VERSION >subscript_.c

subscript.o:	subscript_.c subscript.h  $(SRCDIR)/config.h
	$(XTCC) -o subscript.o -c subscript_.c

subscript.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

sync_.c:	$(SRCDIR)/sync.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/sync.c | sed -f $(SRCDIR)/VERSION >sync_.c

sync.o:	sync_.c sync.h  $(SRCDIR)/config.h
	$(XTCC) -o sync.o -c sync_.c

sync.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

tag_.c:	$(SRCDIR)/tag.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/tag.c | sed -f $(SRCDIR)/VERSION >tag_.c

tag.o:	tag_.c tag.h  $(SRCDIR)/config.h
	$(XTCC) -o tag.o -c tag_.c

tag.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

timeline_.c:	$(SRCDIR)/timeline.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/timeline.c | sed -f $(SRCDIR)/VERSION >timeline_.c

timeline.o:	timeline_.c timeline.h  $(SRCDIR)/config.h
	$(XTCC) -o timeline.o -c timeline_.c

timeline.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

tkt_.c:	$(SRCDIR)/tkt.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/tkt.c | sed -f $(SRCDIR)/VERSION >tkt_.c

tkt.o:	tkt_.c tkt.h  $(SRCDIR)/config.h
	$(XTCC) -o tkt.o -c tkt_.c

tkt.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

tktconfig_.c:	$(SRCDIR)/tktconfig.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/tktconfig.c | sed -f $(SRCDIR)/VERSION >tktconfig_.c

tktconfig.o:	tktconfig_.c tktconfig.h  $(SRCDIR)/config.h
	$(XTCC) -o tktconfig.o -c tktconfig_.c

tktconfig.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

tktsetup_.c:	$(SRCDIR)/tktsetup.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/tktsetup.c | sed -f $(SRCDIR)/VERSION >tktsetup_.c

tktsetup.o:	tktsetup_.c tktsetup.h  $(SRCDIR)/config.h
	$(XTCC) -o tktsetup.o -c tktsetup_.c

tktsetup.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

undo_.c:	$(SRCDIR)/undo.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/undo.c | sed -f $(SRCDIR)/VERSION >undo_.c

undo.o:	undo_.c undo.h  $(SRCDIR)/config.h
	$(XTCC) -o undo.o -c undo_.c

undo.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

update_.c:	$(SRCDIR)/update.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/update.c | sed -f $(SRCDIR)/VERSION >update_.c

update.o:	update_.c update.h  $(SRCDIR)/config.h
	$(XTCC) -o update.o -c update_.c

update.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

url_.c:	$(SRCDIR)/url.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/url.c | sed -f $(SRCDIR)/VERSION >url_.c

url.o:	url_.c url.h  $(SRCDIR)/config.h
	$(XTCC) -o url.o -c url_.c

url.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

user_.c:	$(SRCDIR)/user.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/user.c | sed -f $(SRCDIR)/VERSION >user_.c

user.o:	user_.c user.h  $(SRCDIR)/config.h
	$(XTCC) -o user.o -c user_.c

user.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

verify_.c:	$(SRCDIR)/verify.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/verify.c | sed -f $(SRCDIR)/VERSION >verify_.c

verify.o:	verify_.c verify.h  $(SRCDIR)/config.h
	$(XTCC) -o verify.o -c verify_.c

verify.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

vfile_.c:	$(SRCDIR)/vfile.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/vfile.c | sed -f $(SRCDIR)/VERSION >vfile_.c

vfile.o:	vfile_.c vfile.h  $(SRCDIR)/config.h
	$(XTCC) -o vfile.o -c vfile_.c

vfile.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

wiki_.c:	$(SRCDIR)/wiki.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/wiki.c | sed -f $(SRCDIR)/VERSION >wiki_.c

wiki.o:	wiki_.c wiki.h  $(SRCDIR)/config.h
	$(XTCC) -o wiki.o -c wiki_.c

wiki.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

wikiformat_.c:	$(SRCDIR)/wikiformat.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/wikiformat.c | sed -f $(SRCDIR)/VERSION >wikiformat_.c

wikiformat.o:	wikiformat_.c wikiformat.h  $(SRCDIR)/config.h
	$(XTCC) -o wikiformat.o -c wikiformat_.c

wikiformat.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

xfer_.c:	$(SRCDIR)/xfer.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/xfer.c | sed -f $(SRCDIR)/VERSION >xfer_.c

xfer.o:	xfer_.c xfer.h  $(SRCDIR)/config.h
	$(XTCC) -o xfer.o -c xfer_.c

xfer.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

zip_.c:	$(SRCDIR)/zip.c $(SRCDIR)/VERSION translate
	./translate $(SRCDIR)/zip.c | sed -f $(SRCDIR)/VERSION >zip_.c

zip.o:	zip_.c zip.h  $(SRCDIR)/config.h
	$(XTCC) -o zip.o -c zip_.c

zip.h:	makeheaders
	./makeheaders  add_.c:add.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendents_.c:descendents.h diff_.c:diff.h diffcmd_.c:diffcmd.h encode_.c:encode.h file_.c:file.h http_.c:http.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h rss_.c:rss.h schema_.c:schema.h setup_.c:setup.h sha1_.c:sha1.h style_.c:style.h subscript_.c:subscript.h sync_.c:sync.h tag_.c:tag.h timeline_.c:timeline.h tkt_.c:tkt.h tktconfig_.c:tktconfig.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h ./VERSION.h
	touch headers

sqlite3.o:	$(SRCDIR)/sqlite3.c
	$(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_PRIVATE= -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_FTS3=1 -c $(SRCDIR)/sqlite3.c -o sqlite3.o

Changes to src/makemake.tcl.

5
6
7
8
9
10
11

12
13
14
15
16
17
18
..
34
35
36
37
38
39
40

41
42
43
44

45

46



47
48
49
50
51
52
53
...
109
110
111
112
113
114
115
116
117
118





119
120
121
122
123
124
125
...
148
149
150
151
152
153
154
155

156

# Basenames of all source files:
#
set src {
  add
  bag
  blob

  cgi
  checkin
  checkout
  clearsign
  clone
  comformat
  construct
................................................................................
  merge
  merge3
  name
  pivot
  pqueue
  printf
  rebuild

  schema
  setup
  sha1
  style

  sync

  timeline



  undo
  update
  url
  user
  verify
  vfile
  wiki
................................................................................

# WARNING. DANGER. Running the testsuite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(APPNAME)
	$(TCLSH) test/tester.tcl $(APPNAME)

VERSION.h:	$(SRCDIR)/../manifest.uuid
	awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' \
		$(SRCDIR)/../manifest.uuid >VERSION.h






$(APPNAME):	headers $(OBJ) sqlite3.o
	$(TCC) -o $(APPNAME) $(OBJ) sqlite3.o $(LIB)

clean:	
	rm -f *.o *_.c $(APPNAME) VERSION.h
	rm -f translate makeheaders mkindex page_index.h headers}
................................................................................
  puts "$s.h:\tmakeheaders"
  puts "\t./makeheaders $mhargs\n\ttouch headers\n"
}


puts "sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
set opt {-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_PRIVATE=}
append opt " -DTHREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4"

puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o sqlite3.o\n"







>







 







>




>

>

>
>
>







 







|


>
>
>
>
>







 







|
>

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
...
160
161
162
163
164
165
166
167
168
169

# Basenames of all source files:
#
set src {
  add
  bag
  blob
  branch
  cgi
  checkin
  checkout
  clearsign
  clone
  comformat
  construct
................................................................................
  merge
  merge3
  name
  pivot
  pqueue
  printf
  rebuild
  rss
  schema
  setup
  sha1
  style
  subscript
  sync
  tag
  timeline
  tkt
  tktconfig
  tktsetup
  undo
  update
  url
  user
  verify
  vfile
  wiki
................................................................................

# WARNING. DANGER. Running the testsuite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(APPNAME)
	$(TCLSH) test/tester.tcl $(APPNAME)

VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest
	awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' \
		$(SRCDIR)/../manifest.uuid >VERSION.h
	awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' \
		$(SRCDIR)/../manifest.uuid >>VERSION.h
	awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n",\
		substr($$2,1,10),substr($$2,12)}' \
		$(SRCDIR)/../manifest >>VERSION.h

$(APPNAME):	headers $(OBJ) sqlite3.o
	$(TCC) -o $(APPNAME) $(OBJ) sqlite3.o $(LIB)

clean:	
	rm -f *.o *_.c $(APPNAME) VERSION.h
	rm -f translate makeheaders mkindex page_index.h headers}
................................................................................
  puts "$s.h:\tmakeheaders"
  puts "\t./makeheaders $mhargs\n\ttouch headers\n"
}


puts "sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
set opt {-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_PRIVATE=}
append opt " -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4"
append opt " -DSQLITE_ENABLE_FTS3=1"
puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o sqlite3.o\n"

Changes to src/manifest.c.

17
18
19
20
21
22
23
24



25
26
27
28
29
30
















31
32
33
34
35


36

37
38
39



40
41
42
43
44
45
46
47
48
49
50
51




















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



64
65
66
67
68
69
70
71
72
73
74





75
76
77
78
79








80
81
82

83
84


85
86
87



88
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
104
105
106





107
108
109








































































































110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134












135
136
137
138
139

140
141























142
143

















144
145
146
147
148
149














150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167











168
169
170

171
172
173
174



















175
176
177
178
179
180
181
182




















183
184
185
186





187











188
189
190
191












192
193
194

195



196
197



























































198
199
200
201
202
203
204
205
206
207
208




209
210
211

212




























































213
214
215
216

217
218
219
220
221
222
223
...
320
321
322
323
324
325
326

327
328
329
330
331

332
333
334
335
336
337
338

339
340
341
342
343
344
345
346
347
348

349




350




351
352


353
354
355
356
357
358






































































359
360
361
362
363
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to cross link manifests



*/
#include "config.h"
#include "manifest.h"
#include <assert.h>

#if INTERFACE
















/*
** A parsed manifest or cluster.
*/
struct Manifest {
  Blob content;         /* The original content blob */


  char *zComment;       /* Decoded comment */

  double rDate;         /* Time in the "D" line */
  char *zUser;          /* Name of the user */
  char *zRepoCksum;     /* MD5 checksum of the baseline content */



  int nFile;            /* Number of F lines */
  int nFileAlloc;       /* Slots allocated in aFile[] */
  struct { 
    char *zName;           /* Name of a file */
    char *zUuid;           /* UUID of the file */
  } *aFile;
  int nParent;          /* Number of parents */
  int nParentAlloc;     /* Slots allocated in azParent[] */
  char **azParent;      /* UUIDs of parents */
  int nCChild;          /* Number of cluster children */
  int nCChildAlloc;     /* Number of closts allocated in azCChild[] */
  char **azCChild;      /* UUIDs of referenced objects in a cluster */




















};
#endif


/*
** Clear the memory allocated in a manifest object
*/
void manifest_clear(Manifest *p){
  blob_reset(&p->content);
  free(p->aFile);
  free(p->azParent);
  free(p->azCChild);



  memset(p, 0, sizeof(*p));
}

/*
** Parse a manifest blob into a Manifest object.  The Manifest
** object takes over the input blob and will free it when the
** Manifest object is freed.  Zeros are inserted into the blob
** as string terminators so that blob should not be used again.
**
** Return TRUE if the content really is a manifest.  Return FALSE
** if there are syntax errors.





**
** The pContent is reset.  If TRUE is returned, then pContent will
** be reset when the Manifest object is cleared.  If FALSE is
** returned then the Manifest object is cleared automatically
** and pContent is reset before the return.








*/
int manifest_parse(Manifest *p, Blob *pContent){
  int seenHeader = 0;

  int i;
  Blob line, token, a1, a2, a3;



  memset(p, 0, sizeof(*p));
  memcpy(&p->content, pContent, sizeof(p->content));



  blob_zero(pContent);
  pContent = &p->content;

  blob_zero(&a1);
  blob_zero(&a2);
  md5sum_init();
  while( blob_line(pContent, &line) ){
    char *z = blob_buffer(&line);

    if( z[0]=='-' ){
      if( strncmp(z, "-----BEGIN PGP ", 15)!=0 ){
        goto manifest_syntax_error;
      }
      if( seenHeader ){
        break;
      }
      while( blob_line(pContent, &line)>2 ){}
      if( blob_line(pContent, &line)==0 ) break;
      z = blob_buffer(&line);
    }





    seenHeader = 1;
    if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
    if( z[0]=='F' ){








































































































      char *zName, *zUuid;
      md5sum_step_text(blob_buffer(&line), blob_size(&line));
      if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
      zName = blob_terminate(&a1);
      zUuid = blob_terminate(&a2);
      if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
      if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
      defossilize(zName);
      if( !file_is_simple_pathname(zName) ){
        goto manifest_syntax_error;
      }
      if( p->nFile>=p->nFileAlloc ){
        p->nFileAlloc = p->nFileAlloc*2 + 10;
        p->aFile = realloc(p->aFile, p->nFileAlloc*sizeof(p->aFile[0]) );
        if( p->aFile==0 ) fossil_panic("out of memory");
      }
      i = p->nFile++;
      p->aFile[i].zName = zName;
      p->aFile[i].zUuid = zUuid;
      if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
        goto manifest_syntax_error;
      }
    }else if( z[0]=='C' ){












      md5sum_step_text(blob_buffer(&line), blob_size(&line));
      if( p->zComment!=0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
      p->zComment = blob_terminate(&a1);

      defossilize(p->zComment);
    }else if( z[0]=='D' ){























      char *zDate;
      md5sum_step_text(blob_buffer(&line), blob_size(&line));

















      if( p->rDate!=0.0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
      zDate = blob_terminate(&a1);
      p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
    }else if( z[0]=='M' ){














      char *zUuid;
      md5sum_step_text(blob_buffer(&line), blob_size(&line));
      if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
      zUuid = blob_terminate(&a1);
      if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
      if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
      if( p->nCChild>=p->nCChildAlloc ){
        p->nCChildAlloc = p->nCChildAlloc*2 + 10;
        p->azCChild = 
           realloc(p->azCChild, p->nCChildAlloc*sizeof(p->azCChild[0]) );
        if( p->azCChild==0 ) fossil_panic("out of memory");
      }
      i = p->nCChild++;
      p->azCChild[i] = zUuid;
      if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){
        goto manifest_syntax_error;
      }
    }else if( z[0]=='U' ){











      md5sum_step_text(blob_buffer(&line), blob_size(&line));
      if( p->zUser!=0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;

      if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
      p->zUser = blob_terminate(&a1);
      defossilize(p->zUser);
    }else if( z[0]=='R' ){



















      md5sum_step_text(blob_buffer(&line), blob_size(&line));
      if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
      if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
      p->zRepoCksum = blob_terminate(&a1);
      if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
    }else if( z[0]=='P' ){




















      md5sum_step_text(blob_buffer(&line), blob_size(&line));
      while( blob_token(&line, &a1) ){
        char *zUuid;
        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;





        zUuid = blob_terminate(&a1);











        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
        if( p->nParent>=p->nParentAlloc ){
          p->nParentAlloc = p->nParentAlloc*2 + 5;
          p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));












          if( p->azParent==0 ) fossil_panic("out of memory");
        }
        i = p->nParent++;

        p->azParent[i] = zUuid;



      }
    }else if( z[0]=='Z' ){



























































      int rc;
      Blob hash;
      if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
      if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
      if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
      if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
      md5sum_finish(&hash);
      rc = blob_compare(&hash, &a1);
      blob_reset(&hash);
      if( rc!=0 ) goto manifest_syntax_error;
    }else{




      goto manifest_syntax_error;
    }
  }

  if( !seenHeader ) goto manifest_syntax_error;




























































  md5sum_init();
  return 1;

manifest_syntax_error:

  md5sum_init();
  manifest_clear(p);
  return 0;
}

/*
** Add a single entry to the mlink table.  Also add the filename to
................................................................................
** is a cluster then remove all referenced elements from the
** unclustered table and create phantoms for any unknown elements.
*/
int manifest_crosslink(int rid, Blob *pContent){
  int i;
  Manifest m;
  Stmt q;


  if( manifest_parse(&m, pContent)==0 ){
    return 0;
  }
  db_begin_transaction();

  if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
    for(i=0; i<m.nParent; i++){
      int pid = uuid_to_rid(m.azParent[i], 1);
      db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
                    "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
      if( i==0 ){
        add_mlink(pid, 0, rid, &m);

      }
    }
    db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
    while( db_step(&q)==SQLITE_ROW ){
      int cid = db_column_int(&q, 0);
      add_mlink(rid, &m, cid, 0);
    }
    db_finalize(&q);
    db_multi_exec(
      "INSERT INTO event(type,mtime,objid,user,comment)"

      "VALUES('ci',%.17g,%d,%Q,%Q)",




      m.rDate, rid, m.zUser, m.zComment




    );
  }


  for(i=0; i<m.nCChild; i++){
    int rid;
    rid = uuid_to_rid(m.azCChild[i], 1);
    if( rid>0 ){
      db_multi_exec("DELETE FROM unclustered WHERE rid=%d", rid);
    }






































































  }
  db_end_transaction(0);
  manifest_clear(&m);
  return 1;
}







|
>
>
>






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





>
>

>



>
>
>












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












>
>
>




|
|



|
|
>
>
>
>
>





>
>
>
>
>
>
>
>



>
|

>
>



>
>
>








>











>
>
>
>
>


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

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

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




>







 







>





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





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

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

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375


376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

407
408
409
410
411
412
413
414
415
416
417
418

419
420
421
422


423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448

449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470

471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510

511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579

580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
...
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to cross link control files and
** manifests.  The file is named "manifest.c" because it was
** original only used to parse manifests.  Then later clusters
** and control files and wiki pages and tickets were added.
*/
#include "config.h"
#include "manifest.h"
#include <assert.h>

#if INTERFACE
/*
** Types of control files
*/
#define CFTYPE_MANIFEST   1
#define CFTYPE_CLUSTER    2
#define CFTYPE_CONTROL    3
#define CFTYPE_WIKI       4
#define CFTYPE_TICKET     5

/*
** Mode parameter values
*/
#define CFMODE_READ       1
#define CFMODE_APPEND     2
#define CFMODE_WRITE      3

/*
** A parsed manifest or cluster.
*/
struct Manifest {
  Blob content;         /* The original content blob */
  int type;             /* Type of file */
  int mode;             /* Access mode */
  char *zComment;       /* Decoded comment */
  char zUuid[UUID_SIZE+1];  /* Self UUID */
  double rDate;         /* Time in the "D" line */
  char *zUser;          /* Name of the user */
  char *zRepoCksum;     /* MD5 checksum of the baseline content */
  char *zWiki;          /* Text of the wiki page */
  char *zWikiTitle;     /* Name of the wiki page */
  char *zTicketUuid;    /* UUID for a ticket */
  int nFile;            /* Number of F lines */
  int nFileAlloc;       /* Slots allocated in aFile[] */
  struct { 
    char *zName;           /* Name of a file */
    char *zUuid;           /* UUID of the file */
  } *aFile;
  int nParent;          /* Number of parents */
  int nParentAlloc;     /* Slots allocated in azParent[] */
  char **azParent;      /* UUIDs of parents */
  int nCChild;          /* Number of cluster children */
  int nCChildAlloc;     /* Number of closts allocated in azCChild[] */
  char **azCChild;      /* UUIDs of referenced objects in a cluster */
  int nTag;             /* Number of T lines */
  int nTagAlloc;        /* Slots allocated in aTag[] */
  struct { 
    char *zName;           /* Name of the tag */
    char *zUuid;           /* UUID that the tag is applied to */
    char *zValue;          /* Value if the tag is really a property */
  } *aTag;
  int nField;           /* Number of J lines */
  int nFieldAlloc;      /* Slots allocated in aField[] */
  struct { 
    char *zName;           /* Key or field name */
    char *zValue;          /* Value of the field */
  } *aField;
  int nAttach;          /* Number of A lines */
  int nAttachAlloc;     /* Slots allocated in aAttach[] */
  struct { 
    char *zUuid;           /* UUID of the attachment */
    char *zName;           /* Name of the attachment */
    char *zDesc;           /* Description of the attachment */
  } *aAttach;
};
#endif


/*
** Clear the memory allocated in a manifest object
*/
void manifest_clear(Manifest *p){
  blob_reset(&p->content);
  free(p->aFile);
  free(p->azParent);
  free(p->azCChild);
  free(p->aTag);
  free(p->aField);
  free(p->aAttach);
  memset(p, 0, sizeof(*p));
}

/*
** Parse a blob into a Manifest object.  The Manifest object
** takes over the input blob and will free it when the
** Manifest object is freed.  Zeros are inserted into the blob
** as string terminators so that blob should not be used again.
**
** Return TRUE if the content really is a control file of some
** kind.  Return FALSE if there are syntax errors.
**
** This routine is strict about the format of a control file.
** The format must match exactly or else it is rejected.  This
** rule minimizes the risk that a content file will be mistaken
** for a control file simply because they look the same.
**
** The pContent is reset.  If TRUE is returned, then pContent will
** be reset when the Manifest object is cleared.  If FALSE is
** returned then the Manifest object is cleared automatically
** and pContent is reset before the return.
**
** The entire file can be PGP clear-signed.  The signature is ignored.
** The file consists of zero or more cards, one card per line.
** (Except: the content of the W card can extend of multiple lines.)
** Each card is divided into tokens by a single space character.
** The first token is a single upper-case letter which is the card type.
** The card type determines the other parameters to the card.
** Cards must occur in lexicographical order.
*/
int manifest_parse(Manifest *p, Blob *pContent){
  int seenHeader = 0;
  int seenZ = 0;
  int i, lineNo=0;
  Blob line, token, a1, a2, a3;
  Blob selfuuid;
  char cPrevType = 0;

  memset(p, 0, sizeof(*p));
  memcpy(&p->content, pContent, sizeof(p->content));
  sha1sum_blob(&p->content, &selfuuid);
  memcpy(p->zUuid, blob_buffer(&selfuuid), UUID_SIZE);
  p->zUuid[UUID_SIZE] = 0;
  blob_zero(pContent);
  pContent = &p->content;

  blob_zero(&a1);
  blob_zero(&a2);
  md5sum_init();
  while( blob_line(pContent, &line) ){
    char *z = blob_buffer(&line);
    lineNo++;
    if( z[0]=='-' ){
      if( strncmp(z, "-----BEGIN PGP ", 15)!=0 ){
        goto manifest_syntax_error;
      }
      if( seenHeader ){
        break;
      }
      while( blob_line(pContent, &line)>2 ){}
      if( blob_line(pContent, &line)==0 ) break;
      z = blob_buffer(&line);
    }
    if( z[0]<cPrevType ){
      /* Lines of a manifest must occur in lexicographical order */
      goto manifest_syntax_error;
    }
    cPrevType = z[0];
    seenHeader = 1;
    if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
    switch( z[0] ){
      /*
      **     A <uuid> <filename> <description>
      **
      ** Identifies an attachment to either a wiki page or a ticket.
      ** <uuid> is the artifact that is the attachment.
      */
      case 'A': {
        char *zName, *zUuid, *zDesc;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a3)==0 ) goto manifest_syntax_error;
        zUuid = blob_terminate(&a1);
        zName = blob_terminate(&a2);
        zDesc = blob_terminate(&a3);
        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
        defossilize(zName);
        if( !file_is_simple_pathname(zName) ){
          goto manifest_syntax_error;
        }
        defossilize(zDesc);
        if( p->nAttach>=p->nAttachAlloc ){
          p->nAttachAlloc = p->nAttachAlloc*2 + 10;
          p->aAttach = realloc(p->aAttach,
                               p->nAttachAlloc*sizeof(p->aAttach[0]) );
          if( p->aAttach==0 ) fossil_panic("out of memory");
        }
        i = p->nAttach++;
        p->aAttach[i].zUuid = zUuid;
        p->aAttach[i].zName = zName;
        p->aAttach[i].zDesc = zDesc;
        if( i>0 && strcmp(p->aAttach[i-1].zUuid, zUuid)>=0 ){
          goto manifest_syntax_error;
        }
        break;
      }

      /*
      **     C <comment>
      **
      ** Comment text is fossil-encoded.  There may be no more than
      ** one C line.  C lines are required for manifests and are
      ** disallowed on all other control files.
      */
      case 'C': {
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( p->zComment!=0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        p->zComment = blob_terminate(&a1);
        defossilize(p->zComment);
        break;
      }

      /*
      **     D <timestamp>
      **
      ** The timestamp should be ISO 8601.   YYYY-MM-DDtHH:MM:SS
      ** There can be no more than 1 D line.  D lines are required
      ** for all control files except for clusters.
      */
      case 'D': {
        char *zDate;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( p->rDate!=0.0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        zDate = blob_terminate(&a1);
        p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
        break;
      }

      /*
      **     E <mode>
      **
      ** Access mode.  <mode> can be one of "read", "append",
      ** or "write".
      */
      case 'E': {
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( p->mode!=0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        if( blob_eq(&a1, "write") ){
          p->mode = CFMODE_WRITE;
        }else if( blob_eq(&a1, "append") ){
          p->mode = CFMODE_APPEND;
        }else if( blob_eq(&a1, "read") ){
          p->mode = CFMODE_READ;
        }else{
          goto manifest_syntax_error;
        }
        break;
      }

      /*
      **     F <filename> <uuid>
      **
      ** Identifies a file in a manifest.  Multiple F lines are
      ** allowed in a manifest.  F lines are not allowed in any
      ** other control file.  The filename is fossil-encoded.
      */
      case 'F': {
        char *zName, *zUuid;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
        zName = blob_terminate(&a1);
        zUuid = blob_terminate(&a2);
        if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
        defossilize(zName);
        if( !file_is_simple_pathname(zName) ){
          goto manifest_syntax_error;
        }
        if( p->nFile>=p->nFileAlloc ){
          p->nFileAlloc = p->nFileAlloc*2 + 10;
          p->aFile = realloc(p->aFile, p->nFileAlloc*sizeof(p->aFile[0]) );
          if( p->aFile==0 ) fossil_panic("out of memory");
        }
        i = p->nFile++;
        p->aFile[i].zName = zName;
        p->aFile[i].zUuid = zUuid;
        if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
          goto manifest_syntax_error;
        }

        break;
      }

      /*
      **     J '+'?<name> <value>
      **
      ** Specifies a name value pair for ticket.  If the first character
      ** of <name> is "+" then the value is appended to any preexisting
      ** value.
      */
      case 'J': {
        char *zName, *zValue;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
        zName = blob_terminate(&a1);
        zValue = blob_terminate(&a2);
        defossilize(zValue);

        if( p->nField>=p->nFieldAlloc ){
          p->nFieldAlloc = p->nFieldAlloc*2 + 10;
          p->aField = realloc(p->aField,
                               p->nFieldAlloc*sizeof(p->aField[0]) );
          if( p->aField==0 ) fossil_panic("out of memory");
        }
        i = p->nField++;
        p->aField[i].zName = zName;
        p->aField[i].zValue = zValue;
        if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){
          goto manifest_syntax_error;
        }
        break;
      }


      /*
      **    K <uuid>
      **
      ** A K-line gives the UUID for the ticket which this control file
      ** is amending.
      */
      case 'K': {
        char *zUuid;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        zUuid = blob_terminate(&a1);
        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
        if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
        p->zTicketUuid = zUuid;
        break;
      }

      /*
      **     L <wikititle>
      **
      ** The wiki page title is fossil-encoded.  There may be no more than
      ** one L line.
      */
      case 'L': {
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        p->zWikiTitle = blob_terminate(&a1);


        defossilize(p->zWikiTitle);
        if( !wiki_name_is_wellformed(p->zWikiTitle) ){
          goto manifest_syntax_error;
        }
        break;
      }

      /*
      **    M <uuid>
      **
      ** An M-line identifies another artifact by its UUID.  M-lines
      ** occur in clusters only.
      */
      case 'M': {
        char *zUuid;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        zUuid = blob_terminate(&a1);
        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
        if( p->nCChild>=p->nCChildAlloc ){
          p->nCChildAlloc = p->nCChildAlloc*2 + 10;
          p->azCChild = 
             realloc(p->azCChild, p->nCChildAlloc*sizeof(p->azCChild[0]) );
          if( p->azCChild==0 ) fossil_panic("out of memory");
        }
        i = p->nCChild++;
        p->azCChild[i] = zUuid;
        if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){
          goto manifest_syntax_error;
        }

        break;
      }

      /*
      **     P <uuid> ...
      **
      ** Specify one or more other artifacts where are the parents of
      ** this artifact.  The first parent is the primary parent.  All
      ** others are parents by merge.
      */
      case 'P': {
        md5sum_step_text(blob_buffer(&line), blob_size(&line));

        while( blob_token(&line, &a1) ){
          char *zUuid;
          if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
          zUuid = blob_terminate(&a1);


          if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
          if( p->nParent>=p->nParentAlloc ){
            p->nParentAlloc = p->nParentAlloc*2 + 5;
            p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));
            if( p->azParent==0 ) fossil_panic("out of memory");
          }
          i = p->nParent++;
          p->azParent[i] = zUuid;
        }
        break;
      }

      /*
      **     R <md5sum>
      **
      ** Specify the MD5 checksum of the entire baseline in a
      ** manifest.
      */
      case 'R': {
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
        p->zRepoCksum = blob_terminate(&a1);
        if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;

        break;
      }

      /*
      **    T (+|*|-)<tagname> <uuid> ?<value>?
      **
      ** Create or cancel a tag or property.  The tagname is fossil-encoded.
      ** The first character of the name must be either "+" to create a
      ** singleton tag, "*" to create a propagating tag, or "-" to create
      ** anti-tag that undoes a prior "+" or blocks propagation of of
      ** a "*".
      **
      ** The tag is applied to <uuid>.  If <uuid> is "*" then the tag is
      ** applied to the current manifest.  If <value> is provided then 
      ** the tag is really a property with the given value.
      **
      ** Tags are not allowed in clusters.  Multiple T lines are allowed.
      */
      case 'T': {
        char *zName, *zUuid, *zValue;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ){

          goto manifest_syntax_error;
        }
        if( blob_token(&line, &a2)==0 ){
          goto manifest_syntax_error;
        }
        zName = blob_terminate(&a1);
        zUuid = blob_terminate(&a2);
        if( blob_token(&line, &a3)==0 ){
          zValue = 0;
        }else{
          zValue = blob_terminate(&a3);
          defossilize(zValue);
        }
        if( blob_size(&a2)==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
          /* A valid uuid */
        }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){
          zUuid = p->zUuid;
        }else{
          goto manifest_syntax_error;



        }
        defossilize(zName);
        if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
          goto manifest_syntax_error;
        }
        if( validate16(&zName[1], strlen(&zName[1])) ){
          /* Do not allow tags whose names look like UUIDs */
          goto manifest_syntax_error;
        }
        if( p->nTag>=p->nTagAlloc ){
          p->nTagAlloc = p->nTagAlloc*2 + 10;
          p->aTag = realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) );
          if( p->aTag==0 ) fossil_panic("out of memory");
        }
        i = p->nTag++;
        p->aTag[i].zName = zName;
        p->aTag[i].zUuid = zUuid;
        p->aTag[i].zValue = zValue;
        if( i>0 && strcmp(p->aTag[i-1].zName, zName)>=0 ){
          goto manifest_syntax_error;
        }

        break;
      }

      /*
      **     U <login>
      **
      ** Identify the user who created this control file by their
      ** login.  Only one U line is allowed.  Prohibited in clusters.
      */
      case 'U': {
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( p->zUser!=0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        p->zUser = blob_terminate(&a1);
        defossilize(p->zUser);
        break;
      }

      /*
      **     W <size>
      **
      ** The next <size> bytes of the file contain the text of the wiki
      ** page.  There is always an extra \n before the start of the next
      ** record.
      */
      case 'W': {
        int size;
        Blob wiki;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error;
        if( size<0 ) goto manifest_syntax_error;
        if( p->zWiki!=0 ) goto manifest_syntax_error;
        blob_zero(&wiki);
        if( blob_extract(pContent, size+1, &wiki)!=size+1 ){
          goto manifest_syntax_error;
        }
        p->zWiki = blob_buffer(&wiki);
        md5sum_step_text(p->zWiki, size+1);
        if( p->zWiki[size]!='\n' ) goto manifest_syntax_error;
        p->zWiki[size] = 0;
        break;
      }


      /*
      **     Z <md5sum>
      **
      ** MD5 checksum on this control file.  The checksum is over all
      ** lines (other than PGP-signature lines) prior to the current
      ** line.  This must be the last record.
      **
      ** This card is required for all control file types except for
      ** Manifest.  It is not required for manifest only for historical
      ** compatibility reasons.
      */
      case 'Z': {
        int rc;
        Blob hash;
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
        if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
        md5sum_finish(&hash);
        rc = blob_compare(&hash, &a1);
        blob_reset(&hash);
        if( rc!=0 ) goto manifest_syntax_error;

        seenZ = 1;
        break;
      }
      default: {
        goto manifest_syntax_error;
      }
    }
  }
  if( !seenHeader ) goto manifest_syntax_error;

  if( p->nFile>0 ){
    if( p->nCChild>0 ) goto manifest_syntax_error;
    if( p->rDate==0.0 ) goto manifest_syntax_error;
    if( p->nField>0 ) goto manifest_syntax_error;
    if( p->zTicketUuid ) goto manifest_syntax_error;
    if( p->nAttach>0 ) goto manifest_syntax_error;
    if( p->zWiki ) goto manifest_syntax_error;
    if( p->zWikiTitle ) goto manifest_syntax_error;
    if( p->zTicketUuid ) goto manifest_syntax_error;
    p->type = CFTYPE_MANIFEST;
  }else if( p->nCChild>0 ){
    if( p->rDate>0.0 ) goto manifest_syntax_error;
    if( p->zComment!=0 ) goto manifest_syntax_error;
    if( p->zUser!=0 ) goto manifest_syntax_error;
    if( p->nTag>0 ) goto manifest_syntax_error;
    if( p->nParent>0 ) goto manifest_syntax_error;
    if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
    if( p->nField>0 ) goto manifest_syntax_error;
    if( p->zTicketUuid ) goto manifest_syntax_error;
    if( p->nAttach>0 ) goto manifest_syntax_error;
    if( p->zWiki ) goto manifest_syntax_error;
    if( p->zWikiTitle ) goto manifest_syntax_error;
    if( !seenZ ) goto manifest_syntax_error;
    p->type = CFTYPE_CLUSTER;
  }else if( p->nField>0 ){
    if( p->rDate==0.0 ) goto manifest_syntax_error;
    if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
    if( p->zWiki ) goto manifest_syntax_error;
    if( p->zWikiTitle ) goto manifest_syntax_error;
    if( p->nCChild>0 ) goto manifest_syntax_error;
    if( p->nTag>0 ) goto manifest_syntax_error;
    if( p->zTicketUuid==0 ) goto manifest_syntax_error;
    if( p->zUser==0 ) goto manifest_syntax_error;
    if( !seenZ ) goto manifest_syntax_error;
    p->type = CFTYPE_TICKET;
  }else if( p->zWiki!=0 ){
    if( p->rDate==0.0 ) goto manifest_syntax_error;
    if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
    if( p->nCChild>0 ) goto manifest_syntax_error;
    if( p->nTag>0 ) goto manifest_syntax_error;
    if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
    if( p->zWikiTitle==0 ) goto manifest_syntax_error;
    if( !seenZ ) goto manifest_syntax_error;
    p->type = CFTYPE_WIKI;
  }else if( p->nTag>0 ){
    if( p->rDate<=0.0 ) goto manifest_syntax_error;
    if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
    if( p->nParent>0 ) goto manifest_syntax_error;
    if( p->nAttach>0 ) goto manifest_syntax_error;
    if( p->nField>0 ) goto manifest_syntax_error;
    if( p->zWiki ) goto manifest_syntax_error;
    if( p->zWikiTitle ) goto manifest_syntax_error;
    if( p->zTicketUuid ) goto manifest_syntax_error;
    if( !seenZ ) goto manifest_syntax_error;
    p->type = CFTYPE_CONTROL;
  }else{
    goto manifest_syntax_error;
  }
    
  md5sum_init();
  return 1;

manifest_syntax_error:
  /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
  md5sum_init();
  manifest_clear(p);
  return 0;
}

/*
** Add a single entry to the mlink table.  Also add the filename to
................................................................................
** is a cluster then remove all referenced elements from the
** unclustered table and create phantoms for any unknown elements.
*/
int manifest_crosslink(int rid, Blob *pContent){
  int i;
  Manifest m;
  Stmt q;
  int parentid = 0;

  if( manifest_parse(&m, pContent)==0 ){
    return 0;
  }
  db_begin_transaction();
  if( m.type==CFTYPE_MANIFEST ){
    if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
      for(i=0; i<m.nParent; i++){
        int pid = uuid_to_rid(m.azParent[i], 1);
        db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
                      "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
        if( i==0 ){
          add_mlink(pid, 0, rid, &m);
          parentid = pid;
        }
      }
      db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
      while( db_step(&q)==SQLITE_ROW ){
        int cid = db_column_int(&q, 0);
        add_mlink(rid, &m, cid, 0);
      }
      db_finalize(&q);
      db_multi_exec(
        "INSERT INTO event(type,mtime,objid,user,comment,"
        "                  bgcolor,brbgcolor,euser,ecomment)"
        "VALUES('ci',%.17g,%d,%Q,%Q,"
        " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype=1),"
        "(SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype!=1),"
        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
        m.rDate, rid, m.zUser, m.zComment, 
        TAG_BGCOLOR, rid,
        TAG_BGCOLOR, rid,
        TAG_USER, rid,
        TAG_COMMENT, rid
      );
    }
  }
  if( m.type==CFTYPE_CLUSTER ){
    for(i=0; i<m.nCChild; i++){
      int mid;
      mid = uuid_to_rid(m.azCChild[i], 1);
      if( mid>0 ){
        db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid);
      }
    }
  }
  if( m.type==CFTYPE_CONTROL || m.type==CFTYPE_MANIFEST ){
    for(i=0; i<m.nTag; i++){
      int tid;
      int type;
      tid = uuid_to_rid(m.aTag[i].zUuid, 1);
      switch( m.aTag[i].zName[0] ){
        case '+':  type = 1; break;
        case '*':  type = 2; break;
        case '-':  type = 0; break;
        default:
          fossil_fatal("unknown tag type in manifest: %s", m.aTag);
          return 0;
      }
      tag_insert(&m.aTag[i].zName[1], type, m.aTag[i].zValue, 
                 rid, m.rDate, tid);
    }
    if( parentid ){
      tag_propagate_all(parentid);
    }
  }
  if( m.type==CFTYPE_WIKI ){
    char *zTag = mprintf("wiki-%s", m.zWikiTitle);
    int tagid = tag_findid(zTag, 1);
    int prior;
    char *zComment;
    tag_insert(zTag, 1, 0, rid, m.rDate, rid);
    free(zTag);
    prior = db_int(0,
      "SELECT rid FROM tagxref"
      " WHERE tagid=%d AND mtime<%.17g"
      " ORDER BY mtime DESC",
      tagid, m.rDate
    );
    if( prior ){
      content_deltify(prior, rid, 0);
    }
    zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
    db_multi_exec(
      "INSERT INTO event(type,mtime,objid,user,comment,"
      "                  bgcolor,brbgcolor,euser,ecomment)"
      "VALUES('w',%.17g,%d,%Q,%Q,"
      " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype=1),"
      "(SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype!=1),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
      m.rDate, rid, m.zUser, zComment, 
      TAG_BGCOLOR, rid,
      TAG_BGCOLOR, rid,
      TAG_USER, rid,
      TAG_COMMENT, rid
    );
    free(zComment);
  }
  if( m.type==CFTYPE_TICKET ){
    char *zTag;
    char *zComment;

    ticket_insert(&m, 1, 1);
    zTag = mprintf("tkt-%s", m.zTicketUuid);
    tag_insert(zTag, 1, 0, rid, m.rDate, rid);
    free(zTag);
    zComment = mprintf("Changes to ticket [%.10s]", m.zTicketUuid);
    db_multi_exec(
      "INSERT INTO event(type,mtime,objid,user,comment)"
      "VALUES('t',%.17g,%d,%Q,%Q)",
      m.rDate, rid, m.zUser, zComment
    );
    free(zComment);
  }
  db_end_transaction(0);
  manifest_clear(&m);
  return 1;
}

Changes to src/merge.c.

42
43
44
45
46
47
48

49

50
51
52
53
54
55
56
...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227


228
229
230
231



232

233
234
235
236
237
238
239
240

241







242
243
244
245
246
247
248
** names might have been changed in the branch being merged in.
*/
void merge_cmd(void){
  int vid;              /* Current version */
  int mid;              /* Version we are merging against */
  int pid;              /* The pivot version - most recent common ancestor */
  Stmt q;



  if( g.argc!=3 ){
    usage("VERSION");
  }
  db_must_be_within_tree();
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    fossil_panic("nothing is checked out");
................................................................................
  }
  db_finalize(&q);

  /*
  ** Do a three-way merge on files that have changes pid->mid and pid->vid
  */
  db_prepare(&q,
    "SELECT ridm, idv, ridp FROM fv"
    " WHERE idp>0 AND idv>0 AND idm>0"
    "   AND ridm!=ridp AND (ridv!=ridp OR chnged)"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int ridm = db_column_int(&q, 0);
    int idv = db_column_int(&q, 1);
    int ridp = db_column_int(&q, 2);


    char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
    char *zFullPath;
    Blob m, p, v, r;
    /* Do a 3-way merge of idp->idm into idp->idv.  The results go into idv. */



    printf("MERGE %s\n", zName);

    undo_save(zName);
    zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
    free(zName);
    content_get(ridp, &p);
    content_get(ridm, &m);
    blob_zero(&v);
    blob_read_from_file(&v, zFullPath);
    blob_merge(&p, &m, &v, &r);

    blob_write_to_file(&r, zFullPath);







    blob_reset(&p);
    blob_reset(&m);
    blob_reset(&v);
    blob_reset(&r);
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
                  idv,ridm);
  }







>

>







 







|







>
>




>
>
>
|
>


<




|
>
|
>
>
>
>
>
>
>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
** names might have been changed in the branch being merged in.
*/
void merge_cmd(void){
  int vid;              /* Current version */
  int mid;              /* Version we are merging against */
  int pid;              /* The pivot version - most recent common ancestor */
  Stmt q;
  int detailFlag;

  detailFlag = find_option("detail",0,0)!=0;
  if( g.argc!=3 ){
    usage("VERSION");
  }
  db_must_be_within_tree();
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    fossil_panic("nothing is checked out");
................................................................................
  }
  db_finalize(&q);

  /*
  ** Do a three-way merge on files that have changes pid->mid and pid->vid
  */
  db_prepare(&q,
    "SELECT ridm, idv, ridp, ridv FROM fv"
    " WHERE idp>0 AND idv>0 AND idm>0"
    "   AND ridm!=ridp AND (ridv!=ridp OR chnged)"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int ridm = db_column_int(&q, 0);
    int idv = db_column_int(&q, 1);
    int ridp = db_column_int(&q, 2);
    int ridv = db_column_int(&q, 3);
    int rc;
    char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
    char *zFullPath;
    Blob m, p, v, r;
    /* Do a 3-way merge of idp->idm into idp->idv.  The results go into idv. */
    if( detailFlag ){
      printf("MERGE %s  (pivot=%d v1=%d v2=%d)\n", zName, ridp, ridm, ridv);
    }else{
      printf("MERGE %s\n", zName);
    }
    undo_save(zName);
    zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);

    content_get(ridp, &p);
    content_get(ridm, &m);
    blob_zero(&v);
    blob_read_from_file(&v, zFullPath);
    rc = blob_merge(&p, &m, &v, &r);
    if( rc>=0 ){
      blob_write_to_file(&r, zFullPath);
      if( rc>0 ){
        printf("***** %d merge conflicts in %s\n", rc, zName);
      }
    }else{
      printf("***** Cannot merge binary file %s\n", zName);
    }
    free(zName);
    blob_reset(&p);
    blob_reset(&m);
    blob_reset(&v);
    blob_reset(&r);
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
                  idv,ridm);
  }

Changes to src/merge3.c.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
...
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
**
** This module implements a 3-way merge
*/
#include "config.h"
#include "merge3.h"

#if 0
# define DEBUG1(X) X
#else
# define DEBUG1(X)
#endif
#if 0
#define DEBUG2(X) X
/*
** For debugging:
** Print 16 characters of text from zBuf
*/
static const char *print16(const char *z){
  int i;
  static char zBuf[20];
  for(i=0; i<16; i++){
    if( z[i]>=0x20 && z[i]<=0x7e ){
      zBuf[i] = z[i];
    }else{
      zBuf[i] = '.';
    }
  }
  zBuf[i] = 0;
  return zBuf;
}
#else
# define DEBUG2(X)
#endif

/*
** Must be a 32-bit integer
*/
typedef unsigned int u32;

/*
** Must be a 16-bit value 
*/
typedef unsigned short int u16;

/*
** The width of a hash window in bytes.  The algorithm only works if this
** is a power of 2.
*/
#define NHASH 16

/*
** The current state of the rolling hash.
**
** z[] holds the values that have been hashed.  z[] is a circular buffer.
** z[i] is the first entry and z[(i+NHASH-1)%NHASH] is the last entry of 
** the window.
**
** Hash.a is the sum of all elements of hash.z[].  Hash.b is a weighted
** sum.  Hash.b is z[i]*NHASH + z[i+1]*(NHASH-1) + ... + z[i+NHASH-1]*1.
** (Each index for z[] should be module NHASH, of course.  The %NHASH operator
** is omitted in the prior expression for brevity.)
*/
typedef struct hash hash;
struct hash {
  u16 a, b;         /* Hash values */
  u16 i;            /* Start of the hash window */
  char z[NHASH];    /* The values that have been hashed */
};

/*
** Initialize the rolling hash using the first NHASH characters of z[]
*/
static void hash_init(hash *pHash, const char *z){
  u16 a, b, i;
  a = b = 0;
  for(i=0; i<NHASH; i++){
    a += z[i];
    b += (NHASH-i)*z[i];
    pHash->z[i] = z[i];
  }
  pHash->a = a & 0xffff;
  pHash->b = b & 0xffff;
  pHash->i = 0;
}

/*
** Advance the rolling hash by a single character "c"
*/
static void hash_next(hash *pHash, int c){
  u16 old = pHash->z[pHash->i];
  pHash->z[pHash->i] = c;
  pHash->i = (pHash->i+1)&(NHASH-1);
  pHash->a = pHash->a - old + c;
  pHash->b = pHash->b - NHASH*old + pHash->a;
}

/*
** Return a 32-bit hash value
*/
static u32 hash_32bit(hash *pHash){
  return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16);
}

/*
** Maximum number of landmarks to set in the source file.
*/
#define MX_LANDMARK (1024*128)

/*
** A mapping structure is used to record which parts of two
** files contain the same text.  There are zero or more mapping
** entries in a mapping.  Each entry maps a segment of text in
** the source file into a segment of the output file.
**
**     fromFirst...fromLast ->  toFirst...toLast
**
** Extra text might be inserted in the output file after a 
** mapping.  The nExtra parameter records the number of bytes
** of extra text to insert.
*/
typedef struct Mapping Mapping;
struct Mapping {
  int nMap;
  int nUsed;
  struct Mapping_entry {
    int fromFirst, fromLast;
    int toFirst, toLast;
    int nExtra;
  } *aMap;
};

/*
** Free malloced memory associated with a mapping.
*/
static void MappingClear(Mapping *p){
  free(p->aMap);
  memset(p, 0, sizeof(*p));
}

/*
** Add an entry to a mapping structure.  The mapping is:
**
**     a...b  ->   c...d
**
** The nExtra parameter is initially zero.  It will be changed
** later if necessary.
*/
static void MappingInsert(Mapping *p, int a, int b, int c, int d){
  struct Mapping_entry *pEntry;
  int i;
  for(i=0, pEntry=p->aMap; i<p->nUsed; i++, pEntry++){
    if( pEntry->fromFirst==a && pEntry->fromLast==b && pEntry->toFirst==c ){
      DEBUG2( printf("DUPLICATE: %6d..%-6d %6d..%d\n", a, b, c, d); )
      return;
    }
  }
  if( p->nUsed>=p->nMap ){
    p->nMap = p->nMap * 2 + 10;
    p->aMap = realloc(p->aMap, p->nMap*sizeof(p->aMap[0]) );
    if( p->aMap==0 ) exit(1);
  }
  pEntry = &p->aMap[p->nUsed];
  pEntry->fromFirst = a;
  pEntry->fromLast = b;
  pEntry->toFirst = c;
  pEntry->toLast = d;
  pEntry->nExtra = 0;
  p->nUsed++;
}

DEBUG1(
/*
** For debugging purposes:
** Print the content of a mapping.
*/
static void MappingPrint(Mapping *pMap){
  int i;
  struct Mapping_entry *p;
  for(i=0, p=pMap->aMap; i<pMap->nUsed; i++, p++){
    printf("%6d..%-6d %6d..%-6d  %d\n",
       p->fromFirst, p->fromLast,
       p->toFirst, p->toLast, p->nExtra);
  }
}
)

/*
** Remove deleted entries from a mapping.  Deleted enties have 
** an fromFirst of less than 0.
*/
static void MappingPurgeDeletedEntries(Mapping *p){
  int i, j;
  for(i=j=0; i<p->nUsed; i++){
    if( p->aMap[i].fromFirst<0 ) continue;
    if( j<i ){
      p->aMap[j] = p->aMap[i];
    }
    j++;
  }
  p->nUsed = j;
}

/*
** Comparisons functions used for sorting elements of a Mapping
*/
static int intAbs(int x){ return x<0 ? -x : x; }
static int compareSize(const void *a, const void *b){
  const struct Mapping_entry *A = (const struct Mapping_entry*)a;
  const struct Mapping_entry *B = (const struct Mapping_entry*)b;
  int rc;
  rc = (B->fromLast - B->fromFirst) - (A->fromLast - A->fromFirst);
  if( rc==0 ){
    rc = intAbs(A->toFirst - A->fromFirst) -
              intAbs(B->toFirst - B->fromFirst);
  }
  return rc;
}
static int compareFrom(const void *a, const void *b){
  const struct Mapping_entry *A = (const struct Mapping_entry*)a;
  const struct Mapping_entry *B = (const struct Mapping_entry*)b;
  return A->fromFirst - B->fromFirst;
}
static int compareTo(const void *a, const void *b){
  const struct Mapping_entry *A = (const struct Mapping_entry*)a;
  const struct Mapping_entry *B = (const struct Mapping_entry*)b;
  return A->toFirst - B->toFirst;
}

/*
** Routines for sorting the entries of a mapping.  SortSize sorts
** the entries in order of decreasing size (largest first.)  
** SortFrom and SortTo sort the entries in order of increasing
** fromFirst and toFirst.
*/
static void MappingSortSize(Mapping *p){
  qsort(p->aMap, p->nUsed, sizeof(p->aMap[0]), compareSize);
}
static void MappingSortFrom(Mapping *p){
  qsort(p->aMap, p->nUsed, sizeof(p->aMap[0]), compareFrom);
}
static void MappingSortTo(Mapping *p){
  qsort(p->aMap, p->nUsed, sizeof(p->aMap[0]), compareTo);
}

/*
** Initialize pMap to contain a set of similarities between two files.
*/
static void MappingInit(
  const char *zSrc,      /* The source or pattern file */
  int lenSrc,            /* Length of the source file */
  const char *zOut,      /* The target file */
  int lenOut,            /* Length of the target file */
  Mapping *pMap          /* Write the map of similaries here */
){
  int i, j, base, prefix;
  hash h;
  int *collide;
  int origLenOut = lenOut;
  struct Mapping_entry *aMap;
  int landmark[MX_LANDMARK];

  /*
  ** Initialize the map
  */
  memset(pMap, 0, sizeof(*pMap));

  /*
  ** Find common prefix and suffix
  */
  if( lenSrc<=NHASH || lenOut<=NHASH ){
    MappingInsert(pMap, 0, 0, 0, 0);
    goto add_nextra;
  }
  for(i=0; i<lenSrc && i<lenOut && zSrc[i]==zOut[i]; i++){}
  if( i>=NHASH ){
    MappingInsert(pMap, 0, i-1, 0, i-1);
    lenSrc -= i;
    zSrc += i;
    lenOut -= i;
    zOut += i;
    if( lenSrc<=0 || lenOut<=0 ) goto add_nextra;
    prefix = i;
  }else{
    prefix = 0;
  }
  for(i=1; i<=lenSrc && i<=lenOut && zSrc[lenSrc-i]==zOut[lenOut-i]; i++){}
  if( i>NHASH ){
    MappingInsert(pMap, prefix+lenSrc-i+1, prefix+lenSrc-1,
                        prefix+lenOut-i+1, prefix+lenOut-1);
    lenSrc -= i;
    lenOut -= i;
  }

  /* If the source file is very small, it means that we have no
  ** chance of ever finding any matches.  We can leave early.
  */
  if( lenSrc<=NHASH ) goto add_nextra;

  /* Compute the hash table used to locate matching sections in the
  ** source file.
  */
  collide = malloc( lenSrc*sizeof(int)/NHASH );
  if( collide==0 ) return;
  memset(landmark, -1, sizeof(landmark));
  memset(collide, -1, lenSrc*sizeof(int)/NHASH );
  for(i=0; i<lenSrc-NHASH; i+=NHASH){
    int hv;
    hash_init(&h, &zSrc[i]);
    hv = hash_32bit(&h) & (MX_LANDMARK-1);
    collide[i/NHASH] = landmark[hv];
    landmark[hv] = i/NHASH;
  }

  /* Begin scanning the target file and generating mappings.  In this
  ** step, we generate as many mapping entries is we can.  Many of these
  ** entries might overlap.  The overlapping entries are removed by
  ** the loop the follows.
  */
  base = 0;    /* We have already checked everything before zOut[base] */
  while( base<lenOut-NHASH ){
    int iSrc, iBlock, nextBase, nextBaseI;
    hash_init(&h, &zOut[base]);
    i = 0;     /* Trying to match a landmark against zOut[base+i] */
    nextBaseI = NHASH;
    nextBase = base;
    while(1){
      int hv;

      hv = hash_32bit(&h) & (MX_LANDMARK-1);
      DEBUG2( printf("LOOKING: %d+%d+%d=%d [%s]\n",
              prefix,base,i,prefix+base+i, print16(&zOut[base+i])); )
      iBlock = landmark[hv];
      while( iBlock>=0 ){
        /*
        ** The hash window has identified a potential match against 
        ** landmark block iBlock.  But we need to investigate further.
        ** 
        ** Look for a region in zOut that matches zSrc. Anchor the search
        ** at zSrc[iSrc] and zOut[base+i].
        **
        ** Set cnt equal to the length of the match and set ofst so that
        ** zSrc[ofst] is the first element of the match. 
        */
        int cnt, ofstSrc;
        int j, k, x, y;

        /* Beginning at iSrc, match forwards as far as we can.  j counts
        ** the number of characters that match */
        iSrc = iBlock*NHASH;
        for(j=0, x=iSrc, y=base+i; x<lenSrc && y<lenOut; j++, x++, y++){
          if( zSrc[x]!=zOut[y] ) break;
        }
        j--;

        /* Beginning at iSrc-1, match backwards as far as we can.  k counts
        ** the number of characters that match */
        for(k=1; k<iSrc && k<=base+i; k++){
          if( zSrc[iSrc-k]!=zOut[base+i-k] ) break;
        }
        k--;

        /* Compute the offset and size of the matching region zSrc */
        ofstSrc = iSrc-k;
        cnt = j+k+1;
        DEBUG2( printf("MATCH %d bytes at SRC[%d..%d]: [%s]\n",
                 cnt, ofstSrc, ofstSrc+cnt-1, print16(&zSrc[ofstSrc])); )
        if( cnt>NHASH ){
          int ofstOut = base+i-k;
          DEBUG2( printf("COPY %6d..%-6d %6d..%d\n",
            prefix+ofstSrc, prefix+ofstSrc+cnt-1,
            prefix+ofstOut, prefix+ofstOut+cnt-1); )
          MappingInsert(pMap,
            prefix+ofstSrc, prefix+ofstSrc+cnt-1,
            prefix+ofstOut, prefix+ofstOut+cnt-1);
          if( nextBase < ofstOut+cnt-1 ){
            nextBase = ofstOut+cnt-1;
            nextBaseI = i+NHASH;
          }
        }

        /* Check the next matching block */
        iBlock = collide[iBlock];
      }

      /* If we found a match, then jump out to the outer loop and begin
      ** a new cycle.
      */
      if( nextBase>base && i>=nextBaseI ){
        base = nextBase;
        break;
      }

      /* Advance the hash by one character.  Keep looking for a match */
      if( base+i+NHASH>=lenOut ){
        base = lenOut;
        break;
      }
      hash_next(&h, zOut[base+i+NHASH]);
      i++;
    }
  }
  free(collide);
  DEBUG1(
   printf("after creation:\n");
   MappingPrint(pMap);
  )

  /* In this step, we will remove overlapping entries from the mapping.
  **
  ** We use a greedy algorithm.  Select the largest mapping first and
  ** remove all overlapping mappings.  Then take the next largest
  ** mapping and remove others that overlap with it.  Keep going until
  ** all mappings have been processed.
  */
  MappingSortSize(pMap);
  for(i=0; i<pMap->nUsed; i++){
    int sortNeeded = 0;
    int purgeNeeded = 0;
    struct Mapping_entry *pA;
    pA = &pMap->aMap[i];
    for(j=i+1; j<pMap->nUsed; j++){
      int diff;
      struct Mapping_entry *pB;
      pB = &pMap->aMap[j];
      if( pB->fromLast<pA->fromFirst || pB->fromFirst>pA->fromLast ){
        /* No overlap.  Do nothing */
      }else if( pB->fromFirst>=pA->fromFirst && pB->fromLast<=pA->fromLast ){
        /* B is contained entirely within A.  Drop B */
        pB->fromFirst = -1;
        purgeNeeded = 1;
        continue;
      }else if( pB->fromFirst<pA->fromFirst ){
        /* The tail B overlaps the head of A */
        assert( pB->fromLast>=pA->fromFirst && pB->fromLast<=pA->fromLast );
        diff = pB->fromLast + 1 - pA->fromFirst;
        pB->fromLast -= diff;
        pB->toLast -= diff;
        sortNeeded = 1;
      }else{
        /* The head of B overlaps the tail of A */
        assert( pB->fromFirst<=pA->fromLast && pB->fromLast>pA->fromLast );
        diff = pA->fromLast + 1 - pB->fromFirst;
        pB->fromFirst += diff;
        pB->toFirst += diff;
        sortNeeded = 1;
      }
      if( pB->toLast<pA->toFirst || pB->toFirst>pA->toLast ){
        /* No overlap.  Do nothing */
      }else if( pB->toFirst>=pA->toFirst && pB->toLast<=pA->toLast ){
        /* B is contained entirely within A.  Drop B */
        pB->fromFirst = -1;
        purgeNeeded = 1;
      }else if( pB->toFirst<pA->toFirst ){
        /* The tail of B overlaps the head of A */
        assert( pB->toLast>=pA->toFirst && pB->toLast<=pA->toLast );
        diff = pB->toLast + 1 - pA->toFirst;
        pB->fromLast -= diff;
        pB->toLast -= diff;
        sortNeeded = 1;
      }else{
        /* The head of B overlaps the tail of A */
        assert( pB->toFirst<=pA->toLast && pB->toLast>pA->toLast );
        diff = pA->toLast + 1 - pB->toFirst;
        pB->fromFirst += diff;
        pB->toFirst += diff;
        sortNeeded = 1;
      }
    }
    if( purgeNeeded ){
      MappingPurgeDeletedEntries(pMap);
    }
    if( sortNeeded && i<pMap->nUsed-2 ){
      MappingSortSize(pMap);
    }
  }

  /* Final step:  Arrange the mapping entires so that they are in the
  ** order of the output file.  Then fill in the nExtra values.
  */
add_nextra:
  MappingSortTo(pMap);
  aMap = pMap->aMap;
  for(i=0; i<pMap->nUsed-1; i++){
    aMap[i].nExtra = aMap[i+1].toFirst - aMap[i].toLast - 1;
  }
  if( pMap->nUsed>0 && origLenOut > aMap[i].toLast+1 ){
    aMap[i].nExtra = origLenOut - aMap[i].toLast - 1;
  }
}

/*
** Translate an index into a file using a mapping.
**
** The mapping "p" shows how blocks in the input file map into blocks
** of the output file.  The index iFrom is an index into the input file.
** This routine returns the index into the output file of the corresponding
** character.
**
** If pInserted!=0 and iFrom points to the last character before a
** insert in the output file, then the return value is adjusted forward
** so that it points to the end of the insertion and the number of
** bytes inserted is written into *pInserted.  If pInserted==0 then
** iFrom always maps directly in the corresponding output file
** index regardless of whether or not it points to the last character
** before an insertion.
*/
static int MappingTranslate(Mapping *p, int iFrom, int *pInserted){
  int i;
  for(i=0; i<p->nUsed; i++){
    if( iFrom>p->aMap[i].fromLast ) continue;
    if( iFrom<=p->aMap[i].fromFirst ){
      return p->aMap[i].toFirst;
    }
    if( pInserted && iFrom==p->aMap[i].fromLast ){
      int n = p->aMap[i].nExtra;
      *pInserted = n;
      return p->aMap[i].toLast + n;
    }else{
      return p->aMap[i].toFirst + iFrom - p->aMap[i].fromFirst;
    }
  }
  i--;
  return p->aMap[i].toLast + p->aMap[i].nExtra;
}

/*
** Do a three-way merge.  Initialize pOut to contain the result.
*/
void blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){
  Mapping map1, map2;
  int i;
  const char *zV1, *zV2;
  blob_zero(pOut);
  DEBUG1( printf("map1:\n"); )
  MappingInit(
    blob_buffer(pPivot), blob_size(pPivot),
    blob_buffer(pV1), blob_size(pV1),
    &map1);
  MappingSortFrom(&map1);
  DEBUG1( 
    printf("map1-final:\n");
    MappingPrint(&map1);
    printf("map2:\n");
  )
  MappingInit(
    blob_buffer(pPivot), blob_size(pPivot),
    blob_buffer(pV2), blob_size(pV2),
    &map2);
  DEBUG1(
    printf("map2-final:\n");
    MappingPrint(&map2);
  )
  zV1 = blob_buffer(pV1);
  zV2 = blob_buffer(pV2);
  if( map2.nUsed==0 ) return;
  if( map1.aMap[0].toFirst>0 ){
    blob_append(pOut, zV1, map1.aMap[0].toFirst);
    DEBUG1( printf("INSERT %d bytes from V1[0..%d]\n",
            map1.aMap[0].toFirst, map1.aMap[0].toFirst-1); )
  }
  if( map2.aMap[0].toFirst>0 ){
    blob_append(pOut, zV2, map2.aMap[0].toFirst);
    DEBUG1( printf("INSERT %d bytes from V2[0..%d]\n",
            map2.aMap[0].toFirst, map2.aMap[0].toFirst-1); )
  }
  for(i=0; i<map2.nUsed; i++){
    int iFirst, iLast, nInsert;
    struct Mapping_entry *p = &map2.aMap[i];
    iFirst = MappingTranslate(&map1, p->fromFirst, 0);
    iLast = MappingTranslate(&map1, p->fromLast, &nInsert);
    blob_append(pOut, &zV1[iFirst], iLast - iFirst + 1);
    DEBUG1(
      printf("COPY %d bytes from V1[%d..%d]\n", iLast-iFirst+1, iFirst, iLast);
    )
    if( p->nExtra>0 ){
      if( p->nExtra==nInsert
          && memcmp(&zV2[p->toLast+1], &zV1[iLast-nInsert+1], nInsert)==0 ){
        /* Omit a duplicate insert */
        DEBUG1( printf("OMIT duplicate insert\n"); )
      }else{
        blob_append(pOut, &zV2[p->toLast+1], p->nExtra);
        DEBUG1(
          printf("INSERT %d bytes from V2[%d..%d]\n",
                  p->nExtra, p->toLast+1, p->toLast+p->nExtra);
        )
      }
    }
  }
  MappingClear(&map1);
  MappingClear(&map2);
}

/*
** COMMAND:  test-3-way-merge
**
** Combine change in going from PIVOT->VERSION1 with the change going
** from PIVOT->VERSION2 and write the combined changes into MERGED.
................................................................................
    exit(1);
  }
  blob_reset(&pivot);
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&merged);
}


/*
** COMMAND:  test-mapping
*/
void mapping_test(void){
  int i;
  const char *z;
  Blob a, b;
  Mapping map;
  if( g.argc!=4 ){
    usage("FILE1 FILE2");
  }
  blob_read_from_file(&a, g.argv[2]);
  blob_read_from_file(&b, g.argv[3]);
  memset(&map, 0, sizeof(map));
  MappingInit(blob_buffer(&a), blob_size(&a),
              blob_buffer(&b), blob_size(&b),
              &map);
  z = blob_buffer(&a);
  for(i=0; i<map.nUsed; i++){
    printf("======= %6d..%-6d %6d..%-6d %d\n", 
         map.aMap[i].fromFirst, map.aMap[i].fromLast,
         map.aMap[i].toFirst, map.aMap[i].toLast,
         map.aMap[i].nExtra);
    printf("%.*s\n", map.aMap[i].fromLast - map.aMap[i].fromFirst + 1, 
                     &z[map.aMap[i].fromFirst]);
  }
}







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







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206






















































































































































































































































































































































































































207
208
209
210
211
212
213
...
236
237
238
239
240
241
242





























**
** This module implements a 3-way merge
*/
#include "config.h"
#include "merge3.h"

#if 0
#define DEBUG(X)  X
#else
#define DEBUG(X)
#endif

/*
** Opcodes
*/
#define CPY 0
#define DEL 1
#define INS 2
#define END 3
#define UNK 4

/*
** Compare a single line of text from pV1 and pV2.  If the lines
** are the same, return true.  Return false if they are different.
**
** The cursor on both pV1 and pV2 is unchanged.
*/
static int sameLine(Blob *pV1, Blob *pV2){
  char *z1, *z2;
  int i;

  z1 = &blob_buffer(pV1)[blob_tell(pV1)];
  z2 = &blob_buffer(pV2)[blob_tell(pV2)];
  for(i=0; z1[i]!='\n' && z1[i]==z2[i]; i++){}
  return z2[i]=='\n' || (z2[i]=='\r' && z2[i+1]=='\n')
          || (z1[i]=='\r' && z2[i]=='\n' && z1[i+1]=='\n');
}

/*
** Do a three-way merge.  Initialize pOut to contain the result.
**
** The merge is an edit against pV2.  Both pV1 and pV2 have a
** common origin at pPivot.  Apply the changes of pPivot ==> pV1
** to pV2.
**
** The return is 0 upon complete success. If any input file is binary,
** -1 is returned and pOut is unmodified.  If there are merge
** conflicts, the merge proceeds as best as it can and the number 
** of conflicts is returns
*/
int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){
  int *aC1;  /* Changes from pPivot to pV1 */
  int *aC2;  /* Changes from pPivot to pV2 */
  int i1, i2;
  int op1, op2;
  int limit1, limit2;
  int inConflict = 0;
  int nConflict = 0;
  static const char zBegin[] = ">>>>>>>> BEGIN MERGE CONFLICT <<<<<<<<\n";
  static const char zEnd[]   = ">>>>>>>>> END MERGE CONFLICT <<<<<<<<<\n";

  aC1 = text_diff(pPivot, pV1, 0, 0);
  aC2 = text_diff(pPivot, pV2, 0, 0);

  if( aC2==0 || aC2==0 ){
    free(aC1);
    free(aC2);
    return -1;
  }
  blob_zero(pOut);
  blob_rewind(pV1);
  blob_rewind(pV2);
  blob_rewind(pPivot);

  for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){}
  limit1 = i1;
  for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){}
  limit2 = i2;

  DEBUG(
    for(i1=0; i1<limit1; i1+=3){
      printf("c1: %4d %4d %4d\n", aC1[i1], aC1[i1+1], aC1[i1+2]);
    }
    for(i2=0; i2<limit2; i2+=3){
     printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]);
    }
  )

  op1 = op2 = UNK;
  i1 = i2 = 0;
  while( i1<limit1 && aC1[i1]==0 ){ i1++; }
  while( i2<limit2 && aC2[i2]==0 ){ i2++; }

  while(1){
    if( op1==UNK ){
      if( i1>=limit1 ){
        op1 = END;
        DEBUG( printf("get op1=END\n"); )
      }else{
        op1 = i1 % 3;
        aC1[i1]--;
        DEBUG( printf("get op1=%d from %d (%d->%d)\n",
               op1,i1,aC1[i1]+1,aC1[i1]); )
        while( i1<limit1 && aC1[i1]==0 ){ i1++; }
      }
    }
    if( op2==UNK ){
      if( i2>=limit2 ){
        op2 = END;
        DEBUG( printf("get op2=END\n"); )
      }else{
        op2 = i2 % 3;
        aC2[i2]--;
        DEBUG( printf("get op2=%d from %d (%d->%d)\n",
               op2,i2,aC2[i2]+1,aC2[i2]); )
        while( i2<limit2 && aC2[i2]==0 ){ i2++; }
      }
    }
    DEBUG( printf("op1=%d op2=%d\n", op1, op2); )
    if( op1==END ){
      if( op2==INS ){
        blob_copy_lines(pOut, pV2, 1);
        op2 = UNK;
      }else{
        break;
      }
    }else if( op2==END ){
      if( op1==INS ){
        blob_copy_lines(pOut, pV1, 1);
        op1 = UNK;
      }else{
        break;
      }
    }else if( op1==INS && op2==INS ){
      if( !inConflict && sameLine(pV1, pV2) ){
        blob_copy_lines(pOut, pV2, 1);
        blob_copy_lines(0, pV1, 0);
        op1 = UNK;
        op2 = UNK;
      }else{
        if( !inConflict ){
          inConflict = 1;
          nConflict++;
          blob_appendf(pOut, zBegin);
        }
        blob_copy_lines(pOut, pV1, 1);
        op1 = UNK;
      }
    }else if( op1==INS ){
      blob_copy_lines(pOut, pV1, 1);
      op1 = UNK;
    }else if( op2==INS ){
      blob_copy_lines(pOut, pV2, 1);
      op2 = UNK;
    }else{
      if( inConflict ){
        inConflict = 0;
        blob_appendf(pOut, zEnd);
      }
      if( op1==DEL || op2==DEL ){
        blob_copy_lines(0, pPivot, 1);
        if( op2==CPY ){
          blob_copy_lines(0, pV2, 1);
        }
        if( op1==CPY ){
          blob_copy_lines(0, pV1, 1);
        }
      }else{
        assert( op1==CPY && op2==CPY );
        blob_copy_lines(pOut, pPivot, 1);
        blob_copy_lines(0, pV1, 1);
        blob_copy_lines(0, pV2, 1);
      }
      op1 = UNK;
      op2 = UNK;
    }
  }
  if( inConflict ){
    blob_appendf(pOut, zEnd);
  }

  free(aC1);
  free(aC2);
  return nConflict;






















































































































































































































































































































































































































}

/*
** COMMAND:  test-3-way-merge
**
** Combine change in going from PIVOT->VERSION1 with the change going
** from PIVOT->VERSION2 and write the combined changes into MERGED.
................................................................................
    exit(1);
  }
  blob_reset(&pivot);
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&merged);
}





























Changes to src/name.c.

31
32
33
34
35
36
37



38
39
40
41
42
43
44
45


















46

47





48
49
50
51
52
53
54
..
70
71
72
73
74
75
76


77
78
79
80
81
82
83
...
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "name.h"
#include <assert.h>

/*
** This routine takes a user-entered UUID which might be in mixed
** case and might only be a prefix of the full UUID and converts it
** into the full-length UUID in canonical form.



**
** Return the number of errors.
*/
int name_to_uuid(Blob *pName, int iErrPriority){
  int rc;
  int sz;
  sz = blob_size(pName);
  if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){


















    fossil_error(iErrPriority, "not a valid object name: %b", pName);

    return 1;





  }
  blob_materialize(pName);
  canonical16(blob_buffer(pName), sz);
  if( sz==UUID_SIZE ){
    rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName);
    if( rc ){
      fossil_error(iErrPriority, "unknown object: %b", pName);
................................................................................
         zOrig[sz-1]--;
         fossil_error(iErrPriority, "non-unique name prefix: %s", zOrig);
         rc = 1;
      }else{
         rc = 0;
      }
    }


  }
  return rc;
}

/*
** COMMAND:  test-name-to-uuid
**
................................................................................
    rid = atoi(zName);
    if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
      return rid;
    }
  }
  blob_init(&name, zName, -1);
  if( name_to_uuid(&name, 1) ){
    fossil_panic("%s", g.zErrMsg);
  }
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name);
  blob_reset(&name);
  return rid;
}







>
>
>








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







 







>
>







 







|





31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
147
148
149
150
151
152
153
154
155
156
157
158
159
#include "name.h"
#include <assert.h>

/*
** This routine takes a user-entered UUID which might be in mixed
** case and might only be a prefix of the full UUID and converts it
** into the full-length UUID in canonical form.
**
** If the input is not a UUID or a UUID prefix, then try to resolve
** the name as a tag.
**
** Return the number of errors.
*/
int name_to_uuid(Blob *pName, int iErrPriority){
  int rc;
  int sz;
  sz = blob_size(pName);
  if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){
    Stmt q;
    Blob uuid;

    db_prepare(&q,
      "SELECT (SELECT uuid FROM blob WHERE rid=objid)"
      "  FROM tagxref JOIN event ON rid=objid"
      " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%B)"
      "   AND tagtype>0"
      "   AND value IS NULL"
      " ORDER BY event.mtime DESC",
      pName
    );
    blob_zero(&uuid);
    if( db_step(&q)==SQLITE_ROW ){
      db_column_blob(&q, 0, &uuid);
    }
    db_finalize(&q);
    if( blob_size(&uuid)==0 ){
      fossil_error(iErrPriority, "not a valid object name: %b", pName);
      blob_reset(&uuid);
      return 1;
    }else{
      blob_reset(pName);
      *pName = uuid;
      return 0;
    }
  }
  blob_materialize(pName);
  canonical16(blob_buffer(pName), sz);
  if( sz==UUID_SIZE ){
    rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName);
    if( rc ){
      fossil_error(iErrPriority, "unknown object: %b", pName);
................................................................................
         zOrig[sz-1]--;
         fossil_error(iErrPriority, "non-unique name prefix: %s", zOrig);
         rc = 1;
      }else{
         rc = 0;
      }
    }
  }else{
    rc = 0;
  }
  return rc;
}

/*
** COMMAND:  test-name-to-uuid
**
................................................................................
    rid = atoi(zName);
    if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
      return rid;
    }
  }
  blob_init(&name, zName, -1);
  if( name_to_uuid(&name, 1) ){
    fossil_fatal("%s", g.zErrMsg);
  }
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name);
  blob_reset(&name);
  return rid;
}

Changes to src/pivot.c.

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*
** Find the most recent common ancestor of the primary and one of
** the secondaries.  Return its rid.  Return 0 if no common ancestor
** can be found.
*/
int pivot_find(void){
  Stmt q1, q2, u1, i1;
  int rid;
  
  /* aqueue must contain at least one primary and one other.  Otherwise
  ** we abort early
  */
  if( db_int(0, "SELECT count(distinct src) FROM aqueue")<2 ){
    fossil_panic("lack both primary and secondary files");
  }







|







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*
** Find the most recent common ancestor of the primary and one of
** the secondaries.  Return its rid.  Return 0 if no common ancestor
** can be found.
*/
int pivot_find(void){
  Stmt q1, q2, u1, i1;
  int rid = 0;
  
  /* aqueue must contain at least one primary and one other.  Otherwise
  ** we abort early
  */
  if( db_int(0, "SELECT count(distinct src) FROM aqueue")<2 ){
    fossil_panic("lack both primary and secondary files");
  }

Changes to src/printf.c.

47
48
49
50
51
52
53



54
55
56
57
58
59
60
..
90
91
92
93
94
95
96


97
98
99
100
101
102
103
...
107
108
109
110
111
112
113

114
115
116
117
118
119
120
...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
...
539
540
541
542
543
544
545
















546
547
548
549
550
551
552
...
638
639
640
641
642
643
644















645
646
647
648
649
650
651
652
653
654
655
656
657
658
...
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
#define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '',
                          NULL pointers replaced by SQL NULL.  %Q */
#define etPOINTER    15 /* The %p conversion */
#define etHTMLIZE    16 /* Make text safe for HTML */
#define etHTTPIZE    17 /* Make text safe for HTTP.  "/" encoded as %2f */
#define etURLIZE     18 /* Make text safe for HTTP.  "/" not encoded */
#define etFOSSILIZE  19 /* The fossil header encoding format. */





/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;

................................................................................
  {  's',  0, 4, etSTRING,     0,  0 },
  {  'g',  0, 1, etGENERIC,    30, 0 },
  {  'z',  0, 6, etDYNSTRING,  0,  0 },
  {  'q',  0, 4, etSQLESCAPE,  0,  0 },
  {  'Q',  0, 4, etSQLESCAPE2, 0,  0 },
  {  'b',  0, 2, etBLOB,       0,  0 },
  {  'B',  0, 2, etBLOBSQL,    0,  0 },


  {  'h',  0, 4, etHTMLIZE,    0,  0 },
  {  't',  0, 4, etHTTPIZE,    0,  0 },  /* "/" -> "%2F" */
  {  'T',  0, 4, etURLIZE,     0,  0 },  /* "/" unchanged */
  {  'F',  0, 4, etFOSSILIZE,  0,  0 },
  {  'c',  0, 0, etCHARX,      0,  0 },
  {  'o',  8, 0, etRADIX,      0,  2 },
  {  'u', 10, 0, etRADIX,      0,  0 },
................................................................................
  {  'e',  0, 1, etEXP,        30, 0 },
  {  'E',  0, 1, etEXP,        14, 0 },
  {  'G',  0, 1, etGENERIC,    14, 0 },
  {  'i', 10, 1, etRADIX,      0,  0 },
  {  'n',  0, 0, etSIZE,       0,  0 },
  {  '%',  0, 0, etPERCENT,    0,  0 },
  {  'p', 16, 0, etPOINTER,    0,  1 },

};
#define etNINFO  (sizeof(fmtinfo)/sizeof(fmtinfo[0]))

/*
** "*val" is a double such that 0.1 <= *val < 10.0
** Return the ascii code for the leading digit of *val, then
** multiply "*val" by 10.0 to renormalize.
................................................................................
**          the function "func".  Returns -1 on a error.
**
** Note that the order in which automatic variables are declared below
** seems to make a big difference in determining how fast this beast
** will run.
*/
int vxprintf(
  void (*func)(void*,const char*,int),     /* Consumer of text */
  void *arg,                         /* First argument to the consumer */
  const char *fmt,                   /* Format string */
  va_list ap                         /* arguments */
){
  int c;                     /* Next character in the format string */
  char *bufpt;               /* Pointer to the conversion buffer */
  int precision;             /* Precision of the current field */
  int length;                /* Length of the field */
................................................................................
  int  exp, e2;              /* exponent of real numbers */
  double rounder;            /* Used for rounding floating point values */
  etByte flag_dp;            /* True if decimal point should be shown */
  etByte flag_rtz;           /* True if trailing zeros should be removed */
  etByte flag_exp;           /* True to force display of the exponent */
  int nsd;                   /* Number of significant digits returned */

  func(arg,"",0);
  count = length = 0;
  bufpt = 0;
  for(; (c=(*fmt))!=0; ++fmt){
    if( c!='%' ){
      int amt;
      bufpt = (char *)fmt;
      amt = 1;
      while( (c=(*++fmt))!='%' && c!=0 ) amt++;
      (*func)(arg,bufpt,amt);
      count += amt;
      if( c==0 ) break;
    }
    if( (c=(*++fmt))==0 ){
      errorflag = 1;
      (*func)(arg,"%",1);
      count++;
      break;
    }
    /* Find out what flags are present */
    flag_leftjustify = flag_plussign = flag_blanksign = 
     flag_alternateform = flag_altform2 = flag_zeropad = 0;
    done = 0;
................................................................................
          for(idx=1; idx<precision; idx++) buf[idx] = c;
          length = precision;
        }else{
          length =1;
        }
        bufpt = buf;
        break;
















      case etSTRING:
      case etDYNSTRING:
        bufpt = va_arg(ap,char*);
        if( bufpt==0 ){
          bufpt = "";
        }else if( xtype==etDYNSTRING ){
          zExtra = bufpt;
................................................................................
      case etFOSSILIZE: {
        char *zMem = va_arg(ap,char*);
        if( zMem==0 ) zMem = "";
        zExtra = bufpt = fossilize(zMem, -1);
        length = strlen(bufpt);
        if( precision>=0 && precision<length ) length = precision;
        break;















      }
      case etERROR:
        buf[0] = '%';
        buf[1] = c;
        errorflag = 0;
        idx = 1+(c!=0);
        (*func)(arg,"%",idx);
        count += idx;
        if( c==0 ) fmt--;
        break;
    }/* End switch over the format type */
    /*
    ** The text of the conversion is pointed to by "bufpt" and is
    ** "length" characters long.  The field width is "width".  Do
................................................................................
    */
    if( !flag_leftjustify ){
      register int nspace;
      nspace = width-length;
      if( nspace>0 ){
        count += nspace;
        while( nspace>=etSPACESIZE ){
          (*func)(arg,spaces,etSPACESIZE);
          nspace -= etSPACESIZE;
        }
        if( nspace>0 ) (*func)(arg,spaces,nspace);
      }
    }
    if( length>0 ){
      (*func)(arg,bufpt,length);
      count += length;
    }
    if( flag_leftjustify ){
      register int nspace;
      nspace = width-length;
      if( nspace>0 ){
        count += nspace;
        while( nspace>=etSPACESIZE ){
          (*func)(arg,spaces,etSPACESIZE);
          nspace -= etSPACESIZE;
        }
        if( nspace>0 ) (*func)(arg,spaces,nspace);
      }
    }
    if( zExtra ){
      free(zExtra);
    }
  }/* End for loop over the format string */
  return errorflag ? -1 : count;







>
>
>







 







>
>







 







>







 







|
<







 







<








|





|







 







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







 







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






|







 







|


|



|








|


|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
...
173
174
175
176
177
178
179
180

181
182
183
184
185
186
187
...
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
...
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
...
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
...
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
#define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '',
                          NULL pointers replaced by SQL NULL.  %Q */
#define etPOINTER    15 /* The %p conversion */
#define etHTMLIZE    16 /* Make text safe for HTML */
#define etHTTPIZE    17 /* Make text safe for HTTP.  "/" encoded as %2f */
#define etURLIZE     18 /* Make text safe for HTTP.  "/" not encoded */
#define etFOSSILIZE  19 /* The fossil header encoding format. */
#define etPATH       20 /* Path type */
#define etWIKISTR    21 /* Wiki text rendered from a char* */
#define etWIKIBLOB   22 /* Wiki text rendered from a Blob* */


/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;

................................................................................
  {  's',  0, 4, etSTRING,     0,  0 },
  {  'g',  0, 1, etGENERIC,    30, 0 },
  {  'z',  0, 6, etDYNSTRING,  0,  0 },
  {  'q',  0, 4, etSQLESCAPE,  0,  0 },
  {  'Q',  0, 4, etSQLESCAPE2, 0,  0 },
  {  'b',  0, 2, etBLOB,       0,  0 },
  {  'B',  0, 2, etBLOBSQL,    0,  0 },
  {  'w',  0, 2, etWIKISTR,    0,  0 },
  {  'W',  0, 2, etWIKIBLOB,   0,  0 },
  {  'h',  0, 4, etHTMLIZE,    0,  0 },
  {  't',  0, 4, etHTTPIZE,    0,  0 },  /* "/" -> "%2F" */
  {  'T',  0, 4, etURLIZE,     0,  0 },  /* "/" unchanged */
  {  'F',  0, 4, etFOSSILIZE,  0,  0 },
  {  'c',  0, 0, etCHARX,      0,  0 },
  {  'o',  8, 0, etRADIX,      0,  2 },
  {  'u', 10, 0, etRADIX,      0,  0 },
................................................................................
  {  'e',  0, 1, etEXP,        30, 0 },
  {  'E',  0, 1, etEXP,        14, 0 },
  {  'G',  0, 1, etGENERIC,    14, 0 },
  {  'i', 10, 1, etRADIX,      0,  0 },
  {  'n',  0, 0, etSIZE,       0,  0 },
  {  '%',  0, 0, etPERCENT,    0,  0 },
  {  'p', 16, 0, etPOINTER,    0,  1 },
  {  '/',  0, 0, etPATH,       0,  0 },
};
#define etNINFO  (sizeof(fmtinfo)/sizeof(fmtinfo[0]))

/*
** "*val" is a double such that 0.1 <= *val < 10.0
** Return the ascii code for the leading digit of *val, then
** multiply "*val" by 10.0 to renormalize.
................................................................................
**          the function "func".  Returns -1 on a error.
**
** Note that the order in which automatic variables are declared below
** seems to make a big difference in determining how fast this beast
** will run.
*/
int vxprintf(
  Blob *pBlob,                       /* Append output to this blob */

  const char *fmt,                   /* Format string */
  va_list ap                         /* arguments */
){
  int c;                     /* Next character in the format string */
  char *bufpt;               /* Pointer to the conversion buffer */
  int precision;             /* Precision of the current field */
  int length;                /* Length of the field */
................................................................................
  int  exp, e2;              /* exponent of real numbers */
  double rounder;            /* Used for rounding floating point values */
  etByte flag_dp;            /* True if decimal point should be shown */
  etByte flag_rtz;           /* True if trailing zeros should be removed */
  etByte flag_exp;           /* True to force display of the exponent */
  int nsd;                   /* Number of significant digits returned */


  count = length = 0;
  bufpt = 0;
  for(; (c=(*fmt))!=0; ++fmt){
    if( c!='%' ){
      int amt;
      bufpt = (char *)fmt;
      amt = 1;
      while( (c=(*++fmt))!='%' && c!=0 ) amt++;
      blob_append(pBlob,bufpt,amt);
      count += amt;
      if( c==0 ) break;
    }
    if( (c=(*++fmt))==0 ){
      errorflag = 1;
      blob_append(pBlob,"%",1);
      count++;
      break;
    }
    /* Find out what flags are present */
    flag_leftjustify = flag_plussign = flag_blanksign = 
     flag_alternateform = flag_altform2 = flag_zeropad = 0;
    done = 0;
................................................................................
          for(idx=1; idx<precision; idx++) buf[idx] = c;
          length = precision;
        }else{
          length =1;
        }
        bufpt = buf;
        break;
      case etPATH: {
        int i;
        char *e = va_arg(ap,char*);
        if( e==0 ){e="";}
        length = strlen(e);
        zExtra = bufpt = malloc(length+1);
        for( i=0; i<length; i++ ){
          if( e[i]=='\\' ){
            bufpt[i]='/';
          }else{
            bufpt[i]=e[i];
          }
        }
        bufpt[length]='\0';
        break;
      }
      case etSTRING:
      case etDYNSTRING:
        bufpt = va_arg(ap,char*);
        if( bufpt==0 ){
          bufpt = "";
        }else if( xtype==etDYNSTRING ){
          zExtra = bufpt;
................................................................................
      case etFOSSILIZE: {
        char *zMem = va_arg(ap,char*);
        if( zMem==0 ) zMem = "";
        zExtra = bufpt = fossilize(zMem, -1);
        length = strlen(bufpt);
        if( precision>=0 && precision<length ) length = precision;
        break;
      }
      case etWIKISTR: {
        char *zWiki = va_arg(ap, char*);
        Blob wiki;
        blob_init(&wiki, zWiki, -1);
        wiki_convert(&wiki, pBlob, WIKI_INLINE);
        blob_reset(&wiki);
        length = width = 0;
        break;
      }
      case etWIKIBLOB: {
        Blob *pWiki = va_arg(ap, Blob*);
        wiki_convert(pWiki, pBlob, WIKI_INLINE);
        length = width = 0;
        break;
      }
      case etERROR:
        buf[0] = '%';
        buf[1] = c;
        errorflag = 0;
        idx = 1+(c!=0);
        blob_append(pBlob,"%",idx);
        count += idx;
        if( c==0 ) fmt--;
        break;
    }/* End switch over the format type */
    /*
    ** The text of the conversion is pointed to by "bufpt" and is
    ** "length" characters long.  The field width is "width".  Do
................................................................................
    */
    if( !flag_leftjustify ){
      register int nspace;
      nspace = width-length;
      if( nspace>0 ){
        count += nspace;
        while( nspace>=etSPACESIZE ){
          blob_append(pBlob,spaces,etSPACESIZE);
          nspace -= etSPACESIZE;
        }
        if( nspace>0 ) blob_append(pBlob,spaces,nspace);
      }
    }
    if( length>0 ){
      blob_append(pBlob,bufpt,length);
      count += length;
    }
    if( flag_leftjustify ){
      register int nspace;
      nspace = width-length;
      if( nspace>0 ){
        count += nspace;
        while( nspace>=etSPACESIZE ){
          blob_append(pBlob,spaces,etSPACESIZE);
          nspace -= etSPACESIZE;
        }
        if( nspace>0 ) blob_append(pBlob,spaces,nspace);
      }
    }
    if( zExtra ){
      free(zExtra);
    }
  }/* End for loop over the format string */
  return errorflag ? -1 : count;

Changes to src/rebuild.c.

22
23
24
25
26
27
28



























29
30
31
32
33
34
35





36
37
38
39
40
41

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

56
57




58
59
60

61



62
63
64
65
66





67
68
69
70
71
72




73
74
75
76
77
78
79
..
81
82
83
84
85
86
87

88
89
90

91
92
93
94
95
96
97
98
99
100
101
102
103
104
*******************************************************************************
**
** This file contains code used to rebuild the database.
*/
#include "config.h"
#include "rebuild.h"
#include <assert.h>




























/*
** Core function to rebuild the infomration in the derived tables of a
** fossil repository from the blobs. This function is shared between
** 'rebuild_database' ('rebuild') and 'reconstruct_cmd'
** ('reconstruct'), both of which have to regenerate this information
** from scratch.





*/

int rebuild_db(void){
  Stmt s;
  int errCnt = 0;
  char *zTable;


  db_multi_exec(
    "CREATE INDEX IF NOT EXISTS delta_i1 ON delta(srcid);"
  );
  for(;;){
    zTable = db_text(0,
       "SELECT name FROM sqlite_master"
       " WHERE type='table'"
       " AND name NOT IN ('blob','delta','rcvfrom','user','config')");
    if( zTable==0 ) break;
    db_multi_exec("DROP TABLE %Q", zTable);
    free(zTable);
  }
  db_multi_exec(zRepositorySchema2);


  db_multi_exec("INSERT INTO unclustered SELECT rid FROM blob");




  db_multi_exec(
    "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')"
  );

  db_prepare(&s, "SELECT rid, size FROM blob");



  while( db_step(&s)==SQLITE_ROW ){
    int rid = db_column_int(&s, 0);
    int size = db_column_int(&s, 1);
    if( size>=0 ){
      Blob content;





      content_get(rid, &content);
      manifest_crosslink(rid, &content);
      blob_reset(&content);
    }else{
      db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
    }




  }
  return errCnt;
}

/*
** COMMAND:  rebuild
**
................................................................................
**
** Reconstruct the named repository database from the core
** records.  Run this command after updating the fossil
** executable in a way that changes the database schema.
*/
void rebuild_database(void){
  int forceFlag;

  int errCnt;

  forceFlag = find_option("force","f",0)!=0;

  if( g.argc!=3 ){
    usage("REPOSITORY-FILENAME");
  }
  db_open_repository(g.argv[2]);
  db_begin_transaction();
  errCnt = rebuild_db();
  if( errCnt && !forceFlag ){
    printf("%d errors. Rolling back changes. Use --force to force a commit.\n",
            errCnt);
    db_end_transaction(1);
  }else{
    db_end_transaction(0);
  }
}







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







>
>
>
>
>

<
|



>

|
<
<




|





>


>
>
>
>



>
|
>
>
>





>
>
>
>
>






>
>
>
>







 







>



>





|








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

69
70
71
72
73
74
75


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
*******************************************************************************
**
** This file contains code used to rebuild the database.
*/
#include "config.h"
#include "rebuild.h"
#include <assert.h>

/*
** Schema changes
*/
static const char zSchemaUpdates[] =
@ -- Index on the delta table
@ --
@ CREATE INDEX IF NOT EXISTS delta_i1 ON delta(srcid);
@
@ -- Artifacts that should not be processed are identified in the
@ -- "shun" table.  Artifacts that are control-file forgeries or
@ -- spam can be shunned in order to prevent them from contaminating
@ -- the repository.
@ --
@ CREATE TABLE IF NOT EXISTS shun(uuid UNIQUE);
@
@ -- An entry in this table describes a database query that generates a
@ -- table of tickets.
@ --
@ CREATE TABLE IF NOT EXISTS reportfmt(
@    rn integer primary key,  -- Report number
@    owner text,              -- Owner of this report format (not used)
@    title text,              -- Title of this report
@    cols text,               -- A color-key specification
@    sqlcode text             -- An SQL SELECT statement for this report
@ );
;

/*
** Core function to rebuild the infomration in the derived tables of a
** fossil repository from the blobs. This function is shared between
** 'rebuild_database' ('rebuild') and 'reconstruct_cmd'
** ('reconstruct'), both of which have to regenerate this information
** from scratch.
**
** If the randomize parameter is true, then the BLOBs are deliberately
** extracted in a random order.  This feature is used to test the
** ability of fossil to accept records in any order and still
** construct a sane repository.
*/

int rebuild_db(int randomize, int ttyOutput){
  Stmt s;
  int errCnt = 0;
  char *zTable;
  int cnt = 0;

  db_multi_exec(zSchemaUpdates);


  for(;;){
    zTable = db_text(0,
       "SELECT name FROM sqlite_master"
       " WHERE type='table'"
       " AND name NOT IN ('blob','delta','rcvfrom','user','config','shun')");
    if( zTable==0 ) break;
    db_multi_exec("DROP TABLE %Q", zTable);
    free(zTable);
  }
  db_multi_exec(zRepositorySchema2);
  ticket_create_table(0);

  db_multi_exec("INSERT INTO unclustered SELECT rid FROM blob");
  db_multi_exec(
     "DELETE FROM unclustered"
     " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))"
  );
  db_multi_exec(
    "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')"
  );
  db_prepare(&s,
     "SELECT rid, size FROM blob %s"
     " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)",
     randomize ? "ORDER BY random()" : ""
  );
  while( db_step(&s)==SQLITE_ROW ){
    int rid = db_column_int(&s, 0);
    int size = db_column_int(&s, 1);
    if( size>=0 ){
      Blob content;
      if( ttyOutput ){
        cnt++;
        printf("%d...\r", cnt);
        fflush(stdout);
      }
      content_get(rid, &content);
      manifest_crosslink(rid, &content);
      blob_reset(&content);
    }else{
      db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
    }
  }
  db_finalize(&s);
  if( ttyOutput ){
    printf("\n");
  }
  return errCnt;
}

/*
** COMMAND:  rebuild
**
................................................................................
**
** Reconstruct the named repository database from the core
** records.  Run this command after updating the fossil
** executable in a way that changes the database schema.
*/
void rebuild_database(void){
  int forceFlag;
  int randomizeFlag;
  int errCnt;

  forceFlag = find_option("force","f",0)!=0;
  randomizeFlag = find_option("randomize", 0, 0)!=0;
  if( g.argc!=3 ){
    usage("REPOSITORY-FILENAME");
  }
  db_open_repository(g.argv[2]);
  db_begin_transaction();
  errCnt = rebuild_db(randomizeFlag, 1);
  if( errCnt && !forceFlag ){
    printf("%d errors. Rolling back changes. Use --force to force a commit.\n",
            errCnt);
    db_end_transaction(1);
  }else{
    db_end_transaction(0);
  }
}

Added src/report.c.

































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**  
** Code to generate the bug report listings
*/
#include "config.h"
#include "report.h"
#include <assert.h>

/* Forward references to static routines */
static void report_format_hints(void);

/*
** WEBPAGE: /reportlist
*/
void view_list(void){
  Stmt q;

  login_check_credentials();
  if( !g.okRdTkt ){ login_needed(); return; }
  style_header("Available Report Formats");
  db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title");
  @ <p>Choose a report format from the following list:</p>
  @ <ol>
  while( db_step(&q)==SQLITE_ROW ){
    int rn = db_column_int(&q, 0);
    const char *zTitle = db_column_text(&q, 1);
    const char *zOwner = db_column_text(&q, 2);
    @ <li><a href="rptview?rn=%d(rn)"
    @        rel="nofollow">%h(zTitle)</a>&nbsp;&nbsp;&nbsp;
    if( g.okWrite && zOwner && zOwner[0] ){
      @ (by <i>%h(zOwner)</i>)
    }
    if( g.okWrTkt ){
      @ [<a href="rptedit?rn=%d(rn)&amp;copy=1" rel="nofollow">copy</a>]
    }
    if( g.okAdmin || (g.okWrTkt && zOwner && strcmp(g.zLogin,zOwner)==0) ){
      @ [<a href="rptedit?rn=%d(rn)" rel="nofollow">edit</a>]
    }
    @ [<a href="rptsql?rn=%d(rn)" rel="nofollow">sql</a>]
    @ </li>
  }
  if( g.okWrTkt ){
    @ <li><a href="rptnew">Create a new report format</a></li>
  }
  @ </ol>
  common_footer();
}

/*
** Remove whitespace from both ends of a string.
*/
char *trim_string(const char *zOrig){
  int i;
  while( isspace(*zOrig) ){ zOrig++; }
  i = strlen(zOrig);
  while( i>0 && isspace(zOrig[i-1]) ){ i--; }
  return mprintf("%.*s", i, zOrig);
}

/*
** Extract a numeric (integer) value from a string.
*/
char *extract_integer(const char *zOrig){
  if( zOrig == NULL || zOrig[0] == 0 ) return "";
  while( *zOrig && !isdigit(*zOrig) ){ zOrig++; }
  if( *zOrig ){
    /* we have a digit. atoi() will get as much of the number as it
    ** can. We'll run it through mprintf() to get a string. Not
    ** an efficient way to do it, but effective.
    */
    return mprintf("%d", atoi(zOrig));
  }
  return "";
}

/*
** Remove blank lines from the beginning of a string and
** all whitespace from the end. Removes whitespace preceeding a NL,
** which also converts any CRNL sequence into a single NL.
*/
char *remove_blank_lines(const char *zOrig){
  int i, j, n;
  char *z;
  for(i=j=0; isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; }
  n = strlen(&zOrig[j]);
  while( n>0 && isspace(zOrig[j+n-1]) ){ n--; }
  z = mprintf("%.*s", n, &zOrig[j]);
  for(i=j=0; z[i]; i++){
    if( z[i+1]=='\n' && z[i]!='\n' && isspace(z[i]) ){
      z[j] = z[i];
      while(isspace(z[j]) && z[j] != '\n' ){ j--; }
      j++;
      continue;
    }

    z[j++] = z[i];
  }
  z[j] = 0;
  return z;
}


/*********************************************************************/

/*
** This is the SQLite authorizer callback used to make sure that the
** SQL statements entered by users do not try to do anything untoward.
** If anything suspicious is tried, set *(char**)pError to an error
** message obtained from malloc.
*/
static int report_query_authorizer(
  void *pError,
  int code,
  const char *zArg1,
  const char *zArg2,
  const char *zArg3,
  const char *zArg4
){
  char *zError = *(char**)pError;
  if( zError ){
    /* We've already seen an error.  No need to continue. */
    return SQLITE_OK;
  }
  switch( code ){
    case SQLITE_SELECT:
    case SQLITE_FUNCTION: {
      break;
    }
    case SQLITE_READ: {
      static const char *azAllowed[] = {
         "ticket",
         "blob",
         "filename",
         "mlink",
         "plink",
         "event",
         "tag",
         "tagxref",
      }
      int i;
      for(i=0; i<sizeof(azAllowed)/sizeof(azAllowed[0]); i++){
        if( strcasecmp(zArg1, azAllowed[i])==0 ) break;
      }
      if( i>=sizeof(azAllowed)/sizeof(azAllowed[0]) ){
        zError = mprintf("cannot access table %s", zArg1);
      }
      break;
    }
    default: {
      zError = mprintf("only SELECT statements are allowed");
      break;
    }
  }
  return SQLITE_OK;
}


/*
** Check the given SQL to see if is a valid query that does not
** attempt to do anything dangerous.  Return 0 on success and a
** pointer to an error message string (obtained from malloc) if
** there is a problem.
*/
char *verify_sql_statement(char *zSql){
  int i;
  char *zErr1 = 0;
  char *zErr2 = 0;
  char *zTail;

  /* First make sure the SQL is a single query command by verifying that
  ** the first token is "SELECT" and that there are no unquoted semicolons.
  */
  for(i=0; isspace(zSql[i]); i++){}
  if( strncasecmp(&zSql[i],"select",6)!=0 ){
    return mprintf("The SQL must be a SELECT statement");
  }
  for(i=0; zSql[i]; i++){
    if( zSql[i]==';' ){
      int bad;
      int c = zSql[i+1];
      zSql[i+1] = 0;
      bad = sqlite3_complete(zSql);
      zSql[i+1] = c;
      if( bad ){
        /* A complete statement basically means that an unquoted semi-colon
        ** was found. We don't actually check what's after that.
        */
        return mprintf("Semi-colon detected! "
                       "Only a single SQL statement is allowed");
      }
    }
  }
  
  /* Compile the statement and check for illegal accesses or syntax errors. */
  sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr);
  rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, &zTail);
  if( rc!=SQLITE_OK ){
    free(zErr);
    zErr = mprintf("Syntax error: %s", sqlite3_errmsg(g.db));
  }
  if( pStmt ){
    sqlite3_finalize(pStmt);
  }
  sqlite3_set_authorizer(g.db, 0, 0);
  return zErr;
}

/*
** WEBPAGE: /rptsql
*/
void view_see_sql(void){
  int rn, rc;
  char *zTitle;
  char *zSQL;
  char *zOwner;
  char *zClrKey;
  Stmt q;

  login_check_credentials();
  if( !g.okQuery ){
    login_needed();
    return;
  }
  rn = atoi(PD("rn","0"));
  db_prepare(&q, "SELECT title, sqlcode, owner, cols "
                   "FROM reportfmt WHERE rn=%d",rn);
  style_header("SQL For Report Format Number %d", rn);
  if( db_step(&q)!=SQLITE_ROW ){
    @ <p>Unknown report number: %d(rn)</p>
    style_footer();
    return;
  }
  zTitle = db_column_text(&q, 0);
  zSQL = db_column_text(&q, 1);
  zOwner = db_column_text(&q, 2);
  zClrKey = db_column_text(&q, 3);
  @ <table cellpadding=0 cellspacing=0 border=0>
  @ <tr><td valign="top" align="right">Title:</td><td width=15></td>
  @ <td colspan=3>%h(zTitle)</td></tr>
  @ <tr><td valign="top" align="right">Owner:</td><td></td>
  @ <td colspan=3>%h(zOwner)</td></tr>
  @ <tr><td valign="top" align="right">SQL:</td><td></td>
  @ <td valign="top"><pre>
  @ %h(zSQL)
  @ </pre></td>
  @ <td width=15></td><td valign="top">
  output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3");
  @ </td>
  @ </tr></table>
  report_format_hints();
  style_footer();
}

/*
** WEBPAGE: /rptnew
** WEBPAGE: /rptedit
*/
void view_edit(void){
  int rn;
  const char *zTitle;
  const char *z;
  const char *zOwner;
  char *zClrKey;
  char *zSQL;
  char *zErr = 0;

  login_check_credentials();
  if( !g.okQuery ){
    login_needed();
    return;
  }
  view_add_functions(0);
  rn = atoi(PD("rn","0"));
  zTitle = P("t");
  zOwner = PD("w",g.zLogin);
  z = P("s");
  zSQL = z ? trim_string(z) : 0;
  zClrKey = trim_string(PD("k",""));
  if( rn>0 && P("del2") ){
    db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
    cgi_redirect("reportlist");
    return;
  }else if( rn>0 && P("del1") ){
    zTitle = db_text(0, "SELECT title FROM reportfmt "
                         "WHERE rn=%d", rn);
    if( zTitle==0 ) cgi_redirect("reportlist");

    style_header("Are You Sure?");
    @ <form action="rptedit" method="POST">
    @ <p>You are about to delete all traces of the report
    @ <strong>%h(zTitle)</strong> from
    @ the database.  This is an irreversible operation.  All records
    @ related to this report will be removed and cannot be recovered.</p>
    @
    @ <input type="hidden" name="rn" value="%d(rn)">
    @ <input type="submit" name="del2" value="Delete The Report">
    @ <input type="submit" name="can" value="Cancel">
    @ </form>
    style_footer();
    return;
  }else if( P("can") ){
    /* user cancelled */
    cgi_redirect("reportlist");
    return;
  }
  if( zTitle && zSQL ){
    if( zSQL[0]==0 ){
      zErr = "Please supply an SQL query statement";
    }else if( (zTitle = trim_string(zTitle))[0]==0 ){
      zErr = "Please supply a title"; 
    }else{
      zErr = verify_sql_statement(zSQL);
    }
    if( zErr==0 ){
      if( rn>0 ){
        db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
                      " owner=%Q, cols=%Q WHERE rn=%d",
           zTitle, zSQL, zOwner, zClrKey, rn);
      }else{
        db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols) "
           "VALUES(%Q,%Q,%Q,%Q)",
           zTitle, zSQL, zOwner, zClrKey);
        rn = db_last_insert_rowid();
      }
      cgi_redirect(mprintf("rptview?rn=%d", rn));
      return;
    }
  }else if( rn==0 ){
    zTitle = "";
    zSQL = 
      @ SELECT
      @   CASE WHEN status IN ('new','active') THEN '#f2dcdc'
      @        WHEN status='review' THEN '#e8e8bd'
      @        WHEN status='fixed' THEN '#cfe8bd'
      @        WHEN status='tested' THEN '#bde5d6'
      @        WHEN status='defer' THEN '#cacae5'
      @        ELSE '#c8c8c8' END AS 'bgcolor',
      @   tn AS '#',
      @   type AS 'Type',
      @   status AS 'Status',
      @   sdate(origtime) AS 'Created',
      @   owner AS 'By',
      @   subsystem AS 'Subsys',
      @   sdate(changetime) AS 'Changed',
      @   assignedto AS 'Assigned',
      @   severity AS 'Svr',
      @   priority AS 'Pri',
      @   title AS 'Title'
      @ FROM ticket
    ;
    zClrKey = 
      @ #ffffff Key:
      @ #f2dcdc Active
      @ #e8e8e8 Review
      @ #cfe8bd Fixed
      @ #bde5d6 Tested
      @ #cacae5 Deferred
      @ #c8c8c8 Closed
    ;
  }else{
    db_prepare(&q, "SELECT title, sqlcode, owner, cols "
                     "FROM reportfmt WHERE rn=%d",rn);
    if( db_step(&q)==SQLITE_ROW ){
      zTitle = db_column_malloc(&q, 0);
      zSQL = db_column_malloc(&q, 1);
      zOwner = db_column_malloc(&q, 2);
      zClrKey = db_column_malloc(&q, 3);
    }
    if( P("copy") ){
      rn = 0;
      zTitle = mprintf("Copy Of %s", zTitle);
      zOwner = g.zLogin;
    }
  }
  if( zOwner==0 ) zOwner = g.zLogin;
  style_submenu_element("Cancel", "Cancel", "reportlist");
  if( rn>0 ){
    style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
  }
  style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
  if( zErr ){
    @ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
  }
  @ <form action="rptedit" method="POST">
  @ <input type="hidden" name="rn" value="%d(rn)">
  @ <p>Report Title:<br>
  @ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
  @ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
  @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
  @ </p>
  if( g.okAdmin ){
    @ <p>Report owner:
    @ <input type="text" name="w" size="20" value="%h(zOwner)">
    @ </p>
  } else {
    @ <input type="hidden" name="w" value="%h(zOwner)">
  }
  @ <p>Enter an optional color key in the following box.  (If blank, no
  @ color key is displayed.)  Each line contains the text for a single
  @ entry in the key.  The first token of each line is the background
  @ color for that line.<br>
  @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
  @ </p>
  if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
    @ <p>This report format is owned by %h(zOwner).  You are not allowed
    @ to change it.</p>
    @ </form>
    report_format_hints();
    style_footer();
    return;
  }
  @ <input type="submit" value="Apply Changes">
  if( rn>0 ){
    @ <input type="submit" value="Delete This Report" name="del1">
  }
  @ </form>
  report_format_hints();
  style_footer();
}

/*
** Output a bunch of text that provides information about report
** formats
*/
static void report_format_hints(void){
  char *zSchema;
  zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
  @ <hr><h3>TICKET Schema</h3>
  @ <blockquote><pre>
  @ %h(zSchema)
  @ </pre></blockquote>
  @ <h3>Notes</h3>
  @ <ul>
  @ <li><p>The SQL must consist of a single SELECT statement</p></li>
  @
  @ <li><p>If a column of the result set is named "#" then that column
  @ is assumed to hold a ticket number.  A hyperlink will be created from
  @ that column to a detailed view of the ticket.</p></li>
  @
  @ <li><p>If a column of the result set is named "bgcolor" then the content
  @ of that column determines the background color of the row.</p></li>
  @
  @ <li><p>The <b>user()</b> SQL function returns a string
  @ which is the login of the current user.</p></li>
  @
  @ <li><p>The first column whose name begins with underscore ("_") and all
  @ subsequent columns are shown on their own rows in the table.  This might
  @ be useful for displaying the description of tickets.
  @ </p></li>
  @
  @ <li><p>The <b>aux()</b> SQL function takes a parameter name as an argument
  @ and returns the value that the user enters in the resulting HTML form. A
  @ second optional parameter provides a default value for the field.</p></li>
  @
  @ <li><p>The <b>option()</b> SQL function takes a parameter name
  @ and a quoted SELECT statement as parameters. The query results are
  @ presented as an HTML dropdown menu and the function returns
  @ the currently selected value. Results may be a single value column or
  @ two <b>value,description</b> columns. The first row is the default.</p></li>
  @
  @ <li><p>The <b>cgi()</b> SQL function takes a parameter name as an argument
  @ and returns the value of a corresponding CGI query value. If the CGI
  @ parameter doesn't exist, an optional second argument will be returned
  @ instead.</p></li>
  @
  @ <li><p>If a column is wrapped by the <b>wiki()</b> SQL function, it will
  @ be rendered as wiki formatted content.</p></li>
  @
  @ <li><p>If a column is wrapped by the <b>tkt()</b> SQL function, it will
  @ be shown as a ticket id with a link to the appropriate page</p></li>
  @
  @ <li><p>If a column is wrapped by the <b>chng()</b> SQL function, it will
  @ be shown as a baseline id with a link to the appropriate page.</p></li>
  @
  @ <li><p>The <b>search()</b> SQL function takes a keyword pattern and
  @ a search text. The function returns an integer score which is
  @ higher depending on how well the search went.</p></li>
  @
  @ <li><p>The query can join other tables in the database besides TICKET.
  @ </p></li>
  @ </ul>
  @
  @ <h3>Examples</h3>
  @ <p>In this example, the first column in the result set is named
  @ "bgcolor".  The value of this column is not displayed.  Instead, it
  @ selects the background color of each row based on the TICKET.STATUS
  @ field of the database.  The color key at the right shows the various
  @ color codes.</p>
  @ <table align="right" style="margin: 0 5px;" border=1 cellspacing=0 width=125>
  @ <tr bgcolor="#f2dcdc"><td align="center">new or active</td></tr>
  @ <tr bgcolor="#e8e8bd"><td align="center">review</td></tr>
  @ <tr bgcolor="#cfe8bd"><td align="center">fixed</td></tr>
  @ <tr bgcolor="#bde5d6"><td align="center">tested</td></tr>
  @ <tr bgcolor="#cacae5"><td align="center">defer</td></tr>
  @ <tr bgcolor="#c8c8c8"><td align="center">closed</td></tr>
  @ </table>
  @ <blockquote><pre>
  @ SELECT
  @   CASE WHEN status IN ('new','active') THEN '#f2dcdc'
  @        WHEN status='review' THEN '#e8e8bd'
  @        WHEN status='fixed' THEN '#cfe8bd'
  @        WHEN status='tested' THEN '#bde5d6'
  @        WHEN status='defer' THEN '#cacae5'
  @        ELSE '#c8c8c8' END as 'bgcolor',
  @   tn AS '#',
  @   type AS 'Type',
  @   status AS 'Status',
  @   sdate(origtime) AS 'Created',
  @   owner AS 'By',
  @   subsystem AS 'Subsys',
  @   sdate(changetime) AS 'Changed',
  @   assignedto AS 'Assigned',
  @   severity AS 'Svr',
  @   priority AS 'Pri',
  @   title AS 'Title'
  @ FROM ticket
  @ </pre></blockquote>
  @ <p>To base the background color on the TICKET.PRIORITY or
  @ TICKET.SEVERITY fields, substitute the following code for the
  @ first column of the query:</p>
  @ <table align="right" style="margin: 0 5px;" border=1 cellspacing=0 width=125>
  @ <tr bgcolor="#f2dcdc"><td align="center">1</td></tr>
  @ <tr bgcolor="#e8e8bd"><td align="center">2</td></tr>
  @ <tr bgcolor="#cfe8bd"><td align="center">3</td></tr>
  @ <tr bgcolor="#cacae5"><td align="center">4</td></tr>
  @ <tr bgcolor="#c8c8c8"><td align="center">5</td></tr>
  @ </table>
  @ <blockquote><pre>
  @ SELECT
  @   CASE priority WHEN 1 THEN '#f2dcdc'
  @        WHEN 2 THEN '#e8e8bd'
  @        WHEN 3 THEN '#cfe8bd'
  @        WHEN 4 THEN '#cacae5'
  @        ELSE '#c8c8c8' END as 'bgcolor',
  @ ...
  @ FROM ticket
  @ </pre></blockquote>
#if 0
  @ <p>You can, of course, substitute different colors if you choose.
  @ Here is a palette of suggested background colors:</p>
  @ <blockquote>
  @ <table border=1 cellspacing=0 width=300>
  @ <tr><td align="center" bgcolor="#ffbdbd">#ffbdbd</td>
  @     <td align="center" bgcolor="#f2dcdc">#f2dcdc</td></tr>
  @ <tr><td align="center" bgcolor="#ffffbd">#ffffbd</td>
  @     <td align="center" bgcolor="#e8e8bd">#e8e8bd</td></tr>
  @ <tr><td align="center" bgcolor="#c0ebc0">#c0ebc0</td>
  @     <td align="center" bgcolor="#cfe8bd">#cfe8bd</td></tr>
  @ <tr><td align="center" bgcolor="#c0c0f4">#c0c0f4</td>
  @     <td align="center" bgcolor="#d6d6e8">#d6d6e8</td></tr>
  @ <tr><td align="center" bgcolor="#d0b1ff">#d0b1ff</td>
  @     <td align="center" bgcolor="#d2c0db">#d2c0db</td></tr>
  @ <tr><td align="center" bgcolor="#bbbbbb">#bbbbbb</td>
  @     <td align="center" bgcolor="#d0d0d0">#d0d0d0</td></tr>
  @ </table>
  @ </blockquote>
#endif
  @ <p>To see the TICKET.DESCRIPTION and TICKET.REMARKS fields, include
  @ them as the last two columns of the result set and given them names
  @ that begin with an underscore.  Like this:</p>
  @ <blockquote><pre>
  @  SELECT
  @    tn AS '#',
  @    type AS 'Type',
  @    status AS 'Status',
  @    sdate(origtime) AS 'Created',
  @    owner AS 'By',
  @    subsystem AS 'Subsys',
  @    sdate(changetime) AS 'Changed',
  @    assignedto AS 'Assigned',
  @    severity AS 'Svr',
  @    priority AS 'Pri',
  @    title AS 'Title',
  @    description AS '_Description',   -- When the column name begins with '_'
  @    remarks AS '_Remarks'            -- the data is shown on a separate row.
  @  FROM ticket
  @ </pre></blockquote>
  @
  @ <p>Or, to see part of the description on the same row, use the
  @ <b>wiki()</b> function with some string manipulation. Using the
  @ <b>tkt()</b> function on the ticket number will also generate a linked
  @ field, but without the extra <i>edit</i> column:
  @ </p>
  @ <blockquote><pre>
  @  SELECT
  @    tkt(tn) AS '',
  @    title AS 'Title',
  @    wiki(substr(description,0,80)) AS 'Description'
  @  FROM ticket
  @ </pre></blockquote>
  @
}

/*********************************************************************/
static void output_report_field(const char *zData,int rn){
  const char *zWkey = wiki_key();
  const char *zTkey = tkt_key();
  const char *zCkey = chng_key();

  if( !strncmp(zData,zWkey,strlen(zWkey)) ){
    output_formatted(&zData[strlen(zWkey)],0);
  }else if( !strncmp(zData,zTkey,strlen(zTkey)) ){
    output_ticket(atoi(&zData[strlen(zTkey)]),rn);
  }else if( !strncmp(zData,zCkey,strlen(zCkey)) ){
    output_chng(atoi(&zData[strlen(zCkey)]));
  }else{
    @ %h(zData)
  }
}

static void column_header(int rn,const char *zCol, int nCol, int nSorted,
    const char *zDirection, const char *zExtra
){
  int set = (nCol==nSorted);
  int desc = !strcmp(zDirection,"DESC");

  /*
  ** Clicking same column header 3 times in a row resets any sorting.
  ** Note that we link to rptview, which means embedded reports will get
  ** sent to the actual report view page as soon as a user tries to do
  ** any sorting. I don't see that as a Bad Thing.
  */
  if(set && desc){
    @ <th bgcolor="%s(BG1)" class="bkgnd1">
    @   <a href="rptview?rn=%d(rn)%s(zExtra)">%h(zCol)</a></th>
  }else{
    if(set){
      @ <th bgcolor="%s(BG1)" class="bkgnd1"><a
    }else{
      @ <th><a
    }
    @ href="rptview?rn=%d(rn)&amp;order_by=%d(nCol)&amp;\
    @ order_dir=%s(desc?"ASC":"DESC")\
    @ %s(zExtra)">%h(zCol)</a></th>
  }
}

/*********************************************************************/
struct GenerateHTML {
  int rn;
  int nCount;
};

/*
** The callback function for db_query
*/
static int generate_html(
  void* pUser,     /* Pointer to output state */
  int nArg,        /* Number of columns in this result row */
  char **azArg,    /* Text of data in all columns */
  char **azName    /* Names of the columns */
){
  struct GenerateHTML* pState = (struct GenerateHTML*)pUser;
  int i;
  int tn;            /* Ticket number.  (value of column named '#') */
  int rn;            /* Report number */
  int ncol;          /* Number of columns in the table */
  int multirow;      /* True if multiple table rows per line of data */
  int newrowidx;     /* Index of first column that goes on a separate row */
  int iBg = -1;      /* Index of column that determines background color */
  char *zBg = 0;     /* Use this background color */
  char zPage[30];    /* Text version of the ticket number */

  /* Get the report number
  */
  rn = pState->rn;

  /* Figure out the number of columns, the column that determines background
  ** color, and whether or not this row of data is represented by multiple
  ** rows in the table.
  */
  ncol = 0;
  multirow = 0;
  newrowidx = -1;
  for(i=0; i<nArg; i++){
    if( azName[i][0]=='b' && strcmp(azName[i],"bgcolor")==0 ){
      zBg = azArg ? azArg[i] : 0;
      iBg = i;
      continue;
    }
    if( g.okWrite && azName[i][0]=='#' ){
      ncol++;
    }
    if( !multirow ){
      if( azName[i][0]=='_' ){
        multirow = 1;
        newrowidx = i;
      }else{
        ncol++;
      }
    }
  }

  /* The first time this routine is called, output a table header
  */
  if( pState->nCount==0 ){
    char zExtra[2000];
    int nField = atoi(PD("order_by","0"));
    const char* zDir = PD("order_dir","");
    zDir = !strcmp("ASC",zDir) ? "ASC" : "DESC";
    zExtra[0] = 0;

    if( g.nAux ){
      @ <tr>
      @ <td colspan=%d(ncol)><form action="rptview" method="GET">
      @ <input type="hidden" name="rn" value="%d(rn)">
      for(i=0; i<g.nAux; i++){
        const char *zN = g.azAuxName[i];
        const char *zP = g.azAuxParam[i];
        if( g.azAuxVal[i] && g.azAuxVal[i][0] ){
          appendf(zExtra,0,sizeof(zExtra),
                  "&amp;%t=%t",g.azAuxParam[i],g.azAuxVal[i]);
        }
        if( g.azAuxOpt[i] ){
          @ %h(zN): 
          if( g.anAuxCols[i]==1 ) {
            cgi_v_optionmenu( 0, zP, g.azAuxVal[i], g.azAuxOpt[i] );
          }else if( g.anAuxCols[i]==2 ){
            cgi_v_optionmenu2( 0, zP, g.azAuxVal[i], g.azAuxOpt[i] );
          }
        }else{
          @ %h(zN): <input type="text" name="%h(zP)" value="%h(g.azAuxVal[i])">
        }
      }
      @ <input type="submit" value="Go">
      @ </form></td></tr> 
    }
    @ <tr>
    tn = -1;
    for(i=0; i<nArg; i++){
      char *zName = azName[i];
      if( i==iBg ) continue;
      if( newrowidx>=0 && i>=newrowidx ){
        if( g.okWrite && tn>=0 ){
          @ <th>&nbsp;</th>
          tn = -1;
        }
        if( zName[0]=='_' ) zName++;
        @ </tr><tr><th colspan=%d(ncol)>%h(zName)</th>
      }else{
        if( zName[0]=='#' ){
          tn = i;
        }
        /*
        ** This handles any sorting related stuff. Note that we don't
        ** bother trying to sort on the "wiki format" columns. I don't
        ** think it makes much sense, visually.
        */
        column_header(rn,azName[i],i+1,nField,zDir,zExtra);
      }
    }
    if( g.okWrite && tn>=0 ){
      @ <th>&nbsp;</th>
    }
    @ </tr>
  }
  if( azArg==0 ){
    @ <tr><td colspan="%d(ncol)">
    @ <i>No records match the report criteria</i>
    @ </td></tr>
    return 0;
  }
  ++pState->nCount;

  /* Output the separator above each entry in a table which has multiple lines
  ** per database entry.
  */
  if( newrowidx>=0 ){
    @ <tr><td colspan=%d(ncol)><font size=1>&nbsp;</font></td></tr>
  }

  /* Output the data for this entry from the database
  */
  if( zBg==0 ) zBg = "white";
  @ <tr bgcolor="%h(zBg)">
  tn = 0;
  zPage[0] = 0;
  for(i=0; i<nArg; i++){
    char *zData;
    if( i==iBg ) continue;
    zData = azArg[i];
    if( zData==0 ) zData = "";
    if( newrowidx>=0 && i>=newrowidx ){
      if( tn>0 && g.okWrite ){
        @ <td valign="top"><a href="tktedit?tn=%d(tn),%d(rn)">edit</a></td>
        tn = 0;
      }
      if( zData[0] ){
        @ </tr><tr bgcolor="%h(zBg)"><td colspan=%d(ncol)>
        output_formatted(zData, zPage[0] ? zPage : 0);
      }
    }else if( azName[i][0]=='#' ){
      tn = atoi(zData);
      if( tn>0 ) bprintf(zPage, sizeof(zPage), "%d", tn);
      @ <td valign="top"><a href="tktview?tn=%d(tn),%d(rn)">%h(zData)</a></td>
    }else if( zData[0]==0 ){
      @ <td valign="top">&nbsp;</td>
    }else{
      @ <td valign="top">
      output_report_field(zData,rn);
      @ </td>
    }
  }
  if( tn>0 && g.okWrite ){
    @ <td valign="top"><a href="tktedit?tn=%d(tn),%d(rn)">edit</a></td>
  }
  @ </tr>
  return 0;
}

/*
** Output the text given in the argument.  Convert tabs and newlines into
** spaces.
*/
static void output_no_tabs(const char *z){
  while( z && z[0] ){
    int i, j;
    for(i=0; z[i] && (!isspace(z[i]) || z[i]==' '); i++){}
    if( i>0 ){
      cgi_printf("%.*s", i, z);
    }
    for(j=i; isspace(z[j]); j++){}
    if( j>i ){
      cgi_printf("%*s", j-i, "");
    }
    z += j;
  }
}

/*
** Output a row as a tab-separated line of text.
*/
static int output_tab_separated(
  void *pUser,     /* Pointer to row-count integer */
  int nArg,        /* Number of columns in this result row */
  char **azArg,    /* Text of data in all columns */
  char **azName    /* Names of the columns */
){
  int *pCount = (int*)pUser;
  int i;

  if( *pCount==0 ){
    for(i=0; i<nArg; i++){
      output_no_tabs(azName[i]);
      cgi_printf("%c", i<nArg-1 ? '\t' : '\n');
    }
  }
  ++*pCount;
  for(i=0; i<nArg; i++){
    output_no_tabs(azArg[i]);
    cgi_printf("%c", i<nArg-1 ? '\t' : '\n');
  }
  return 0;
}

/*
** Generate HTML that describes a color key.
*/
void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){
  int i, j, k;
  char *zSafeKey, *zToFree;
  while( isspace(*zClrKey) ) zClrKey++;
  if( zClrKey[0]==0 ) return;
  @ <table %s(zTabArgs)>
  if( horiz ){
    @ <tr>
  }
  zToFree = zSafeKey = mprintf("%h", zClrKey);
  while( zSafeKey[0] ){
    while( isspace(*zSafeKey) ) zSafeKey++;
    for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){}
    for(j=i; isspace(zSafeKey[j]); j++){}
    for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){}
    if( !horiz ){
      cgi_printf("<tr bgcolor=\"%.*s\"><td>%.*s</td></tr>\n",
        i, zSafeKey, k-j, &zSafeKey[j]);
    }else{
      cgi_printf("<td bgcolor=\"%.*s\">%.*s</td>\n",
        i, zSafeKey, k-j, &zSafeKey[j]);
    }
    zSafeKey += k;
  }
  free(zToFree);
  if( horiz ){
    @ </tr>
  }
  @ </table>
}


/*
** WEBPAGE: /rptview
**
** Generate a report.  The rn query parameter is the report number
** corresponding to REPORTFMT.RN.  If the tablist query parameter exists,
** then the output consists of lines of tab-separated fields instead of
** an HTML table.
*/
void rptview_page(void){
  int count = 0;
  int rn;
  char *zSql;
  char *zTitle;
  char *zOwner;
  char *zClrKey;
  int tabs;
  Stmt q;

  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }
  rn = atoi(PD("rn","0"));
  if( rn==0 ){
    cgi_redirect("reportlist");
    return;
  }
  tabs = P("tablist")!=0;
  view_add_functions(tabs);
  db_prepare(&q,
    "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn);
  if( db_step(&q)!=SQLITE_ROW ){
    cgi_redirect("reportlist");
    return;
  }
  zTitle = db_column_malloc(&q, 0);
  zSql = db_column_malloc(&q, 1);
  zOwner = db_column_malloc(&q, 2);
  zClrKey = db_column_malloc(&q, 3);
  db_finalize(&q);

  if( P("order_by") ){
    /*
    ** If the user wants to do a column sort, wrap the query into a sub
    ** query and then sort the results. This is a whole lot easier than
    ** trying to insert an ORDER BY into the query itself, especially
    ** if the query is already ordered.
    */
    int nField = atoi(P("order_by"));
    if( nField > 0 ){
      const char* zDir = PD("order_dir","");
      zDir = !strcmp("ASC",zDir) ? "ASC" : "DESC";
      zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir);
    }
  }

  count = 0;
  if( !tabs ){
    struct GenerateHTML sState;

    db_execute("PRAGMA empty_result_callbacks=ON");
    style_submenu_element("Raw", "Raw", 
      "rptview?tablist=1&%s", P("QUERY_STRING",""));
    if( g.okAdmin 
       || (g.okQuery && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){
      style_submentu_element("Edit", "Edit", "rptedit?rn=%d", rn);
    }
    style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
    style_header(zTitle);
    output_color_key(zClrKey, 1, 
        "border=0 cellpadding=3 cellspacing=0 class=\"report\"");
    @ <table border=1 cellpadding=2 cellspacing=0 class="report">
    sState.rn = rn;
    sState.nCount = 0;
    sqlite3_exec(g.db, zSql, generate_html, &sState, 0);
    @ </table>
    style_footer();
  }else{
    sqlite3_exec(g.db, zSql, output_tab_separated, &count, 0);
    cgi_set_content_type("text/plain");
  }
}

Added src/rss.c.





























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to create a RSS feed for the CGI interface.
*/
#include "config.h"
#include "rss.h"
#include <assert.h>
#include <time.h>

time_t rss_datetime_to_time_t(const char *dt){
  struct tm the_tm;

  the_tm.tm_year = atoi(dt)-1900;
  the_tm.tm_mon  = atoi(&dt[5])-1;
  the_tm.tm_mday = atoi(&dt[8]);
  the_tm.tm_hour = atoi(&dt[11]);
  the_tm.tm_min  = atoi(&dt[14]);
  the_tm.tm_sec  = atoi(&dt[17]);

  return mktime(&the_tm);
}

/*
** WEBPAGE: timeline.rss
*/

void page_timeline_rss(void){
  Stmt q;
  int nLine=0;
  char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
  const char zSQL[] =
    @ SELECT
    @   blob.rid,
    @   uuid,
    @   datetime(event.mtime),
    @   coalesce(ecomment,comment),
    @   coalesce(euser,user),
    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),
    @   (SELECT count(*) FROM plink WHERE cid=blob.rid)
    @ FROM event, blob
    @ WHERE blob.rid=event.objid
    @ ORDER BY event.mtime DESC
  ;

  cgi_set_content_type("application/rss+xml");

  zProjectName = db_get("project-name", 0);
  if( zProjectName==0 ){
    zFreeProjectName = zProjectName = mprintf("Fossil source repository for: %s",
      g.zBaseURL);
  }
  zProjectDescr = db_get("project-description", 0);
  if( zProjectDescr==0 ){
    zProjectDescr = zProjectName;
  }

  zPubDate = cgi_rfc822_datestamp(time(NULL));

  @ <?xml version="1.0"?>
  @ <rss version="2.0">
  @   <channel>
  @     <title>%s(zProjectName)</title>
  @     <link>%s(g.zBaseURL)</link>
  @     <description>%s(zProjectDescr)</description>
  @     <pubDate>%s(zPubDate)</pubDate>
  @     <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator>
  db_prepare(&q, zSQL);
  while( db_step(&q)==SQLITE_ROW && nLine<=20 ){
    const char *zId = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);
    const char *zAuthor = db_column_text(&q, 4);
    char *zPrefix = "";
    int nChild = db_column_int(&q, 5);
    int nParent = db_column_int(&q, 6);

    zDate = cgi_rfc822_datestamp(rss_datetime_to_time_t(zDate));

    if( nParent>1 && nChild>1 ){
      zPrefix = "*MERGE/FORK* ";
    }else if( nParent>1 ){
      zPrefix = "*MERGE* ";
    }else if( nChild>1 ){
      zPrefix = "*FORK* ";
    }

    @     <item>
    @       <title>%s(zPrefix)%s(zCom)</title>
    @       <link>%s(g.zBaseURL)/vinfo/%s(zId)</link>
    @       <description>%s(zPrefix)%s(zCom)</description>
    @       <pubDate>%s(zDate)</pubDate>
    @       <author>%s(zAuthor)</author>
    @       <guid>%s(g.zBaseURL)/vinfo/%s(zId)</guid>
    @     </item>
    nLine++;
  }

  db_finalize(&q);
  @   </channel>
  @ </rss>

  if( zFreeProjectName != 0 ){
    free( zFreeProjectName );
  }
}

Changes to src/schema.c.

113
114
115
116
117
118
119


















120

121
122
123
124
125
126
127
128
129
130
131



132
133
134
135
136
137
138
...
150
151
152
153
154
155
156
157
158
159
160



161

162
163
164
165
166
167
168
169
170
171
172
...
188
189
190
191
192
193
194
195

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










208
209
210





211
212
213

214
215










216
217
218
219
220
221
222
@ -- in the form of name-value pairs.
@ --
@ CREATE TABLE config(
@   name TEXT PRIMARY KEY NOT NULL,  -- Primary name of the entry
@   value CLOB,                      -- Content of the named parameter
@   CHECK( typeof(name)='text' AND length(name)>=1 )
@ );


















;

const char zRepositorySchema2[] =
@ -- Filenames
@ --
@ CREATE TABLE filename(
@   fnid INTEGER PRIMARY KEY,    -- Filename ID
@   name TEXT UNIQUE             -- Name of file page
@ );
@
@ -- Linkages between manifests, files created by that manifest, and
@ -- the names of those files.
@ --



@ CREATE TABLE mlink(
@   mid INTEGER REFERENCES blob,        -- Manifest ID where change occurs
@   pid INTEGER REFERENCES blob,        -- File ID in parent manifest
@   fid INTEGER REFERENCES blob,        -- Changed file ID in this manifest
@   fnid INTEGER REFERENCES filename    -- Name of the file
@ );
@ CREATE INDEX mlink_i1 ON mlink(mid);
................................................................................
@   UNIQUE(pid, cid)
@ );
@ CREATE INDEX plink_i2 ON plink(cid);
@
@ -- Events used to generate a timeline
@ --
@ CREATE TABLE event(
@   type TEXT,
@   mtime DATETIME,
@   objid INTEGER,
@   uid INTEGER REFERENCES user,



@   user TEXT,

@   comment TEXT
@ );
@ CREATE INDEX event_i1 ON event(mtime);
@ CREATE INDEX event_i2 ON event(objid);
@
@ -- A record of phantoms.  A phantom is a record for which we know the
@ -- UUID but we do not (yet) know the file content.
@ --
@ CREATE TABLE phantom(
@   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
@ );
................................................................................
@ -- used to reduce push operations to a single HTTP request in the
@ -- common case when one repository only talks to a single server.
@ --
@ CREATE TABLE unsent(
@   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
@ );
@
@ -- Aggregated ticket information

@ --
@ CREATE TABLE tkt(
@   tktid INTEGER PRIMARY KEY,           -- Internal ticket ID
@   fnid INTEGER REFERENCES filename,    -- Name of the ticket file
@   rid INTEGER REFERENCES blob,         -- version of ticket file scanned
@   title TEXT,                          -- title of the ticket
@   remarks TEXT                         -- text of the ticket
@ );
@ CREATE TABLE tkttag(
@   tagid INTEGER PRIMARY KEY,           -- Numeric tag ID
@   name TEXT UNIQUE                     -- Human-readable name of tag
@ );










@ CREATE TABLE tktmap(
@   tktid INTEGER REFERENCES tkt,        -- This ticket
@   tagid INTEGER REFERENCES tkttag,     --    ....holds this tag





@   UNIQUE(tktid, tagid)
@ );
@ CREATE INDEX tktmap_i2 ON tktmap(tagid);

;











/*
** The schema for the locate FOSSIL database file found at the root
** of very check-out.  This database contains the complete state of
** the checkout.
*/
const char zLocalSchema[] =
@ -- The VVAR table holds miscellanous information about the local database







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

>











>
>
>







 







|
|
|
|
>
>
>
|
>
|


<







 







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

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

<
>


>
>
>
>
>
>
>
>
>
>







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
...
213
214
215
216
217
218
219
220
221
222

223
224
225


226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
@ -- in the form of name-value pairs.
@ --
@ CREATE TABLE config(
@   name TEXT PRIMARY KEY NOT NULL,  -- Primary name of the entry
@   value CLOB,                      -- Content of the named parameter
@   CHECK( typeof(name)='text' AND length(name)>=1 )
@ );
@
@ -- Artifacts that should not be processed are identified in the
@ -- "shun" table.  Artifacts that are control-file forgeries or
@ -- spam can be shunned in order to prevent them from contaminating
@ -- the repository.
@ --
@ CREATE TABLE shun(uuid UNIQUE);
@
@ -- An entry in this table describes a database query that generates a
@ -- table of tickets.
@ --
@ CREATE TABLE reportfmt(
@    rn integer primary key,  -- Report number
@    owner text,              -- Owner of this report format (not used)
@    title text,              -- Title of this report
@    cols text,               -- A color-key specification
@    sqlcode text             -- An SQL SELECT statement for this report
@ );
;

const char zRepositorySchema2[] =
@ -- Filenames
@ --
@ CREATE TABLE filename(
@   fnid INTEGER PRIMARY KEY,    -- Filename ID
@   name TEXT UNIQUE             -- Name of file page
@ );
@
@ -- Linkages between manifests, files created by that manifest, and
@ -- the names of those files.
@ --
@ -- pid==0 if the file is added by check-in mid.
@ -- fid==0 if the file is removed by check-in mid.
@ --
@ CREATE TABLE mlink(
@   mid INTEGER REFERENCES blob,        -- Manifest ID where change occurs
@   pid INTEGER REFERENCES blob,        -- File ID in parent manifest
@   fid INTEGER REFERENCES blob,        -- Changed file ID in this manifest
@   fnid INTEGER REFERENCES filename    -- Name of the file
@ );
@ CREATE INDEX mlink_i1 ON mlink(mid);
................................................................................
@   UNIQUE(pid, cid)
@ );
@ CREATE INDEX plink_i2 ON plink(cid);
@
@ -- Events used to generate a timeline
@ --
@ CREATE TABLE event(
@   type TEXT,                      -- Type of event
@   mtime DATETIME,                 -- Date and time when the event occurs
@   objid INTEGER PRIMARY KEY,      -- Associated record ID
@   uid INTEGER REFERENCES user,    -- User who caused the event
@   bgcolor TEXT,                   -- Color set by 'bgcolor' property
@   brbgcolor TEXT,                 -- Color set by 'br-bgcolor' property
@   euser TEXT,                     -- User set by 'user' property
@   user TEXT,                      -- Name of the user
@   ecomment TEXT,                  -- Comment set by 'comment' property
@   comment TEXT                    -- Comment describing the event
@ );
@ CREATE INDEX event_i1 ON event(mtime);

@
@ -- A record of phantoms.  A phantom is a record for which we know the
@ -- UUID but we do not (yet) know the file content.
@ --
@ CREATE TABLE phantom(
@   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
@ );
................................................................................
@ -- used to reduce push operations to a single HTTP request in the
@ -- common case when one repository only talks to a single server.
@ --
@ CREATE TABLE unsent(
@   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
@ );
@
@ -- Each baseline or manifest can have one or more tags.  A tag
@ -- is defined by a row in the next table.
@ -- 

@ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of
@ -- the wiki page.  Tickets changes are tagged with "ticket-UUID" where 
@ -- UUID is the indentifier of the ticket.


@ --
@ CREATE TABLE tag(
@   tagid INTEGER PRIMARY KEY,       -- Numeric tag ID
@   tagname TEXT UNIQUE              -- Tag name.
@ );
@ INSERT INTO tag VALUES(1, 'bgcolor');         -- TAG_BGCOLOR
@ INSERT INTO tag VALUES(2, 'comment');         -- TAG_COMMENT
@ INSERT INTO tag VALUES(3, 'user');            -- TAG_USER
@ INSERT INTO tag VALUES(4, 'hidden');          -- TAG_HIDDEN
@
@ -- Assignments of tags to baselines.  Note that we allow tags to
@ -- have values assigned to them.  So we are not really dealing with
@ -- tags here.  These are really properties.  But we are going to
@ -- keep calling them tags because in many cases the value is ignored.
@ --
@ CREATE TABLE tagxref(

@   tagid INTEGER REFERENCES tag,   -- The tag that added or removed
@   tagtype INTEGER,                -- 0:cancel  1:single  2:branch
@   srcid INTEGER REFERENCES blob,  -- Origin of the tag. 0 for propagated tags
@   value TEXT,                     -- Value of the tag.  Might be NULL.
@   mtime TIMESTAMP,                -- Time of addition or removal
@   rid INTEGER REFERENCE blob,     -- Baseline that tag added/removed from
@   UNIQUE(rid, tagid)
@ );

@ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
;

/*
** Predefined tagid values
*/
#if INTERFACE
# define TAG_BGCOLOR    1
# define TAG_COMMENT    2
# define TAG_USER       3
# define TAG_HIDDEN     4
#endif

/*
** The schema for the locate FOSSIL database file found at the root
** of very check-out.  This database contains the complete state of
** the checkout.
*/
const char zLocalSchema[] =
@ -- The VVAR table holds miscellanous information about the local database

Changes to src/setup.c.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69










70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
...
121
122
123
124
125
126
127

128
129
130
131
132
133
134
...
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
...
195
196
197
198
199
200
201

202
203
204
205
206
207
208
...
213
214
215
216
217
218
219

220
221
222
223
224
225
226
...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274

275
276
277
278
279
280
281
...
319
320
321
322
323
324
325

326
327
328
329
330
331
332
...
359
360
361
362
363
364
365
366


367
368
369
370
371
372
373
374
375
376
377
378
...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
...
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465






















466
467
468
469
470
471
472
...
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499


500


























501
502
503
504
505
506
507
...
513
514
515
516
517
518
519














520
















521
522
523
524
525
526





527
528
529
530
531
532
533
534
535
536
537
538







539






540










541




























542

543





544
545
546



















































** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
................................................................................

/*
** Output a single entry for a menu generated using an HTML table.
** If zLink is not NULL or an empty string, then it is the page that
** the menu entry will hyperlink to.  If zLink is NULL or "", then
** the menu entry has no hyperlink - it is disabled.
*/
static void menu_entry(
  const char *zTitle,
  const char *zLink,
  const char *zDesc
){
  @ <dt>
  if( zLink && zLink[0] ){
    @ <a href="%s(zLink)">%h(zTitle)</a>
  }else{
    @ %h(zTitle)
  }
  @ </dt>
  @ <dd>%h(zDesc)</dd>
}

/*
** WEBPAGE: /setup
*/
void setup_page(void){
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }

  style_header("Setup");
  @ <dl id="setup">
  menu_entry("Users", "setup_ulist",
    "Grant privileges to individual users.");
  menu_entry("Access", "setup_access",
    "Control access settings.");
  menu_entry("Configuration", "setup_config",
    "Configure the WWW components of the repository");










  @ </dl>

  style_footer();
}

/*
** WEBPAGE: setup_ulist
**
** Show a list of users.  Clicking on any user jumps to the edit
** screen for that user.
*/
void setup_ulist(void){
  Stmt s;
  
  style_footer();
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
    return;
  }

  style_submenu_element("Add", "Add User", "setup_uedit");
................................................................................
  @ <b>Notes:</b>
  @ <ol>
  @ <li><p>The permission flags are as follows:</p>
  @ <ol type="a">
  @ <li value="1"><b>Admin</b>: Create and delete users</li>
  @ <li value="3"><b>Append-Tkt</b>: Append to tickets</li>
  @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>

  @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
  @ <li value="7"><b>Clone</b>: Clone the repository</li>
  @ <li value="8"><b>History</b>: View detail repository history</li>
  @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
  @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
  @ <li value="11"><b>Write-Wiki</b>: Edit wiki pages</li>
  @ <li value="13"><b>Append-Wiki</b>: Append to wiki pages</li>
................................................................................
}

/*
** WEBPAGE: /setup_uedit
*/
void user_edit(void){
  const char *zId, *zLogin, *zInfo, *zCap;
  char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ;
  char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag;
  int doWrite;
  int uid;
  int higherUser = 0;  /* True if user being edited is SETUP and the */
                       /* user doing the editing is ADMIN.  Disallow editing */

  /* Must have ADMIN privleges to access this page
  */
................................................................................
  if( doWrite ){
    const char *zPw;
    const char *zLogin;
    char zCap[30];
    int i = 0;
    int aa = P("aa")!=0;
    int ad = P("ad")!=0;

    int ai = P("ai")!=0;
    int aj = P("aj")!=0;
    int ak = P("ak")!=0;
    int an = P("an")!=0;
    int ao = P("ao")!=0;
    int ap = P("ap")!=0;
    int aq = P("aq")!=0;
................................................................................
    int af = P("af")!=0;
    int am = P("am")!=0;
    int ah = P("ah")!=0;
    int ag = P("ag")!=0;
    if( aa ){ zCap[i++] = 'a'; }
    if( ac ){ zCap[i++] = 'c'; }
    if( ad ){ zCap[i++] = 'd'; }

    if( af ){ zCap[i++] = 'f'; }
    if( ah ){ zCap[i++] = 'h'; }
    if( ag ){ zCap[i++] = 'g'; }
    if( ai ){ zCap[i++] = 'i'; }
    if( aj ){ zCap[i++] = 'j'; }
    if( ak ){ zCap[i++] = 'k'; }
    if( am ){ zCap[i++] = 'm'; }
................................................................................

    zCap[i] = 0;
    zPw = P("pw");
    if( zPw==0 || zPw[0]==0 ){
      zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
    }
    zLogin = P("login");
    if( uid>0 && 
        db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
    ){
      style_header("User Creation Error");
      @ <font color="red">Login "%h(zLogin)" is already used by a different
      @ user.</font>
      @
      @ <p><a href="setup_uedit?id=%d(uid))>[Bummer]</a></p>
................................................................................
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  oaa = oac = oad = oaf = oag = oah = oai = oaj = oak = oam =
        oan = oao = oap = oaq = oar = oas = oaw = "";
  if( uid ){
    zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
    zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
    zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
    if( strchr(zCap, 'a') ) oaa = " checked";
    if( strchr(zCap, 'c') ) oac = " checked";
    if( strchr(zCap, 'd') ) oad = " checked";

    if( strchr(zCap, 'f') ) oaf = " checked";
    if( strchr(zCap, 'g') ) oag = " checked";
    if( strchr(zCap, 'h') ) oah = " checked";
    if( strchr(zCap, 'i') ) oai = " checked";
    if( strchr(zCap, 'j') ) oaj = " checked";
    if( strchr(zCap, 'k') ) oak = " checked";
    if( strchr(zCap, 'm') ) oam = " checked";
................................................................................
  @   <td align="right" valign="top">Capabilities:</td>
  @   <td>
  if( g.okSetup ){
    @     <input type="checkbox" name="as"%s(oas)>Setup</input><br>
  }
  @     <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
  @     <input type="checkbox" name="ad"%s(oad)>Delete</input><br>

  @     <input type="checkbox" name="ap"%s(oap)>Password</input><br>
  @     <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
  @     <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
  @     <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
  @     <input type="checkbox" name="ah"%s(oah)>History</input><br>
  @     <input type="checkbox" name="ag"%s(oag)>Clone</input><br>
  @     <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br>
................................................................................
    @ </p></li>
    @
  }
  @
  @ <li><p>
  @ The <b>Delete</b> privilege give the user the ability to erase
  @ wiki, tickets, and atttachments that have been added by anonymous
  @ users.  This capability is intended for deletion of spam.


  @ </p></li>
  @
  @ <li><p>
  @ The <b>Query</b> privilege allows the user to create or edit
  @ report formats by specifying appropriate SQL.  Users can run 
  @ existing reports without the Query privilege.
  @ </p></li>
  @
  @ <li><p>
  @ An <b>Admin</b> user can add other users, create new ticket report
  @ formats, and change system defaults.  But only the <b>Setup</b> user
  @ is able to change the repository to
................................................................................
  }
  if( zQ==0 && P("submit") ){
    zQ = "off";
  }
  if( zQ ){
    int iQ = strcmp(zQ,"on")==0 || atoi(zQ);
    if( iQ!=iVal ){
      db_set(zVar, iQ ? "1" : "0");
      iVal = iQ;
    }
  }
  if( iVal ){
    @ <input type="checkbox" name="%s(zQParm)" checked>%s(zLabel)</input>
  }else{
    @ <input type="checkbox" name="%s(zQParm)">%s(zLabel)</input>
................................................................................
** Generate an entry box for an attribute.
*/
static void entry_attribute(
  const char *zLabel,   /* The text label on the entry box */
  int width,            /* Width of the entry box */
  const char *zVar,     /* The corresponding row in the VAR table */
  const char *zQParm,   /* The query parameter */
  const char *zDflt     /* Default value if VAR table entry does not exist */
){
  const char *zVal = db_get(zVar, zDflt);
  const char *zQ = P(zQParm);
  if( zQ && strcmp(zQ,zVal)!=0 ){
    db_set(zVar, zQ);
    zVal = zQ;
  }
  @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)">
  @ %s(zLabel)
}

























/*
** WEBPAGE: setup_access
*/
void setup_access(void){
  login_check_credentials();
................................................................................

  style_header("Access Control Settings");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/setup_access" method="POST">

  @ <hr>
  onoff_attribute("Require password for local access",
     "authenticate-localhost", "localauth", 1);
  @ <p>When enabled, the password sign-in is required for
  @ web access coming from 127.0.0.1.  When disabled, web access
  @ from 127.0.0.1 is allows without any login - the user id is selected
  @ from the ~/.fossil database. Password login is always required
  @ for incoming web connections on internet addresses other than
  @ 127.0.0.1.</p></li>

  @ <hr>
  entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
  @ <p>The number of hours for which a login is valid.  This must be a
  @ positive number.  The default is 8760 hours which is approximately equal
  @ to a year.</p>
   
  @ <hr>
  onoff_attribute("Allow anonymous signup", "anon-signup", "asu", 0);
  @ <p>Allow users to create their own accounts</p>


   


























  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </form>
  db_end_transaction(0);
  style_footer();
}

................................................................................
  if( !g.okSetup ){
    login_needed();
  }

  style_header("WWW Configuration");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/setup_config" method="POST">































  @ <hr>
  entry_attribute("Home page", 60, "homepage", "hp", "");
  @ <p>The name of a wiki file that is the homepage for the website.
  @ The home page is the page that is displayed by the "Home" link
  @ at the top of this screen.  Omit the path and the ".wiki"
  @ suffix.  </p>






  entry_attribute("Ticket subdirectory", 60, "ticket-subdir", "tsd", "");
  @ <p>A subdirectory in the file hierarchy that contains all trouble
  @ tickets.  Leave this blank to disable ticketing.  Tickets text
  @ files within this subdirectory containing a particular format
  @ (documented separately) and with the ".tkt" suffix.</p>

  entry_attribute("Wiki subdirectory", 60, "wiki-subdir", "wsd", "");
  @ <p>A subdirectory in the file hierarchy that contains wiki pages.
  @ Leave this blank to disable wiki.  Wiki pages are
  @ files within this subdirectory whose name is he wiki page title
  @ and with the suffix ".wiki".</p>














   










  @ <hr>




























  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>

  @ </form>





  db_end_transaction(0);
  style_footer();
}


























































|







 







|




|





|
<












|
|

|

|

>
>
>
>
>
>
>
>
>
>
|












|
<







 







>







 







|
|







 







>







 







>







 







|







 







|








>







 







>







 







|
>
>




|







 







|







 







|




|






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







 







|












|

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







 







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

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

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

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

>
>
>
>
>

<

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

93
94
95
96
97
98
99
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
...
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
...
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
...
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
...
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
...
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616





617
618
619
620
621
622





623





624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
................................................................................

/*
** Output a single entry for a menu generated using an HTML table.
** If zLink is not NULL or an empty string, then it is the page that
** the menu entry will hyperlink to.  If zLink is NULL or "", then
** the menu entry has no hyperlink - it is disabled.
*/
void setup_menu_entry(
  const char *zTitle,
  const char *zLink,
  const char *zDesc
){
  @ <tr><td valign="top" align="right">
  if( zLink && zLink[0] ){
    @ <a href="%s(zLink)">%h(zTitle)</a>
  }else{
    @ %h(zTitle)
  }
  @ </td><td valign="top">%h(zDesc)</td></tr>

}

/*
** WEBPAGE: /setup
*/
void setup_page(void){
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }

  style_header("Setup");
  @ <table border="0" cellspacing="20">
  setup_menu_entry("Users", "setup_ulist",
    "Grant privileges to individual users.");
  setup_menu_entry("Access", "setup_access",
    "Control access settings.");
  setup_menu_entry("Configuration", "setup_config",
    "Configure the WWW components of the repository");
  setup_menu_entry("Timeline", "setup_timeline",
    "Timeline display preferences");
  setup_menu_entry("Tickets", "setup_ticket",
    "Configure the trouble-ticketing system for this repository");
  setup_menu_entry("CSS", "setup_editcss",
    "Edit the Cascading Style Sheet used by all pages of this repository");
  setup_menu_entry("Header", "setup_header",
    "Edit HTML text inserted at the top of every page");
  setup_menu_entry("Footer", "setup_footer",
    "Edit HTML text inserted at the bottom of every page");
  @ </table>

  style_footer();
}

/*
** WEBPAGE: setup_ulist
**
** Show a list of users.  Clicking on any user jumps to the edit
** screen for that user.
*/
void setup_ulist(void){
  Stmt s;


  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
    return;
  }

  style_submenu_element("Add", "Add User", "setup_uedit");
................................................................................
  @ <b>Notes:</b>
  @ <ol>
  @ <li><p>The permission flags are as follows:</p>
  @ <ol type="a">
  @ <li value="1"><b>Admin</b>: Create and delete users</li>
  @ <li value="3"><b>Append-Tkt</b>: Append to tickets</li>
  @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>
  @ <li value="5"><b>Email</b>: View EMail addresses on tickets</li>
  @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
  @ <li value="7"><b>Clone</b>: Clone the repository</li>
  @ <li value="8"><b>History</b>: View detail repository history</li>
  @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
  @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
  @ <li value="11"><b>Write-Wiki</b>: Edit wiki pages</li>
  @ <li value="13"><b>Append-Wiki</b>: Append to wiki pages</li>
................................................................................
}

/*
** WEBPAGE: /setup_uedit
*/
void user_edit(void){
  const char *zId, *zLogin, *zInfo, *zCap;
  char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
  char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag, *oae;
  int doWrite;
  int uid;
  int higherUser = 0;  /* True if user being edited is SETUP and the */
                       /* user doing the editing is ADMIN.  Disallow editing */

  /* Must have ADMIN privleges to access this page
  */
................................................................................
  if( doWrite ){
    const char *zPw;
    const char *zLogin;
    char zCap[30];
    int i = 0;
    int aa = P("aa")!=0;
    int ad = P("ad")!=0;
    int ae = P("ae")!=0;
    int ai = P("ai")!=0;
    int aj = P("aj")!=0;
    int ak = P("ak")!=0;
    int an = P("an")!=0;
    int ao = P("ao")!=0;
    int ap = P("ap")!=0;
    int aq = P("aq")!=0;
................................................................................
    int af = P("af")!=0;
    int am = P("am")!=0;
    int ah = P("ah")!=0;
    int ag = P("ag")!=0;
    if( aa ){ zCap[i++] = 'a'; }
    if( ac ){ zCap[i++] = 'c'; }
    if( ad ){ zCap[i++] = 'd'; }
    if( ae ){ zCap[i++] = 'e'; }
    if( af ){ zCap[i++] = 'f'; }
    if( ah ){ zCap[i++] = 'h'; }
    if( ag ){ zCap[i++] = 'g'; }
    if( ai ){ zCap[i++] = 'i'; }
    if( aj ){ zCap[i++] = 'j'; }
    if( ak ){ zCap[i++] = 'k'; }
    if( am ){ zCap[i++] = 'm'; }
................................................................................

    zCap[i] = 0;
    zPw = P("pw");
    if( zPw==0 || zPw[0]==0 ){
      zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
    }
    zLogin = P("login");
    if( uid>0 &&
        db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
    ){
      style_header("User Creation Error");
      @ <font color="red">Login "%h(zLogin)" is already used by a different
      @ user.</font>
      @
      @ <p><a href="setup_uedit?id=%d(uid))>[Bummer]</a></p>
................................................................................
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
        oan = oao = oap = oaq = oar = oas = oaw = "";
  if( uid ){
    zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
    zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
    zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
    if( strchr(zCap, 'a') ) oaa = " checked";
    if( strchr(zCap, 'c') ) oac = " checked";
    if( strchr(zCap, 'd') ) oad = " checked";
    if( strchr(zCap, 'e') ) oae = " checked";
    if( strchr(zCap, 'f') ) oaf = " checked";
    if( strchr(zCap, 'g') ) oag = " checked";
    if( strchr(zCap, 'h') ) oah = " checked";
    if( strchr(zCap, 'i') ) oai = " checked";
    if( strchr(zCap, 'j') ) oaj = " checked";
    if( strchr(zCap, 'k') ) oak = " checked";
    if( strchr(zCap, 'm') ) oam = " checked";
................................................................................
  @   <td align="right" valign="top">Capabilities:</td>
  @   <td>
  if( g.okSetup ){
    @     <input type="checkbox" name="as"%s(oas)>Setup</input><br>
  }
  @     <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
  @     <input type="checkbox" name="ad"%s(oad)>Delete</input><br>
  @     <input type="checkbox" name="ae"%s(oad)>Email</input><br>
  @     <input type="checkbox" name="ap"%s(oap)>Password</input><br>
  @     <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
  @     <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
  @     <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
  @     <input type="checkbox" name="ah"%s(oah)>History</input><br>
  @     <input type="checkbox" name="ag"%s(oag)>Clone</input><br>
  @     <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br>
................................................................................
    @ </p></li>
    @
  }
  @
  @ <li><p>
  @ The <b>Delete</b> privilege give the user the ability to erase
  @ wiki, tickets, and atttachments that have been added by anonymous
  @ users.  This capability is intended for deletion of spam.  The
  @ delete capability is only in effect for 24 hours after the item
  @ is first posted.  The Setup user can delete anything at any time.
  @ </p></li>
  @
  @ <li><p>
  @ The <b>Query</b> privilege allows the user to create or edit
  @ report formats by specifying appropriate SQL.  Users can run
  @ existing reports without the Query privilege.
  @ </p></li>
  @
  @ <li><p>
  @ An <b>Admin</b> user can add other users, create new ticket report
  @ formats, and change system defaults.  But only the <b>Setup</b> user
  @ is able to change the repository to
................................................................................
  }
  if( zQ==0 && P("submit") ){
    zQ = "off";
  }
  if( zQ ){
    int iQ = strcmp(zQ,"on")==0 || atoi(zQ);
    if( iQ!=iVal ){
      db_set(zVar, iQ ? "1" : "0", 0);
      iVal = iQ;
    }
  }
  if( iVal ){
    @ <input type="checkbox" name="%s(zQParm)" checked>%s(zLabel)</input>
  }else{
    @ <input type="checkbox" name="%s(zQParm)">%s(zLabel)</input>
................................................................................
** Generate an entry box for an attribute.
*/
static void entry_attribute(
  const char *zLabel,   /* The text label on the entry box */
  int width,            /* Width of the entry box */
  const char *zVar,     /* The corresponding row in the VAR table */
  const char *zQParm,   /* The query parameter */
  char *zDflt     /* Default value if VAR table entry does not exist */
){
  const char *zVal = db_get(zVar, zDflt);
  const char *zQ = P(zQParm);
  if( zQ && strcmp(zQ,zVal)!=0 ){
    db_set(zVar, zQ, 0);
    zVal = zQ;
  }
  @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)">
  @ %s(zLabel)
}

/*
** Generate a text box for an attribute.
*/
static void textarea_attribute(
  const char *zLabel,   /* The text label on the textarea */
  int rows,             /* Rows in the textarea */
  int cols,             /* Columns in the textarea */
  const char *zVar,     /* The corresponding row in the VAR table */
  const char *zQP,      /* The query parameter */
  const char *zDflt     /* Default value if VAR table entry does not exist */
){
  const char *z = db_get(zVar, (char*)zDflt);
  const char *zQ = P(zQP);
  if( zQ && strcmp(zQ,z)!=0 ){
    db_set(zVar, zQ, 0);
    z = zQ;
  }
  if( rows>0 && cols>0 ){
    @ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
    @ %s(zLabel)
  }
}


/*
** WEBPAGE: setup_access
*/
void setup_access(void){
  login_check_credentials();
................................................................................

  style_header("Access Control Settings");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/setup_access" method="POST">

  @ <hr>
  onoff_attribute("Require password for local access",
     "localauth", "localauth", 1);
  @ <p>When enabled, the password sign-in is required for
  @ web access coming from 127.0.0.1.  When disabled, web access
  @ from 127.0.0.1 is allows without any login - the user id is selected
  @ from the ~/.fossil database. Password login is always required
  @ for incoming web connections on internet addresses other than
  @ 127.0.0.1.</p></li>

  @ <hr>
  entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
  @ <p>The number of hours for which a login is valid.  This must be a
  @ positive number.  The default is 8760 hours which is approximately equal
  @ to a year.</p>

  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </form>
  db_end_transaction(0);
  style_footer();
}

/*
** WEBPAGE: setup_timeline
*/
void setup_timeline(void){
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }

  style_header("Timeline Display Preferences");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/setup_timeline" method="POST">

  @ <hr>
  onoff_attribute("Block markup in timeline",
                  "timeline-block-markup", "tbm", 0);
  @ <p>In timeline displays, check-in comments can be displayed with or
  @ without block markup (paragraphs, tables, etc.)</p>

  @ <hr>
  entry_attribute("Max timeline comment length", 6, 
                  "timeline-max-comment", "tmc", "0");
  @ <p>The maximum length of a comment to be displayed in a timeline.
  @ "0" there is no length limit.</p>

  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </form>
  db_end_transaction(0);
  style_footer();
}

................................................................................
  if( !g.okSetup ){
    login_needed();
  }

  style_header("WWW Configuration");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/setup_config" method="POST">
  @ <hr />
  entry_attribute("Project Name", 60, "project-name", "pn", "");
  @ <p>Give your project a name so visitors know what this site is about.
  @ The project name will also be used as the RSS feed title.</p>
  @ <hr />
  textarea_attribute("Project Description", 5, 60,
                     "project-description", "pd", "");
  @ <p>Describe your project. This will be used in page headers for search
  @ engines as well as a short RSS description.</p>
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </form>
  db_end_transaction(0);
  style_footer();
}

/*
** WEBPAGE: setup_editcss
*/
void setup_editcss(void){
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }
  style_header("Edit CSS");
  @ <form action="%s(g.zBaseURL)/setup_editcss" method="POST">
  @ Edit the CSS:<br />
  textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
  @ <br />
  @ <input type="submit" name="submit" value="Apply Changes">
  @ </form>
  @ <hr>





  @ Here is the default CSS:
  @ <blockquote><pre>
  @ %h(zDefaultCSS)
  @ </pre></blockquote>
  style_footer();
}











/*
** WEBPAGE: setup_header
*/
void setup_header(void){
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }
  db_begin_transaction();
  if( P("clear")!=0 ){
    db_multi_exec("DELETE FROM config WHERE name='header'");
    cgi_replace_parameter("header", zDefaultHeader);
  }else{
    textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
  }
  style_header("Edit Page Header");
  @ <form action="%s(g.zBaseURL)/setup_header" method="POST">
  @ <p>Edit HTML text with embedded subscript that will be used to
  @ generate the beginning of every page through start of the main
  @ menu.</p>
  textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
  @ <br />
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Revert To Default">
  @ </form>
  @ <hr>
  @ Here is the default page header:
  @ <blockquote><pre>
  @ %h(zDefaultHeader)
  @ </pre></blockquote>
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_footer
*/
void setup_footer(void){
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }
  db_begin_transaction();
  if( P("clear")!=0 ){
    db_multi_exec("DELETE FROM config WHERE name='footer'");
    cgi_replace_parameter("footer", zDefaultFooter);
  }else{
    textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
  }
  style_header("Edit Page Footer");
  @ <form action="%s(g.zBaseURL)/setup_footer" method="POST">
  @ <p>Edit HTML text with embedded subscript that will be used to
  @ generate the end of every page.</p>
  textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
  @ <br />
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Revert To Default">
  @ </form>
  @ <hr>
  @ Here is the default page footer:
  @ <blockquote><pre>
  @ %h(zDefaultFooter)
  @ </pre></blockquote>
  db_end_transaction(0);

}

/*
** WEBPAGE: setup_ticket
*/
void setup_ticket(void){
  const char *zConfig;
  int isSubmit;
  
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }
  isSubmit = P("submit")!=0;
  db_begin_transaction();
  zConfig = P("cfg");
  if( zConfig==0 ){
    zConfig = db_text((char*)zDefaultTicketConfig,
               "SELECT value FROM config WHERE name='ticket-configuration'");
  }
  style_header("Edit Ticket Configuration");
  if( P("clear")!=0 ){
    db_multi_exec("DELETE FROM config WHERE name='ticket-configuration'");
    zConfig = zDefaultTicketConfig;
  }else if( isSubmit ){
    char *zErr = ticket_config_check(zConfig);
    if( zErr==0 ){
      db_multi_exec(
         "REPLACE INTO config(name,value) VALUES('ticket-configuration',"
         "%Q)", zConfig
      );
    }else{
      @ <p><font color="red"><b>
      @ SCRIPT ERROR: %h(zErr)
      @ </b></font></p>
    }
  }
  @ <form action="%s(g.zBaseURL)/setup_ticket" method="POST">
  @ <p>Edit the "subscript" script that defines the ticketing
  @ system setup for this server.</p>
  @ <textarea name="cfg" rows="40" cols="80">%h(zConfig)</textarea>
  @ <br />
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Revert To Default">
  @ </form>
  @ <hr>
  @ Here is the default page header:
  @ <blockquote><pre>
  @ %h(zDefaultTicketConfig)
  @ </pre></blockquote>
  db_end_transaction(0);
}

Changes to src/sha1.c.

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/*
** This implementation of SHA1 is adapted from the example implementation
** contained in RFC-3174.
*/
#include <stdint.h>

#include "config.h"
#include "sha1.h"

/*
 * If you do not have the ISO standard stdint.h header file, then you
 * must typdef the following:
 *    name              meaning
 *  uint32_t         unsigned 32 bit integer
 *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)
 *  int_least16_t    integer of >= 16 bits
 *
 */
#define SHA1HashSize 20
#define shaSuccess 0
#define shaInputTooLong 1
#define shaStateError 2

................................................................................
typedef struct SHA1Context SHA1Context;
struct SHA1Context {
    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */

    uint32_t Length_Low;            /* Message length in bits      */
    uint32_t Length_High;           /* Message length in bits      */

                               /* Index into message block array   */
    int_least16_t Message_Block_Index;
    uint8_t Message_Block[64];      /* 512-bit message blocks      */

    int Computed;               /* Is the digest computed?         */
    int Corrupted;             /* Is the message digest corrupted? */
};

/*





>









<







 







|
<







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

16
17
18
19
20
21
22
..
27
28
29
30
31
32
33
34

35
36
37
38
39
40
41
/*
** This implementation of SHA1 is adapted from the example implementation
** contained in RFC-3174.
*/
#include <stdint.h>
#include <sys/types.h>
#include "config.h"
#include "sha1.h"

/*
 * If you do not have the ISO standard stdint.h header file, then you
 * must typdef the following:
 *    name              meaning
 *  uint32_t         unsigned 32 bit integer
 *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)

 *
 */
#define SHA1HashSize 20
#define shaSuccess 0
#define shaInputTooLong 1
#define shaStateError 2

................................................................................
typedef struct SHA1Context SHA1Context;
struct SHA1Context {
    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */

    uint32_t Length_Low;            /* Message length in bits      */
    uint32_t Length_High;           /* Message length in bits      */

    int Message_Block_Index;   /* Index into message block array   */

    uint8_t Message_Block[64];      /* 512-bit message blocks      */

    int Computed;               /* Is the digest computed?         */
    int Corrupted;             /* Is the message digest corrupted? */
};

/*

Changes to src/sqlite3.c.

more than 10,000 changes

Changes to src/style.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
44
45
46
47
48
49
50
51

52

53
54
55

56

57
58
59
60
61
62
63
64
65
66
67
68





69
70
71
72
73

74
75
76

77
78

79
80






81
82

83
84
85
86
87




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

107
108
109

110
111
112
113
114
115

116
117
118
119

120
121
122
123
124
125
126
127
128
129

130






131


132
133

































































































































































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

147



148
149
150
151
152
153
154


155
156
157
/*
** Copyright (c) 2006 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
................................................................................

/*
** Add a new element to the submenu
*/
void style_submenu_element(
  const char *zLabel,
  const char *zTitle,
  const char *zLink

){

  assert( nSubmenu < sizeof(aSubmenu)/sizeof(aSubmenu[0]) );
  aSubmenu[nSubmenu].zLabel = zLabel;
  aSubmenu[nSubmenu].zTitle = zTitle;

  aSubmenu[nSubmenu].zLink = zLink;

  nSubmenu++;
}

/*
** Compare two submenu items for sorting purposes
*/
static int submenuCompare(const void *a, const void *b){
  const struct Submenu *A = (const struct Submenu*)a;
  const struct Submenu *B = (const struct Submenu*)B;
  return strcmp(A->zLabel, B->zLabel);
}






/*
** Draw the header.
*/
void style_header(const char *zTitle){
  const char *zLogInOut = "Logout";

  login_check_credentials();
  @ <html>
  @ <body bgcolor="white">

  @ <hr size="1">
  @ <table border="0" cellpadding="0" cellspacing="0" width="100%%">

  @ <tr><td valign="top" align="left">
  @ <big><big><b>%s(zTitle)</b></big></big><br>






  if( g.zLogin==0 ){
    @ <small>not logged in</small>

    zLogInOut = "Login";
  }else{
    @ <small>logged in as %h(g.zLogin)</small>
  }
  @ </td><td valign="top" align="right">




  @ <a href="%s(g.zBaseURL)/index">Home</a>
  if( g.okRead ){
    @ | <a href="%s(g.zBaseURL)/leaves">Leaves</a>
    @ | <a href="%s(g.zBaseURL)/timeline">Timeline</a>
  }
  if( g.okRdWiki ){
    @ | <a href="%s(g.zBaseURL)/wiki">Wiki</a>
  }
#if 0
  @ | <font color="#888888">Search</font>
  @ | <font color="#888888">Ticket</font>
  @ | <font color="#888888">Reports</font>
#endif
  if( g.okSetup ){
    @ | <a href="%s(g.zBaseURL)/setup">Setup</a>
  }
  if( !g.noPswd ){
    @ | <a href="%s(g.zBaseURL)/login">%s(zLogInOut)</a>
  }

  if( nSubmenu>0 ){
    int i;
    @ <br>

    qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
    for(i=0; i<nSubmenu; i++){
      struct Submenu *p = &aSubmenu[i];
      char *zTail = i<nSubmenu-1 ? " | " : "";
      if( p->zLink==0 ){
        @ <font color="#888888">%h(p->zLabel)</font> %s(zTail)

      }else{
        @ <a href="%T(p->zLink)">%h(p->zLabel)</a> %s(zTail)
      }
    }

  }
  @ </td></tr></table>
  @ <hr size="1">
  g.cgiPanic = 1;
}

/*
** Draw the footer at the bottom of the page.
*/
void style_footer(void){

}









/*
** WEBPAGE: index

































































































































































** WEBPAGE: home
** WEBPAGE: not_found
*/
void page_index(void){
  char *zHome = db_get("homepage", 0);
  if( zHome ){
    g.zExtra = zHome;
    g.okRdWiki = 1;
    wiki_page();
  }else{
    style_header("Main Title Page");
    @ No homepage configured for this server
    style_footer();

  }



}

/*
** WEBPAGE: test_env
*/
void page_test_env(void){
  style_header("Environment Test");


  cgi_print_all();
  style_footer();
}

|









|







 







|
>

>



>
|
>








|



>
>
>
>
>




|
>

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

<
>
>
>
>
|

|
|


|


|
|
|


|


|

>


<
>



<

<
>

|


>

|
<







>
|
>
>
>
>
>
>
|
>
>

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

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







>
>



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84


85
86

87
88
89
90
91
92
93
94
95
96

97
98


99

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

126
127
128
129

130

131
132
133
134
135
136
137
138

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

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

320
321









322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/*
** Copyright (c) 2006,2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
................................................................................

/*
** Add a new element to the submenu
*/
void style_submenu_element(
  const char *zLabel,
  const char *zTitle,
  const char *zLink,
  ...
){
  va_list ap;
  assert( nSubmenu < sizeof(aSubmenu)/sizeof(aSubmenu[0]) );
  aSubmenu[nSubmenu].zLabel = zLabel;
  aSubmenu[nSubmenu].zTitle = zTitle;
  va_start(ap, zLink);
  aSubmenu[nSubmenu].zLink = vmprintf(zLink, ap);
  va_end(ap);
  nSubmenu++;
}

/*
** Compare two submenu items for sorting purposes
*/
static int submenuCompare(const void *a, const void *b){
  const struct Submenu *A = (const struct Submenu*)a;
  const struct Submenu *B = (const struct Submenu*)b;
  return strcmp(A->zLabel, B->zLabel);
}

/*
** The Subscript interpreter used to render header and footer.
*/
static struct Subscript *pInterp;

/*
** Draw the header.
*/
void style_header(const char *zTitle){
  const char *zLogInOut = "Login";
  const char *zHeader = db_get("header", (char*)zDefaultHeader);  
  login_check_credentials();


  
  if( pInterp ) return;


  /* Generate the header up through the main menu */
  pInterp = SbS_Create();
  SbS_Store(pInterp, "project_name",
                     db_get("project-name","Unnamed Fossil Project"), 0);
  SbS_Store(pInterp, "title", zTitle, 0);
  SbS_Store(pInterp, "baseurl", g.zBaseURL, 0);
  SbS_Store(pInterp, "manifest_version", MANIFEST_VERSION, 0);
  SbS_Store(pInterp, "manifest_date", MANIFEST_DATE, 0);
  if( g.zLogin ){

    SbS_Store(pInterp, "login", g.zLogin, 0);
    zLogInOut = "Logout";


  }

  SbS_Render(pInterp, zHeader);

  /* Generate the main menu and the submenu (if any) */
  @ <div class="mainmenu">
  @ <a href="%s(g.zBaseURL)/home">Home</a>
  if( g.okRead ){
    @ <a href="%s(g.zBaseURL)/leaves">Leaves</a>
    @ <a href="%s(g.zBaseURL)/timeline">Timeline</a>
  }
  if( g.okRdWiki ){
    @ <a href="%s(g.zBaseURL)/wiki">Wiki</a>
  }
#if 0
  @ <font color="#888888">Search</font>
  @ <font color="#888888">Ticket</font>
  @ <font color="#888888">Reports</font>
#endif
  if( g.okSetup ){
    @ <a href="%s(g.zBaseURL)/setup">Setup</a>
  }
  if( !g.noPswd ){
    @ <a href="%s(g.zBaseURL)/login">%s(zLogInOut)</a>
  }
  @ </div>
  if( nSubmenu>0 ){
    int i;

    @ <div class="submenu">
    qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
    for(i=0; i<nSubmenu; i++){
      struct Submenu *p = &aSubmenu[i];

      if( p->zLink==0 ){

        @ <span class="label">%h(p->zLabel)</span>
      }else{
        @ <a class="label" href="%s(p->zLink)">%h(p->zLabel)</a>
      }
    }
    @ </div>
  }
  @ <div class="content">

  g.cgiPanic = 1;
}

/*
** Draw the footer at the bottom of the page.
*/
void style_footer(void){
  const char *zFooter;
  
  if( pInterp==0 ) return;
  zFooter = db_get("footer", (char*)zDefaultFooter);
  @ </div>
  SbS_Render(pInterp, zFooter);
  SbS_Destroy(pInterp);
  pInterp = 0;
}

/* @-comment: // */
/*

** The default page header.
*/
const char zDefaultHeader[] = 
@ <html>
@ <head>
@ <title>[project_name html]: [title html]</title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@       href="[baseurl puts]/timeline.rss">
@ <link rel="stylesheet" href="[baseurl puts]/style.css" type="text/css"
@       media="screen">
@ </head>
@ <body>
@ <div class="header">
@   <div class="logo">
@     <!-- <img src="logo.gif" alt="logo"><br></br> -->
@     <nobr>[project_name html]</nobr>
@   </div>
@   <div class="title">[title html]</div>
@   <div class="status"><nobr>
@     [/login exists enable_output]     Logged in as [0 /login get html]
@     [/login exists not enable_output] Not logged in
@     [1 enable_output]
@   </nobr></div>
@ </div>
;

/*
** The default page footer
*/
const char zDefaultFooter[] = 
@ <div class="footer">
@ Fossil version [manifest_version puts] [manifest_date puts]
@ </div>
@ </body></html>
;

/*
** The default Cascading Style Sheet.
*/
const char zDefaultCSS[] = 
@ /* General settings for the entire page */
@ body {
@   margin: 0ex 1ex;
@   padding: 0px;
@   background-color: white;
@   font-family: "sans serif";
@ }
@
@ /* The project logo in the upper left-hand corner of each page */
@ div.logo {
@   display: table-cell;
@   text-align: center;
@   vertical-align: bottom;
@   font-weight: bold;
@   color: #558195;
@ }
@
@ /* The page title centered at the top of each page */
@ div.title {
@   display: table-cell;
@   font-size: 2em;
@   font-weight: bold;
@   text-align: center;
@   color: #558195;
@   vertical-align: bottom;
@   width: 100%;
@ }
@
@ /* The login status message in the top right-hand corner */
@ div.status {
@   display: table-cell;
@   text-align: right;
@   vertical-align: bottom;
@   color: #558195;
@   font-size: 0.8em;
@   font-weight: bold;
@ }
@
@ /* The header across the top of the page */
@ div.header {
@   display: table;
@   width: 100%;
@ }
@
@ /* The main menu bar that appears at the top of the page beneath
@ ** the header */
@ div.mainmenu {
@   padding: 5px 10px 5px 10px;
@   font-size: 0.9em;
@   font-weight: bold;
@   text-align: center;
@   letter-spacing: 1px;
@   background-color: #558195;
@   color: white;
@ }
@
@ /* The submenu bar that *sometimes* appears below the main menu */
@ div.submenu {
@   padding: 3px 10px 3px 0px;
@   font-size: 0.9em;
@   text-align: center;
@   background-color: #456878;
@   color: white;
@ }
@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
@   padding: 3px 10px 3px 10px;
@   color: white;
@   text-decoration: none;
@ }
@ div.mainmenu a:hover, div.submenu a:hover {
@   color: #558195;
@   background-color: white;
@ }
@
@ /* All page content from the bottom of the menu or submenu down to
@ ** the footer */
@ div.content {
@   padding: 0ex 1ex 0ex 2ex;
@ }
@
@ /* Some pages have section dividers */
@ div.section {
@   margin-bottom: 0px;
@   margin-top: 1em;
@   padding: 1px 1px 1px 1px;
@   font-size: 1.2em;
@   font-weight: bold;
@   background-color: #558195;
@   color: white;
@ }
@
@ /* The "Date" that occurs on the left hand side of timelines */
@ div.divider {
@   background: #a1c4d4;
@   border: 2px #558195 solid;
@   font-size: 1em; font-weight: normal;
@   padding: .25em;
@   margin: .2em 0 .2em 0;
@   float: left;
@   clear: left;
@ }
@
@ /* The footer at the very bottom of the page */
@ div.footer {
@   font-size: 0.8em;
@   margin-top: 12px;
@   padding: 5px 10px 5px 10px;
@   text-align: right;
@   background-color: #558195;
@   color: white;
@ }
@
@ /* The label/value pairs on (for example) the vinfo page */
@ table.label-value th {
@   vertical-align: top;
@   text-align: right;
@   padding: 0.2ex 2ex;
@ }
;

/*
** WEBPAGE: style.css

*/
void page_style_css(void){









  char *zCSS = 0;

  cgi_set_content_type("text/css");
  zCSS = db_get("css",(char*)zDefaultCSS);
  cgi_append_content(zCSS, -1);
}

/*
** WEBPAGE: test_env
*/
void page_test_env(void){
  style_header("Environment Test");
  @ g.zBaseURL = %h(g.zBaseURL)<br>
  @ g.zTop = %h(g.zTop)<br>
  cgi_print_all();
  style_footer();
}

Added src/subscript.c.



























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains an implementation of the "subscript" interpreter.
**
** Subscript attempts to be an extremely light-weight scripting
** language.  It contains the barest of bare essentials.  It is
** stack-based and forth-like.  Everything is in a single global
** namespace.  There is only a single datatype of zero-terminated
** string.  The stack is of fixed, limited depth.  The symbal table
** is of a limited and fixed size.
**
** TOKENS:
**
**      *  All tokens are separated from each other by whitespace.
**      *  Leading and trailing whitespace is ignored.
**      *  Text within nested {...} is a single string token.  The outermost
**         curly braces are not part of the token.
**      *  An identifier with a leading "/" is a string token.
**      *  A token that looks like a number is a string token.
**      *  An identifier token is called a "verb".
**
** PROCESSING:
**
**      *  The input is divided into tokens.  Whitespace is discarded.
**         String and verb tokens are passed into the engine.
**      *  String tokens are pushed onto the stack.
**      *  If a verb token corresponds to a procedure, that procedure is
**         run.  The procedure might use, pop, or pull elements from 
**         the stack.
**      *  If a verb token corresponds to a variable, the value of that
**         variable is pushed onto the stack.
**
** This module attempts to be completely self-contained so that it can
** be portable to other projects.
*/
#include "config.h"
#include "subscript.h"
#include <assert.h>

#if INTERFACE
typedef struct Subscript Subscript;
#define SBS_OK      0
#define SBS_ERROR   1
#define SBS_RETURN  2
#define SBS_BREAK   3
#endif

/*
** Configuration constants
*/
#define SBSCONFIG_NHASH    41         /* Size of the hash table */
#define SBSCONFIG_NSTACK   20         /* Maximum stack depth */
#define SBSCONFIG_ERRSIZE  100        /* Maximum size of an error message */

/*
** Available token types:
*/
#define SBSTT_WHITESPACE  1    /* ex:   \040   */
#define SBSTT_NAME        2    /* ex:   /abcde  */
#define SBSTT_VERB        3    /* ex:   abcde   */
#define SBSTT_STRING      4    /* ex:   {...}   */
#define SBSTT_QUOTED      5    /* ex:   "...\n..." */
#define SBSTT_INTEGER     6    /* Integer including option sign */
#define SBSTT_INCOMPLETE  7    /* Unterminated string token */
#define SBSTT_UNKNOWN     8    /* Unknown token */
#define SBSTT_EOF         9    /* End of input */

/*
** Values are stored in the hash table as instances of the following
** structure.
*/
typedef struct SbSValue SbSValue;
struct SbSValue {
  int flags;        /* Bitmask of SBSVAL_* values */
  union {
    struct {
      int size;        /* Number of bytes in string, not counting final zero */
      char *z;         /* Pointer to string content */
    } str;          /* Value if SBSVAL_STR */
    struct {
      int (*xVerb)(Subscript*, void*);     /* Function to do the work */
      void *pArg;                          /* 2nd parameter to xVerb */
    } verb;         /* Value if SBSVAL_VERB */
  } u;              
};
#define SBSVAL_VERB    0x0001      /* Value stored in u.verb */
#define SBSVAL_STR     0x0002      /* Value stored in u.str */ 
#define SBSVAL_DYN     0x0004      /* u.str.z is dynamically allocated */
#define SBSVAL_EXEC    0x0008      /* u.str.z is a script */

/*
** An entry in the hash table is an instance of this structure.
*/
typedef struct SbsHashEntry SbsHashEntry;
struct SbsHashEntry {
  SbsHashEntry *pNext;     /* Next entry with the same hash on zKey */
  SbSValue val;            /* The payload */
  int nKey;               /* Length of the key */
  char zKey[1];           /* The key */
};

/*
** A hash table is an instance of the following structure.
*/
typedef struct SbsHashTab SbsHashTab;
struct SbsHashTab {
  SbsHashEntry *aHash[SBSCONFIG_NHASH];  /* The hash table */
};

/*
** An instance of the Subscript interpreter
*/
struct Subscript {
  int nStack;                        /* Number of entries on stack */
  SbsHashTab symTab;                 /* The symbol table */
  char zErrMsg[SBSCONFIG_ERRSIZE];   /* Space to write an error message */
  SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */
};


/*
** Given an input string z of length n, identify the token that
** starts at z[0].  Write the token type into *pTokenType and
** return the length of the token.
*/
static int sbs_next_token(const char *z, int n, int *pTokenType){
  int c;
  if( n<=0 || z[0]==0 ){
    *pTokenType = SBSTT_EOF;
    return 0;
  }
  c = z[0];
  if( isspace(c) ){
    int i;
    *pTokenType = SBSTT_WHITESPACE;
    for(i=1; i<n && isspace(z[i]); i++){}
    return i;
  }
  if( c=='#' ){
    int i;
    for(i=1; i<n && z[i] && z[i-1]!='\n'; i++){}
    *pTokenType = SBSTT_WHITESPACE;
    return i;
  }
  if( c=='"' ){
    int i;
    for(i=1; i<n && z[i] && z[i]!='"'; i++){
       if( z[i]=='\\' && i<n-1 ){ i++; }
    }
    if( z[i]=='"' ){
      *pTokenType = SBSTT_QUOTED;
      return i+1;
    }else{
      *pTokenType = SBSTT_INCOMPLETE;
      return i;
    }
  }
  if( c=='{' ){
    int depth = 1;
    int i;
    for(i=1; i<n && z[i]; i++){
      if( z[i]=='{' ){
        depth++;
      }else if( z[i]=='}' ){
        depth--;
        if( depth==0 ){
          i++;
          break;
        }
      }
    }
    if( depth ){
      *pTokenType = SBSTT_INCOMPLETE;
    }else{
      *pTokenType = SBSTT_STRING;
    }
    return i;
  }
  if( c=='/' && n>=2 && isalpha(z[1]) ){
    int i;
    for(i=2; i<n && (isalnum(z[i]) || z[i]=='_'); i++){}
    *pTokenType = SBSTT_NAME;
    return i;
  }
  if( isalpha(c) ){
    int i;
    for(i=1; i<n && (isalnum(z[i]) || z[i]=='_'); i++){}
    *pTokenType = SBSTT_VERB;
    return i;
  }
  if( isdigit(c) || ((c=='-' || c=='+') && n>=2 && isdigit(z[1])) ){
    int i;
    for(i=1; i<n && isdigit(z[i]); i++){}
    *pTokenType = SBSTT_INTEGER;
    return i;
  }
  *pTokenType = SBSTT_UNKNOWN;
  return 1;
}


/*
** Release any memory allocated by a value.
*/
static void sbs_value_reset(SbSValue *p){
  if( p->flags & SBSVAL_DYN ){
    free(p->u.str.z);
    p->flags = SBSVAL_STR;
    p->u.str.z = "";
    p->u.str.size = 0;
  }
}

/*
** Compute a hash on a string.
*/
static int sbs_hash(const char *z, int n){
  int h = 0;
  int i;
  for(i=0; i<n; i++){
    h ^= (h<<1) | z[i];
  }
  h &= 0x7ffffff;
  return h % SBSCONFIG_NHASH;
}

/*
** Look up a value in the hash table.  Return a pointer to the value.
** Return NULL if not found.
*/
static const SbSValue *sbs_fetch(
  SbsHashTab *pHash, 
  const char *zKey, 
  int nKey
){
  int h;
  SbsHashEntry *p;

  if( nKey<0 ) nKey = strlen(zKey);
  h = sbs_hash(zKey, nKey);
  for(p = pHash->aHash[h]; p; p=p->pNext){
    if( p->nKey==nKey && memcmp(p->zKey,zKey,nKey)==0 ){
      return &p->val;
    }
  }
  return 0;
}

/*
** Store a value in the hash table.  Overwrite any prior value stored
** under the same name.
**
** If the value in the 4th argument needs to be reset or freed,
** the hash table will take over responsibiliity for doing so.
*/
static int sbs_store(
  SbsHashTab *pHash,       /* Insert into this hash table */
  const char *zKey,        /* The key */
  int nKey,                /* Size of the key */
  const SbSValue *pValue   /* The value to be stored */
){
  int h;
  SbsHashEntry *p, *pNew;

  if( nKey<0 ) nKey = strlen(zKey);
  h = sbs_hash(zKey, nKey);
  for(p = pHash->aHash[h]; p; p=p->pNext){
    if( p->nKey==nKey && memcmp(p->zKey,zKey,nKey)==0 ){
      sbs_value_reset(&p->val);
      memcpy(&p->val, pValue, sizeof(p->val));
      return SBS_OK;
    }
  }
  pNew = malloc( sizeof(*pNew) + nKey );
  if( pNew ){
    pNew->nKey = nKey;
    memcpy(pNew->zKey, zKey, nKey+1);
    memcpy(&pNew->val, pValue, sizeof(pNew->val));
    pNew->pNext = pHash->aHash[h];
    pHash->aHash[h] = pNew;
    return SBS_OK;
  }
  return SBS_ERROR;
}

/*
** Reset a hash table.
*/
static void sbs_hash_reset(SbsHashTab *pHash){
  int i;
  SbsHashEntry *p, *pNext;
  for(i=0; i<SBSCONFIG_NHASH; i++){
    for(p=pHash->aHash[i]; p; p=pNext){
      pNext = p->pNext;
      sbs_value_reset(&p->val);
      free(p);
    }
  }
  memset(pHash, 0, sizeof(*pHash));
}

/*
** Push a value onto the stack of an interpreter
*/
static int sbs_push(Subscript *p, SbSValue *pVal){
  if( p->nStack>=SBSCONFIG_NSTACK ){
    sqlite3_snprintf(SBSCONFIG_ERRSIZE, p->zErrMsg, "stack overflow");
    return SBS_ERROR;
  }
  p->aStack[p->nStack++] = *pVal;
  return SBS_OK;
}

/*
** Create a new subscript interpreter.  Return a pointer to the
** new interpreter, or return NULL if malloc fails.
*/
struct Subscript *SbS_Create(void){
  Subscript *p;
  p = malloc( sizeof(*p) );
  if( p ){
    memset(p, 0, sizeof(*p));
  }
  return p;
}

/*
** Destroy an subscript interpreter
*/
void SbS_Destroy(struct Subscript *p){
  int i;
  sbs_hash_reset(&p->symTab);
  for(i=0; i<p->nStack; i++){
    sbs_value_reset(&p->aStack[i]);
  }
  free(p);
}

/*
** Set the error message for an interpreter.  Verb implementations
** use this routine when they encounter an error.
*/
void SbS_SetErrorMessage(struct Subscript *p, const char *zErr, ...){
  int nErr;
  char *zMsg;
  va_list ap;

  va_start(ap, zErr);
  zMsg = vmprintf(zErr, ap);
  va_end(ap);
  nErr = strlen(zMsg);
  if( nErr>sizeof(p->zErrMsg)-1 ){
    nErr = sizeof(p->zErrMsg)-1;
  }
  memcpy(p->zErrMsg, zMsg, nErr);
  p->zErrMsg[nErr] = 0;
  free(zMsg);
}

/*
** Return a pointer to the current error message for the
** interpreter.
*/
const char *SbS_GetErrorMessage(struct Subscript *p){
  return p->zErrMsg;
}

/*
** Add a new verb the given interpreter
*/
int SbS_AddVerb(
  struct Subscript *p,
  const char *zVerb,
  int (*xVerb)(struct Subscript*,void*),
  void *pArg
){
  SbSValue v;
  v.flags = SBSVAL_VERB;
  v.u.verb.xVerb = xVerb;
  v.u.verb.pArg = pArg;
  return sbs_store(&p->symTab, zVerb, -1, &v);
}

/*
** Store a value in an interpreter variable.
*/
int SbS_Store(
  struct Subscript *p,   /* Store into this interpreter */
  const char *zName,     /* Name of the variable */
  const char *zValue,    /* Value of the variable */
  int persistence        /* 0: static.  1: ephemeral.  2: dynamic */
){
  SbSValue v;
  v.flags = SBSVAL_STR;
  v.u.str.size = strlen(zValue);
  if( persistence==1 ){
    v.u.str.z = mprintf("%s", zValue);
  }else{
    v.u.str.z = (char*)zValue;
  }
  if( persistence>0 ){
    v.flags |= SBSVAL_DYN;
  }
  return sbs_store(&p->symTab, zName, -1, &v);
}

/*
** Push a string value onto the stack.
**
** If the 4th parameter is 0, then the string is static.
** If the 4th parameter is non-zero then the string was obtained
** from malloc and Subscript will take responsibility for freeing
** it.
**
** Return 0 on success and non-zero if there is an error.
*/
int SbS_Push(
  struct Subscript *p,  /* Push onto this interpreter */
  char *z,              /* String value to push */
  int n,                /* Length of the string, or -1 */
  int dyn               /* If true, z was obtained from malloc */
){
  SbSValue v;
  v.flags = SBSVAL_STR;
  if( dyn ){
    v.flags |= SBSVAL_DYN;
  }
  if( n<0 ) n = strlen(z);
  v.u.str.size = n;
  v.u.str.z = z;
  return sbs_push(p, &v);
}

/*
** Push an integer value onto the stack.
**
** This routine really just converts the integer into a string
** then calls SbS_Push.
*/
int SbS_PushInt(struct Subscript *p, int iVal){
  if( iVal==0 ){
    return SbS_Push(p, "0", 1, 0);
  }else if( iVal==1 ){
    return SbS_Push(p, "1", 1, 0);
  }else{
    char *z;
    int n;
    char zVal[50];
    sprintf(zVal, "%d", iVal);
    n = strlen(zVal);
    z = malloc( n+1 );
    if( z ){
      strcpy(z, zVal);
      return SbS_Push(p, z, n, 1);
    }else{
      return SBS_ERROR;
    }
  }
}

/*
** Pop and destroy zero or more values from the stack.
** Return the number of values remaining on the stack after
** the pops occur.
*/
int SbS_Pop(struct Subscript *p, int N){
  while( N>0 && p->nStack>0 ){
    p->nStack--;
    sbs_value_reset(&p->aStack[p->nStack]);
    N--;
  }
  return p->nStack;
}

/*
** Return the N-th element of the stack.  0 is the top of the stack.
** 1 is the first element down.  2 is the second element.  And so forth.
** Return NULL if there is no N-th element.
**
** The pointer returned is only valid until the value is popped
** from the stack.
*/
const char *SbS_StackValue(struct Subscript *p, int N, int *pSize){
  SbSValue *pVal;
  if( N<0 || N>=p->nStack ){
    return 0;
  }
  pVal = &p->aStack[p->nStack-N-1];
  if( (pVal->flags & SBSVAL_STR)==0 ){
    return 0;
  }
  *pSize = pVal->u.str.size;
  return pVal->u.str.z;
}

/*
** A convenience routine for extracting an integer value from the
** stack.
*/
int SbS_StackValueInt(struct Subscript *p, int N){
  int n, v;
  int isNeg = 0;
  const char *z = SbS_StackValue(p, N, &n);
  v = 0;
  if( n==0 ) return 0;
  if( z[0]=='-' ){
    isNeg = 1;
    z++;
    n--;
  }else if( z[0]=='+' ){
    z++;
    n--;
  }
  while( n>0 && isdigit(z[0]) ){
    v = v*10 + z[0] - '0';
    z++;
    n--;
  }
  if( isNeg ){
    v = -v;
  }
  return v;
}

/*
** Retrieve the value of a variable from the interpreter.  Return
** NULL if no such variable is defined.  
**
** The returned string is not necessarily (probably not) zero-terminated.
** The string may be deallocated the next time anything is done to
** the interpreter.  Make a copy if you need it to persist.
*/
const char *SbS_Fetch(
  struct Subscript *p,   /* The interpreter we are interrogating */
  const char *zKey,        /* Name of the variable.  Case sensitive */
  int nKey,                /* Length of the key */
  int *pLength             /* Write the length here */
){
  const SbSValue *pVal;

  pVal = sbs_fetch(&p->symTab, zKey, nKey);
  if( pVal==0 || (pVal->flags & SBSVAL_STR)==0 ){
    *pLength = 0;
    return 0;
  }else{
    *pLength = pVal->u.str.size;
    return pVal->u.str.z;
  }
}

/*
** Generate an error and return non-zero if the stack has
** fewer than N elements.  This is utility routine used in
** the implementation of verbs.
*/
int SbS_RequireStack(struct Subscript *p, int N, const char *zCmd){
  if( p->nStack>=N ) return 0;
  sqlite3_snprintf(sizeof(p->zErrMsg), p->zErrMsg,
     "\"%s\" requires at least %d stack elements - only found %d",
     zCmd, N, p->nStack);
  return 1;
}

/*
** Subscript command:       STRING NAME set
**
** Write the value of STRING into variable called NAME.
*/
static int setCmd(Subscript *p, void *pNotUsed){
  SbSValue *pTos;
  SbSValue *pNos;
  if( SbS_RequireStack(p, 2, "set") ) return SBS_ERROR;
  pTos = &p->aStack[--p->nStack];
  pNos = &p->aStack[--p->nStack];
  sbs_store(&p->symTab, pTos->u.str.z, pTos->u.str.size, pNos);
  sbs_value_reset(pTos);
  return 0;
}

/*
** Subscript command:      INTEGER not INTEGER
*/
static int notCmd(struct Subscript *p, void *pNotUsed){
  int n;
  if( SbS_RequireStack(p, 1, "not") ) return 1;
  n = SbS_StackValueInt(p, 0);
  SbS_Pop(p, 1);
  SbS_PushInt(p, !n);
  return 0;
}

#define SBSOP_ADD   1
#define SBSOP_SUB   2
#define SBSOP_MUL   3
#define SBSOP_DIV   4
#define SBSOP_AND   5
#define SBSOP_OR    6
#define SBSOP_MIN   7
#define SBSOP_MAX   8
#define SBSOP_EQ    9
#define SBSOP_NE   10
#define SBSOP_LT   11
#define SBSOP_GT   12
#define SBSOP_LE   13
#define SBSOP_GE   14

/*
** Subscript command:      INTEGER INTEGER <binary-op> INTEGER
*/
static int bopCmd(struct Subscript *p, void *pOp){
  int a, b, c;
  if( SbS_RequireStack(p, 2, "BINARY-OP") ) return 1;
  a = SbS_StackValueInt(p, 1);
  b = SbS_StackValueInt(p, 0);
  switch( (int)pOp ){
    case SBSOP_EQ:   c = a==b;           break;
    case SBSOP_NE:   c = a!=b;           break;
    case SBSOP_LT:   c = a<b;            break;
    case SBSOP_LE:   c = a<=b;           break;
    case SBSOP_GT:   c = a>b;            break;
    case SBSOP_GE:   c = a>=b;           break;
    case SBSOP_ADD:  c = a+b;            break;
    case SBSOP_SUB:  c = a-b;            break;
    case SBSOP_MUL:  c = a*b;            break;
    case SBSOP_DIV:  c = b!=0 ? a/b : 0; break;
    case SBSOP_AND:  c = a && b;         break;
    case SBSOP_OR:   c = a || b;         break;
    case SBSOP_MIN:  c = a<b ? a : b;    break;
    case SBSOP_MAX:  c = a<b ? b : a;    break;
  }
  SbS_Pop(p, 2);
  SbS_PushInt(p, c);
  return 0;
}

/*
** Subscript command:      STRING STRING streq INTEGER
*/
static int streqCmd(struct Subscript *p, void *pOp){
  int c, nA, nB;
  const char *A, *B;
  
  if( SbS_RequireStack(p, 2, "BINARY-OP") ) return 1;
  A = SbS_StackValue(p, 1, &nA);
  B = SbS_StackValue(p, 0, &nB);
  c = nA==nB && memcmp(A,B,nA)==0;
  SbS_Pop(p, 2);
  SbS_PushInt(p, c);
  return 0;
}

/*
** Subscript command:     STRING hascap INTEGER
**
** Return true if the user has all of the capabilities listed.
*/
static int hascapCmd(struct Subscript *p, void *pNotUsed){
  const char *z;
  int n, a;
  if( SbS_RequireStack(p, 1, "hascap") ) return 1;
  z = SbS_StackValue(p, 0, &n);
  a = login_has_capability(z, n);
  SbS_Pop(p, 1);
  SbS_PushInt(p, a);
  return 0;
}

/*
** Subscript command:     STRING linecount INTEGER
**
** Return one more than the number of \n characters in STRING.
*/
static int linecntCmd(struct Subscript *p, void *pNotUsed){
  const char *z;
  int size, n, i;
  if( SbS_RequireStack(p, 1, "linecount") ) return 1;
  z = SbS_StackValue(p, 0, &size);
  for(n=1, i=0; i<size; i++){
    if( z[i]=='\n' ) n++;
  }
  SbS_Pop(p, 1);
  SbS_PushInt(p, n);
  return 0;
}

/*
** Subscript command:     STRING length INTEGER
**
** Return one more than the number characters in STRING.
*/
static int lengthCmd(struct Subscript *p, void *pNotUsed){
  int size;
  if( SbS_RequireStack(p, 1, "length") ) return 1;
  SbS_StackValue(p, 0, &size);
  SbS_Pop(p, 1);
  SbS_PushInt(p, size);
  return 0;
}

/*
** Subscript command:     NAME exists INTEGER
**
** Return TRUE if the variable NAME exists.
*/
static int existsCmd(struct Subscript *p, void *pNotUsed){
  const char *z;
  int size, x;
  if( SbS_RequireStack(p, 1, "exists") ) return 1;
  z = SbS_StackValue(p, 0, &size);
  x = sbs_fetch(&p->symTab, (char*)z, size)!=0;
  SbS_Pop(p, 1);
  SbS_PushInt(p, x);
  return 0;
}

/*
** Subscript command:       VALUE NAME get VALUE
**
** Push the value of varible NAME onto the stack if it exists.
** If NAME does not exist, puts VALUE onto the stack instead.
*/
static int getCmd(Subscript *p, void *pNotUsed){
  const char *zName;
  int nName;
  const SbSValue *pVal;
  int rc = 0;
  
  if( SbS_RequireStack(p, 2, "get") ) return SBS_ERROR;
  zName = SbS_StackValue(p, 0, &nName);
  pVal = sbs_fetch(&p->symTab, (char*)zName, nName);
  if( pVal!=0 && (pVal->flags & SBSVAL_STR)!=0 ){
    SbS_Pop(p, 2);
    rc = SbS_Push(p, pVal->u.str.z, pVal->u.str.size, 0);
  }else{
    SbS_Pop(p, 1);
  }
  return rc;
}



/*
** True if output is enabled.  False if disabled.
*/
static int enableOutput = 1;

/*
** Subscript command:      BOOLEAN enable_output
**
** Enable or disable the puts and hputs commands.
*/
static int enableOutputCmd(struct Subscript *p, void *pNotUsed){
  if( SbS_RequireStack(p, 1, "enable_output") ) return 1;
  enableOutput = SbS_StackValueInt(p, 0)!=0;
  SbS_Pop(p, 1);
  return 0;
}

/*
** Send text to the appropriate output:  Either to the console
** or to the CGI reply buffer.
*/
static void sendText(const char *z, int n){
  if( enableOutput && n ){
    if( n<0 ) n = strlen(z);
    if( g.cgiPanic ){
      cgi_append_content(z, n);
    }else{
      fwrite(z, 1, n, stdout);
    }
  }
}

/*
** Subscript command:      STRING puts
** Subscript command:      STRING html
**
** Output STRING as HTML (html) or unchanged (puts).  
** Pop it from the stack.
*/
static int putsCmd(struct Subscript *p, void *pConvert){
  int size;
  const char *z;
  char *zOut;
  if( SbS_RequireStack(p, 1, "puts") ) return 1;
  if( enableOutput ){
    z = SbS_StackValue(p, 0, &size);
    if( pConvert ){    
      zOut = htmlize(z, size);
      size = strlen(zOut);
    }else{
      zOut = (char*)z;
    }
    sendText(zOut, size);
    if( pConvert ){
      free(zOut);
    }
  }
  SbS_Pop(p, 1);
  return 0;
}

/*
** Subscript command:      STRING wiki
**
** Render the input string as wiki.
*/
static int wikiCmd(struct Subscript *p, void *pConvert){
  int size;
  const char *z;
  if( SbS_RequireStack(p, 1, "wiki") ) return 1;
  if( enableOutput ){
    Blob src;
    z = SbS_StackValue(p, 0, &size);
    blob_init(&src, z, size);
    wiki_convert(&src, 0, WIKI_INLINE);
    blob_reset(&src);
  }
  SbS_Pop(p, 1);
  return 0;
}

/*
** Subscript command:   NAME TEXT-LIST NUMLINES combobox
**
** Generate an HTML combobox.  NAME is both the name of the
** CGI parameter and the name of a variable that contains the
** currently selected value.  TEXT-LIST is a list of possible
** values for the combobox.  NUMLINES is 1 for a true combobox.
** If NUMLINES is greater than one then the display is a listbox
** with the number of lines given.
*/
static int comboboxCmd(struct Subscript *p, void *NotUsed){
  if( SbS_RequireStack(p, 3, "combobox") ) return 1;
  if( enableOutput ){
    int height;
    Blob list, elem;
    char *zName, *zList;
    int nName, nList, nValue;
    const char *zValue;
    char *z, *zH;

    height = SbS_StackValueInt(p, 0);
    zList = (char*)SbS_StackValue(p, 1, &nList);
    blob_init(&list, zList, nList);
    zName = (char*)SbS_StackValue(p, 2, &nName);
    zValue = SbS_Fetch(p, zName, nName, &nValue);
    z = mprintf("<select name=\"%z\" size=\"%d\">", 
                 htmlize(zName, nName), height);
    sendText(z, -1);
    free(z);
    while( blob_token(&list, &elem) ){
      zH = htmlize(blob_buffer(&elem), blob_size(&elem));
      if( zValue && blob_size(&elem)==nValue 
             && memcmp(zValue, blob_buffer(&elem), nValue)==0 ){
        z = mprintf("<option value=\"%s\" selected>%s</option>", zH, zH);
      }else{
        z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
      }
      free(zH);
      sendText(z, -1);
      free(z);
    }
    sendText("</select>", -1);
    blob_reset(&list);
  }
  SbS_Pop(p, 3);
  return 0;
}

/*
** Subscript command:     STRING BOOLEAN if
**
** Evaluate STRING as a script if BOOLEAN is true.
*/
static int ifCmd(struct Subscript *p, void *pNotUsed){
  int cond;
  int rc = SBS_OK;

  if( SbS_RequireStack(p, 2, "if") ) return 1;
  cond = SbS_StackValueInt(p, 0);
  if( cond ){
    SbSValue script = p->aStack[p->nStack-2];
    p->aStack[p->nStack-2].flags = 0;
    SbS_Pop(p, 2);
    rc = SbS_Eval(p, script.u.str.z, script.u.str.size);
    sbs_value_reset(&script);
  }else{
    SbS_Pop(p, 2);
  }
  return rc;
}

/*
** Subscript command:     ... STRING COUNT concat STRING
**
** Concatenate COUNT strings into a single string and leave
** the concatenation on the stack. 
*/
static int concatCmd(struct Subscript *p, void *pNotUsed){
  int count;
  int nByte;
  char *z;
  int i, j;

  if( SbS_RequireStack(p, 1, "concat") ) return SBS_ERROR;
  count = SbS_StackValueInt(p, 0);
  if( SbS_RequireStack(p, count+1, "concat") ) return SBS_ERROR;
  SbS_Pop(p, 1);
  nByte = 1;
  for(i=p->nStack-count; i<p->nStack; i++){
    nByte += p->aStack[i].u.str.size;
  }
  z = malloc(nByte);
  if( z==0 ){ fossil_panic("out of memory"); }
  for(j=0, i=p->nStack-count; i<p->nStack; i++){
    nByte = p->aStack[i].u.str.size;
    memcpy(&z[j], p->aStack[i].u.str.z, nByte);
    j += nByte;
  }
  z[j] = 0;
  SbS_Pop(p, count);
  SbS_Push(p, z, j, 1);
  return SBS_OK;
}


/*
** A table of built-in commands
*/
static const struct {
  const char *zCmd;
  int (*xCmd)(Subscript*,void*);
  void *pArg;
} aBuiltin[] = {
  { "add",             bopCmd,               (void*)SBSOP_AND    },
  { "and",             bopCmd,               (void*)SBSOP_AND    },
  { "combobox",        comboboxCmd,          0,                  },
  { "concat",          concatCmd,            0,                  },
  { "div",             bopCmd,               (void*)SBSOP_DIV    },
  { "enable_output",   enableOutputCmd,      0                   },
  { "eq",              bopCmd,               (void*)SBSOP_EQ     },
  { "exists",          existsCmd,            0,                  },
  { "get",             getCmd,               0,                  },
  { "hascap",          hascapCmd,            0                   },
  { "html",            putsCmd,              (void*)1            },
  { "if",              ifCmd,                0,                  },
  { "le",              bopCmd,               (void*)SBSOP_LE     },
  { "length",          lengthCmd,            0                   },
  { "linecount",       linecntCmd,           0                   },
  { "lt",              bopCmd,               (void*)SBSOP_LT     },
  { "max",             bopCmd,               (void*)SBSOP_MAX    },
  { "min",             bopCmd,               (void*)SBSOP_MIN    },
  { "mul",             bopCmd,               (void*)SBSOP_MUL    },
  { "not",             notCmd,               0                   },
  { "or",              bopCmd,               (void*)SBSOP_OR     },
  { "puts",            putsCmd,              0                   },
  { "set",             setCmd,               0                   },
  { "streq",           streqCmd,             0                   },
  { "sub",             bopCmd,               (void*)SBSOP_SUB    },
  { "wiki",            wikiCmd,              0,                  },
};
  

/*
** Compare a zero-terminated string zPattern against
** an unterminated string zStr of length nStr.
**
** Return less than, equal to, or greater than zero if
** zPattern is less than, equal to, or greater than zStr.
*/
static int compare_cmd(const char *zPattern, const char *zStr, int nStr){
  int c = strncmp(zPattern, zStr, nStr);
  if( c==0 && zPattern[nStr]!=0 ){
    c = 1;
  }
  return c;
}

/*
** Evaluate the script given by the first nScript bytes of zScript[].
** Return 0 on success and non-zero for an error.
*/
int SbS_Eval(struct Subscript *p, const char *zScript, int nScript){
  int rc = SBS_OK;
  if( nScript<0 ) nScript = strlen(zScript);
  while( nScript>0 && rc==SBS_OK ){
    int n;
    int ttype;
    n = sbs_next_token(zScript, nScript, &ttype);

#if 0
    {
      int i, nElem;
      const char *zElem;
      if( p->nStack>0 ){
        printf("STACK:");
        for(i=0; i<p->nStack; i++){
          zElem = SbS_StackValue(p, i, &nElem);
          printf(" [%.*s]", nElem, zElem);
        }
        printf("\n");
      }
      printf("TOKEN(%d): [%.*s]\n", ttype, n, zScript);
    }
#endif

    switch( ttype ){
      case SBSTT_WHITESPACE: {
        break;
      }
      case SBSTT_EOF: {
        nScript = 0;
        break;
      }
      case SBSTT_INCOMPLETE:
      case SBSTT_UNKNOWN: {
        rc = SBS_ERROR;
        nScript = n;
        break;
      }
      case SBSTT_INTEGER: {
        rc = SbS_Push(p, (char*)zScript, n, 0);
        break;
      }
      case SBSTT_NAME: {
        rc = SbS_Push(p, (char*)&zScript[1], n-1, 0);
        break;
      }
      case SBSTT_STRING: {
        rc = SbS_Push(p, (char*)&zScript[1], n-2, 0);
        break;
      }
      case SBSTT_QUOTED: {
        char *z = mprintf("%.*s", n-2, &zScript[1]);
        int i, j;
        for(i=j=0; z[i]; i++, j++){
          int c = z[i];
          if( c=='\\' && z[i+1] ){
            c = z[++i];
            if( c=='n' ){
              c = '\n';
            }else if( c>='0' && c<='7' ){
              int k;
              c -= '0';
              for(k=1; k<3 && z[i+k]>='0' && z[i+k]<='7'; k++){
                c = c*8 + z[i+k] - '0';
              }
              i += k-1;
            }
          }
          z[j] = c;
        }
        z[j] = 0;
        rc = SbS_Push(p, z, j, 1);
        break;
      }
      case SBSTT_VERB: {
        /* First look up the verb in the hash table */
        const SbSValue *pVal = sbs_fetch(&p->symTab, (char*)zScript, n);
        if( pVal==0 ){
          /* If the verb is not in the hash table, look for a 
          ** built-in command */
          int upr = sizeof(aBuiltin)/sizeof(aBuiltin[0]) - 1;
          int lwr = 0;
          rc = SBS_ERROR;
          while( upr>=lwr ){
            int i = (upr+lwr)/2;
            int c = compare_cmd(aBuiltin[i].zCmd, zScript, n);
            if( c==0 ){
              rc = aBuiltin[i].xCmd(p, aBuiltin[i].pArg);
              break;
            }else if( c>0 ){
              upr = i-1;
            }else{
              lwr = i+1;
            }
          }
          if( upr<lwr ){
            SbS_SetErrorMessage(p, "unknown verb: %.*s", n, zScript);
          }
        }else if( pVal->flags & SBSVAL_VERB ){
          rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg);
        }else if( pVal->flags & SBSVAL_EXEC ){
          rc = SbS_Eval(p, pVal->u.str.z, pVal->u.str.size);
        }else{
          rc = SbS_Push(p, pVal->u.str.z, pVal->u.str.size, 0);
        }
        break;
      }
    }
    zScript += n;
    nScript -= n;
  }
  return rc;
}

/*
** The z[] input contains text mixed with subscript scripts.
** The subscript scripts are contained within [...].  This routine
** processes the template and writes the results on either
** stdout or into CGI.
*/
int SbS_Render(struct Subscript *p, const char *z){
  int i = 0;
  int rc = SBS_OK;
  while( z[i] ){
    if( z[i]=='[' ){
      sendText(z, i);
      z += i+1;
      for(i=0; z[i] && z[i]!=']'; i++){}
      rc = SbS_Eval(p, z, i);
      if( rc!=SBS_OK ) break;
      if( z[i] ) i++;
      z += i;
      i = 0;
    }else{
      i++;
    }
  }
  if( rc==SBS_ERROR ){
    sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1);
    sendText(SbS_GetErrorMessage(p), -1);
    sendText("</b></font></p>", -1);
  }else{
    sendText(z, i);
  }
  return rc;
}

/*
** COMMAND: test-subscript
*/
void test_subscript(void){
  Subscript *p;
  Blob in;
  if( g.argc<3 ){
    usage("FILE");
  }
  blob_zero(&in);
  blob_read_from_file(&in, g.argv[2]);
  p = SbS_Create();
  SbS_Render(p, blob_str(&in));
}

Changes to src/sync.c.

22
23
24
25
26
27
28




























29
30
31
32
33
34
35
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
*******************************************************************************
**
** This file contains code used to push, pull, and sync a repository
*/
#include "config.h"
#include "sync.h"
#include <assert.h>





























/*
** This routine processes the command-line argument for push, pull,
** and sync.  If a command-line argument is given, that is the URL
** of a server to sync against.  If no argument is given, use the
** most recently synced URL.  Remember the current URL for next time.
*/
................................................................................
  if( zUrl==0 ){
    usage("URL");
  }
  url_parse(zUrl);
  if( g.urlIsFile ){
    fossil_fatal("network sync only");
  }
  db_set("last-sync-url", zUrl);
  user_select();
  if( g.argc==2 ){
    if( g.urlPort!=80 ){
      printf("Server:    http://%s:%d%s\n", g.urlName, g.urlPort, g.urlPath);
    }else{
      printf("Server:    http://%s%s\n", g.urlName, g.urlPath);
    }







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







 







|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
*******************************************************************************
**
** This file contains code used to push, pull, and sync a repository
*/
#include "config.h"
#include "sync.h"
#include <assert.h>

/*
** If the respository is configured for autosyncing, then do an
** autosync.  This will be a pull if the argument is true or a push
** if the argument is false.  Return true if the autosync is done
** and false if autosync is not requested for the current repository.
*/
int autosync(int pullFlag){
  const char *zUrl;
  if( db_get_boolean("autosync", 0)==0 ){
    return 0;
  }
  zUrl = db_get("last-sync-url", 0);
  if( zUrl==0 ){
    return 0;  /* No default server */
  }
  url_parse(zUrl);
  if( g.urlIsFile ){
    return 0;  /* Network sync only */
  }
  if( g.urlPort!=80 ){
    printf("Autosync:  http://%s:%d%s\n", g.urlName, g.urlPort, g.urlPath);
  }else{
    printf("Autosync:  http://%s%s\n", g.urlName, g.urlPath);
  }
  client_sync(!pullFlag, pullFlag, 0);
  return 1;
}

/*
** This routine processes the command-line argument for push, pull,
** and sync.  If a command-line argument is given, that is the URL
** of a server to sync against.  If no argument is given, use the
** most recently synced URL.  Remember the current URL for next time.
*/
................................................................................
  if( zUrl==0 ){
    usage("URL");
  }
  url_parse(zUrl);
  if( g.urlIsFile ){
    fossil_fatal("network sync only");
  }
  db_set("last-sync-url", zUrl, 0);
  user_select();
  if( g.argc==2 ){
    if( g.urlPort!=80 ){
      printf("Server:    http://%s:%d%s\n", g.urlName, g.urlPort, g.urlPath);
    }else{
      printf("Server:    http://%s%s\n", g.urlName, g.urlPath);
    }

Added src/tag.c.























































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to manage tags
*/
#include "config.h"
#include "tag.h"
#include <assert.h>

/*
** Propagate the tag given by tagid to the children of pid.
**
** This routine assumes that tagid is a tag that should be
** propagated and that the tag is already present in pid.
**
** If tagtype is 2 then the tag is being propagated from an
** ancestor node.  If tagtype is 0 it means a branch tag is
** being cancelled.
*/
void tag_propagate(
  int pid,             /* Propagate the tag to children of this node */
  int tagid,           /* Tag to propagate */
  int tagType,         /* 2 for a propagating tag.  0 for an antitag */
  const char *zValue,  /* Value of the tag.  Might be NULL */
  double mtime         /* Timestamp on the tag */
){
  PQueue queue;
  Stmt s, ins, eventupdate;

  assert( tagType==0 || tagType==2 );
  pqueue_init(&queue);
  pqueue_insert(&queue, pid, 0.0);
  db_prepare(&s, 
     "SELECT cid, plink.mtime,"
     "       coalesce(srcid=0 AND tagxref.mtime<:mtime, %d) AS doit"
     "  FROM plink LEFT JOIN tagxref ON cid=rid AND tagid=%d"
     " WHERE pid=:pid AND isprim",
     tagType!=0, tagid
  );
  db_bind_double(&s, ":mtime", mtime);
  if( tagType==2 ){
    db_prepare(&ins,
       "REPLACE INTO tagxref(tagid, tagtype, srcid, value, mtime, rid)"
       "VALUES(%d,2,0,%Q,:mtime,:rid)",
       tagid, zValue
    );
    db_bind_double(&ins, ":mtime", mtime);
  }else{
    zValue = 0;
    db_prepare(&ins,
       "DELETE FROM tagxref WHERE tagid=%d AND rid=:rid", tagid
    );
  }
  if( tagid==TAG_BGCOLOR ){
    db_prepare(&eventupdate,
      "UPDATE event SET brbgcolor=%Q WHERE objid=:rid", zValue
    );
  }
  while( (pid = pqueue_extract(&queue))!=0 ){
    db_bind_int(&s, ":pid", pid);
    while( db_step(&s)==SQLITE_ROW ){
      int doit = db_column_int(&s, 2);
      if( doit ){
        int cid = db_column_int(&s, 0);
        double mtime = db_column_double(&s, 1);
        pqueue_insert(&queue, cid, mtime);
        db_bind_int(&ins, ":rid", cid);
        db_step(&ins);
        db_reset(&ins);
        if( tagid==TAG_BGCOLOR ){
          db_bind_int(&eventupdate, ":rid", cid);
          db_step(&eventupdate);
          db_reset(&eventupdate);
        }
      }
    }
    db_reset(&s);
  }
  pqueue_clear(&queue);
  db_finalize(&ins);
  db_finalize(&s);
  if( tagid==TAG_BGCOLOR ){
    db_finalize(&eventupdate);
  }
}

/*
** Propagate all propagatable tags in pid to its children.
*/
void tag_propagate_all(int pid){
  Stmt q;
  db_prepare(&q,
     "SELECT tagid, tagtype, mtime, value FROM tagxref"
     " WHERE rid=%d"
     "   AND (tagtype=0 OR tagtype=2)",
     pid
  );
  while( db_step(&q)==SQLITE_ROW ){
    int tagid = db_column_int(&q, 0);
    int tagtype = db_column_int(&q, 1);
    double mtime = db_column_double(&q, 2);
    const char *zValue = db_column_text(&q, 3);
    tag_propagate(pid, tagid, tagtype, zValue, mtime);
  }
  db_finalize(&q);
}

/*
** Get a tagid for the given TAG.  Create a new tag if necessary
** if createFlag is 1.
*/
int tag_findid(const char *zTag, int createFlag){
  int id;
  id = db_int(0, "SELECT tagid FROM tag WHERE tagname=%Q", zTag);
  if( id==0 && createFlag ){
    db_multi_exec("INSERT INTO tag(tagname) VALUES(%Q)", zTag);
    id = db_last_insert_rowid();
  }
  return id;
}

/*
** Insert a tag into the database.
*/
void tag_insert(
  const char *zTag,        /* Name of the tag (w/o the "+" or "-" prefix */
  int tagtype,             /* 0:cancel  1:singleton  2:propagated */
  const char *zValue,      /* Value if the tag is really a property */
  int srcId,               /* Artifact that contains this tag */
  double mtime,            /* Timestamp.  Use default if <=0.0 */
  int rid                  /* Artifact to which the tag is to attached */
){
  Stmt s;
  const char *zCol;
  int tagid = tag_findid(zTag, 1);
  int rc;

  if( mtime<=0.0 ){
    mtime = db_double(0.0, "SELECT julianday('now')");
  }
  db_prepare(&s,
    "SELECT 1 FROM tagxref"
    " WHERE tagid=%d"
    "   AND rid=%d"
    "   AND mtime>=:mtime",
    tagid, rid
  );
  db_bind_double(&s, ":mtime", mtime);
  rc = db_step(&s);
  db_finalize(&s);
  if( rc==SQLITE_ROW ){
    /* Another entry this is more recent already exists.  Do nothing */
    return;
  }
  db_prepare(&s, 
    "REPLACE INTO tagxref(tagid,tagtype,srcId,value,mtime,rid)"
    " VALUES(%d,%d,%d,%Q,:mtime,%d)",
    tagid, tagtype, srcId, zValue, rid
  );
  db_bind_double(&s, ":mtime", mtime);
  db_step(&s);
  db_finalize(&s);
  if( tagtype==0 ){
    zValue = 0;
  }
  zCol = 0;
  switch( tagid ){
    case TAG_BGCOLOR: {
      if( tagtype==1 ){
        zCol = "bgcolor";
      }else{
        zCol = "brbgcolor";
      }
      break;
    }
    case TAG_COMMENT: {
      zCol = "ecomment";
      break;
    }
    case TAG_USER: {
      zCol = "euser";
      break;
    }
  }
  if( zCol ){
    db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
  }
  if( tagtype==0 || tagtype==2 ){
    tag_propagate(rid, tagid, tagtype, zValue, mtime);
  }
}


/*
** COMMAND: test-tag
** %fossil test-tag (+|*|-)TAGNAME UUID ?VALUE?
**
** Add a tag or anti-tag to the rebuildable tables of the local repository.
** No tag artifact is created so the new tag is erased the next
** time the repository is rebuilt.  This routine is for testing
** use only.
*/
void testtag_cmd(void){
  const char *zTag;
  const char *zValue;
  int rid;
  int tagtype;
  db_must_be_within_tree();
  if( g.argc!=4 && g.argc!=5 ){
    usage("TAGNAME UUID ?VALUE?");
  }
  zTag = g.argv[2];
  switch( zTag[0] ){
    case '+':  tagtype = 1;  break;
    case '*':  tagtype = 2;  break;
    case '-':  tagtype = 0;  break;
    default:   
      fossil_fatal("tag should begin with '+', '*', or '-'");
      return;
  }
  rid = name_to_rid(g.argv[3]);
  if( rid==0 ){
    fossil_fatal("no such object: %s", g.argv[3]);
  }
  zValue = g.argc==5 ? g.argv[4] : 0;
  db_begin_transaction();
  tag_insert(zTag, tagtype, zValue, -1, 0.0, rid);
  db_end_transaction(0); 
}

/*
** Add a control record to the repository that either creates
** or cancels a tag.
*/
static void tag_add_artifact(
  const char *zTagname,       /* The tag to add or cancel */
  const char *zObjName,       /* Name of object attached to */
  const char *zValue,         /* Value for the tag.  Might be NULL */
  int tagtype                 /* 0:cancel 1:singleton 2:propagated */
){
  int rid;
  int nrid;
  char *zDate;
  Blob uuid;
  Blob ctrl;
  Blob cksum;
  static const char zTagtype[] = { '-', '+', '*' };

  assert( tagtype>=0 && tagtype<=2 );
  user_select();
  rid = name_to_rid(zObjName);
  blob_zero(&uuid);
  db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
  blob_zero(&ctrl);
  
  if( validate16(zTagname, strlen(zTagname)) ){
    fossil_fatal("invalid tag name \"%s\" - might be confused with a UUID",
                 zTagname);
  }
  zDate = db_text(0, "SELECT datetime('now')");
  zDate[10] = 'T';
  blob_appendf(&ctrl, "D %s\n", zDate);
  blob_appendf(&ctrl, "T %c%F %b", zTagtype[tagtype], zTagname, &uuid);
  if( tagtype && zValue && zValue[0] ){
    blob_appendf(&ctrl, " %F\n", zValue);
  }else{
    blob_appendf(&ctrl, "\n");
  }
  blob_appendf(&ctrl, "U %F\n", g.zLogin);
  md5sum_blob(&ctrl, &cksum);
  blob_appendf(&ctrl, "Z %b\n", &cksum);
  db_begin_transaction();
  nrid = content_put(&ctrl, 0, 0);
  manifest_crosslink(nrid, &ctrl);
  db_end_transaction(0);
  
  /* Do an autosync push if requested */
  autosync(0);
}

/*
** COMMAND: tag
** Usage: %fossil tag SUBCOMMAND ...
**
** Run various subcommands to control tags and properties
**
**     %fossil tag add TAGNAME UUID ?VALUE?
**
**         Add a new tag or property to UUID.
**
**     %fossil tag branch TAGNAME UUID ?VALUE?
**
**         Add a new tag or property to UUID and make that
**         tag propagate to all direct children.
**
**     %fossil tag delete TAGNAME UUID
**
**         Delete the tag TAGNAME from UUID
**
**     %fossil tag find TAGNAME
**
**         List all baselines that use TAGNAME
**
**     %fossil tag list ?UUID?
**
**         List all tags, or if UUID is supplied, list
**         all tags and their values for UUID.
*/
void tag_cmd(void){
  int n;
  db_find_and_open_repository();
  if( g.argc<3 ){
    goto tag_cmd_usage;
  }
  n = strlen(g.argv[2]);
  if( n==0 ){
    goto tag_cmd_usage;
  }

  if( strncmp(g.argv[2],"add",n)==0 ){
    char *zValue;
    if( g.argc!=5 && g.argc!=6 ){
      usage("add TAGNAME UUID ?VALUE?");
    }
    zValue = g.argc==6 ? g.argv[5] : 0;
    tag_add_artifact(g.argv[3], g.argv[4], zValue, 1);
  }else

  if( strncmp(g.argv[2],"branch",n)==0 ){
    char *zValue;
    if( g.argc!=5 && g.argc!=6 ){
      usage("branch TAGNAME UUID ?VALUE?");
    }
    zValue = g.argc==6 ? g.argv[5] : 0;
    tag_add_artifact(g.argv[3], g.argv[4], zValue, 2);
  }else

  if( strncmp(g.argv[2],"delete",n)==0 ){
    if( g.argc!=5 ){
      usage("delete TAGNAME UUID");
    }
    tag_add_artifact(g.argv[3], g.argv[4], 0, 0);
  }else

  if( strncmp(g.argv[2],"find",n)==0 ){
    Stmt q;
    if( g.argc!=4 ){
      usage("find TAGNAME");
    }
    db_prepare(&q,
      "SELECT blob.uuid FROM tagxref, blob"
      " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
      "   AND blob.rid=tagxref.rid", g.argv[3]
    );
    while( db_step(&q)==SQLITE_ROW ){
      printf("%s\n", db_column_text(&q, 0));
    }
    db_finalize(&q);
  }else

  if( strncmp(g.argv[2],"list",n)==0 ){
    Stmt q;
    if( g.argc==3 ){
      db_prepare(&q, 
        "SELECT tagname"
        "  FROM tag"
        " WHERE EXISTS(SELECT 1 FROM tagxref"
        "               WHERE tagid=tag.tagid"
        "                 AND tagtype>0)"
        " ORDER BY tagname"
      );
      while( db_step(&q)==SQLITE_ROW ){
        printf("%s\n", db_column_text(&q, 0));
      }
      db_finalize(&q);
    }else if( g.argc==4 ){
      int rid = name_to_rid(g.argv[3]);
      db_prepare(&q,
        "SELECT tagname, value"
        "  FROM tagxref, tag"
        " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
        "   AND tagtype>0"
        " ORDER BY tagname",
        rid
      );
      while( db_step(&q)==SQLITE_ROW ){
        const char *zName = db_column_text(&q, 0);
        const char *zValue = db_column_text(&q, 1);
        if( zValue ){
          printf("%s=%s\n", zName, zValue);
        }else{
          printf("%s\n", zName);
        }
      }
      db_finalize(&q);
    }else{
      usage("tag list ?UUID?");
    }
  }else
  {
    goto tag_cmd_usage;
  }
  return;

tag_cmd_usage:
  usage("add|branch|delete|find|list ...");
}

Changes to src/timeline.c.

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89


90
91
92
93
94
95
96
97
98
99




100








101
102
103
104
105

106
107
108
109
110
111

112


113
114
115

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137



138


139
140
141
142
143
144
145
146
147
148
149
150
















151
152
153
154
155
156
157
...
178
179
180
181
182
183
184























185
186
187
188
189
190
191
192
193
194
195
196


197
198
199

200
201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246

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




261






262





263

264
265
266
267
268
269

270
271
272
273
274
275
276
...
339
340
341
342
343
344
345

346
347
348
349
350






351
352
353
354
355
356
357
358









359
360
361
362



363








364
365
366
367
368
369
370
371


372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392


393
394
395
396

397
398
399

400
401


















402
403
404
405
406
407
408
...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491

492

493
494
495
496
497
498
499
/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
  char zShortUuid[UUID_SIZE+1];
  sprintf(zShortUuid, "%.10s", zUuid);
  if( g.okHistory ){
    @ <a href="%s(g.zBaseURL)/vinfo/%s(zUuid)">[%s(zShortUuid)]</a>
  }else{
    @ <b>[%s(zShortUuid)]</b>
  }
}

/*
** Generate a hyperlink that invokes javascript to highlight
................................................................................
      @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
    }
  }
}

/*
** Output a timeline in the web format given a query.  The query
** should return 4 columns:
**
**    0.  rid
**    1.  UUID
**    2.  Date/Time
**    3.  Comment string
**    4.  User
**    5.  Number of non-merge children
**    6.  Number of parents
**    7.  True if is a leaf


*/
void www_print_timeline(
  Stmt *pQuery,
  int *pFirstEvent,
  int *pLastEvent,
  int (*xCallback)(int, Blob*),
  Blob *pArg
 ){
  char zPrevDate[20];
  int cnt = 0;




  zPrevDate[0] = 0;








  db_multi_exec(
     "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);"
     "DELETE FROM seen;"
  );
  @ <table cellspacing=0 border=0 cellpadding=0>

  while( db_step(pQuery)==SQLITE_ROW ){
    int rid = db_column_int(pQuery, 0);
    const char *zUuid = db_column_text(pQuery, 1);
    int nPChild = db_column_int(pQuery, 5);
    int nParent = db_column_int(pQuery, 6);
    int isLeaf = db_column_int(pQuery, 7);

    const char *zDate = db_column_text(pQuery, 2);


    if( cnt==0 && pFirstEvent ){
      *pFirstEvent = rid;
    }

    if( pLastEvent ){
      *pLastEvent = rid;
    }
    db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
    if( xCallback ){
      xCallback(rid, pArg);
    }
    if( memcmp(zDate, zPrevDate, 10) ){
      sprintf(zPrevDate, "%.10s", zDate);
      @ <tr><td colspan=3>
      @ <table cellpadding=2 border=0>
      @ <tr><td bgcolor="#a0b5f4" class="border1">
      @ <table cellpadding=2 cellspacing=0 border=0><tr>
      @ <td bgcolor="#d0d9f4" class="bkgnd1">%s(zPrevDate)</td>
      @ </tr></table>
      @ </td></tr></table>
      @ </td></tr>
    }
    @ <tr>
    @ <td valign="top">%s(&zDate[11])</td>
    @ <td width="20" align="center" valign="top">
    @ <font id="m%d(rid)" size="+1" color="white">*</font></td>



    @ <td valign="top" align="left">


    hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid);
    if( nParent>1 ){
      @ <b>Merge</b> 
    }
    if( nPChild>1 ){
      @ <b>Fork</b>
    }
    if( isLeaf ){
      @ <b>Leaf</b>
    }
    @ %h(db_column_text(pQuery,3))
    @ (by %h(db_column_text(pQuery,4)))</td></tr>
















  }
  @ </table>
}

/*
** Generate javascript code that records the parents and children
** of the version rid.
................................................................................
    blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
    zSep = ",";
  }
  db_finalize(&q);
  blob_appendf(pOut, "];\n");
  return 0;
}
























/*
** WEBPAGE: timeline
**
** Query parameters:
**
**    d=STARTDATE    date in iso8601 notation.          dflt: newest event
**    n=INTEGER      number of events to show.          dflt: 25
**    e=INTEGER      starting event id.                 dflt: nil
**    u=NAME         show only events from user.        dflt: nil
**    a              show events after and including.   dflt: false
**    r              show only related events.          dflt: false


*/
void page_timeline(void){
  Stmt q;

  char *zSQL;
  Blob scriptInit;
  char zDate[100];
  const char *zStart = P("d");
  int nEntry = atoi(PD("n","20"));
  const char *zUser = P("u");
  int objid = atoi(PD("e","0"));
  int relatedEvents = P("r")!=0;
  int afterFlag = P("a")!=0;

  int firstEvent;
  int lastEvent;

  /* To view the timeline, must have permission to read project data.
  */
  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }
................................................................................

  style_header("Timeline");
  if( !g.okHistory &&
      db_exists("SELECT 1 FROM user"
                " WHERE login='anonymous'"
                "   AND cap LIKE '%%h%%'") ){
    @ <p><b>Note:</b> You will be able to access <u>much</u> more
    @ historical information if <a href="%s(g.zBaseURL)/login">login</a>.</p>
  }
  zSQL = mprintf(
    "SELECT blob.rid, uuid, datetime(event.mtime,'localtime'), comment, user,"
    "       (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim=1),"
    "       (SELECT count(*) FROM plink WHERE cid=blob.rid),"
    "       NOT EXISTS (SELECT 1 FROM plink WHERE pid=blob.rid)"
    "  FROM event, blob"
    " WHERE event.type='ci' AND blob.rid=event.objid"
  );

  if( zUser ){
    zSQL = mprintf("%z AND event.user=%Q", zSQL, zUser);
  }
  if( objid ){
    char *z = db_text(0, "SELECT datetime(event.mtime) FROM event"
                         " WHERE objid=%d", objid);
    if( z ){
      zStart = z;
    }
  }
  if( zStart ){
    while( isspace(zStart[0]) ){ zStart++; }
    if( zStart[0] ){
      zSQL = mprintf("%z AND event.mtime %s julianday(%Q, 'localtime')",

                      zSQL, afterFlag ? ">=" : "<=", zStart);
    }
  }
  if( relatedEvents && objid ){
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    if( afterFlag ){
      compute_descendents(objid, nEntry);
    }else{
      compute_ancestors(objid, nEntry);
    }
    zSQL = mprintf("%z AND event.objid IN ok", zSQL);
  }




  zSQL = mprintf("%z ORDER BY event.mtime DESC LIMIT %d", zSQL, nEntry);






  db_prepare(&q, zSQL);





  free(zSQL);

  zDate[0] = 0;
  blob_zero(&scriptInit);
  zDate[0] = 0;
  www_print_timeline(&q, &firstEvent, &lastEvent,
                     save_parentage_javascript, &scriptInit);
  db_finalize(&q);

  if( zStart==0 ){
    zStart = zDate;
  }
  @ <script>
  @ var parentof = new Object();
  @ var childof = new Object();
  cgi_append_content(blob_buffer(&scriptInit), blob_size(&scriptInit));
................................................................................
  @ <form method="GET" action="%s(g.zBaseURL)/timeline">
  @ Start Date:
  @ <input type="text" size="30" value="%h(zStart)" name="d">
  @ Number Of Entries:  
  @ <input type="text" size="4" value="%d(nEntry)" name="n">
  @ <br><input type="submit" value="Submit">
  @ </form>

  @ <form method="GET" action="%s(g.zBaseURL)/timeline">
  @ <input type="hidden" value="%h(zDate)" name="d">
  @ <input type="hidden" value="%d(nEntry)" name="n">
  @ <input type="submit" value="Next %d(nEntry) Rows">
  @ </form>






  style_footer();
}

/*
** The input query q selects various records.  Print a human-readable
** summary of those records.
**
** Limit the number of entries printed to nLine.









*/
void print_timeline(Stmt *q, int mxLine){
  int nLine = 0;
  char zPrevDate[20];



  zPrevDate[0] = 0;









  while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){
    const char *zId = db_column_text(q, 1);
    const char *zDate = db_column_text(q, 2);
    const char *zCom = db_column_text(q, 3);
    int nChild = db_column_int(q, 4);
    int nParent = db_column_int(q, 5);
    char *zFree = 0;


    char zUuid[UUID_SIZE+1];

    sprintf(zUuid, "%.10s", zId);
    if( memcmp(zDate, zPrevDate, 10) ){
      printf("=== %.10s ===\n", zDate);
      memcpy(zPrevDate, zDate, 10);
      nLine++;
    }
    if( zCom==0 ) zCom = "";
    printf("%.8s ", &zDate[11]);
    if( nChild>1 || nParent>1 ){
      int n = 0;
      char zPrefix[50];
      if( nParent>1 ){
        sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
        n = strlen(zPrefix);
      }
      if( nChild>1 ){
        sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*FORK* ");
        n = strlen(zPrefix);
      }


      zFree = sqlite3_mprintf("[%.10s] %s%s", zUuid, zPrefix, zCom);
    }else{
      zFree = sqlite3_mprintf("[%.10s] %s", zUuid, zCom);
    }

    nLine += comment_print(zFree, 9, 79);
    sqlite3_free(zFree);
  }

}




















/*
** COMMAND: timeline
**
** Usage: %fossil timeline ?WHEN? ?UUID|DATETIME? ?-n|--count N?
**
** Print a summary of activity going backwards in date and time
................................................................................
  k = strlen(zOrigin);
  blob_zero(&uuid);
  blob_append(&uuid, zOrigin, -1);
  if( strcmp(zOrigin, "now")==0 ){
    if( mode==3 || mode==4 ){
      fossil_fatal("cannot compute descendents or ancestors of a date");
    }
    zDate = mprintf("(SELECT julianday('now','utc'))");
  }else if( strncmp(zOrigin, "current", k)==0 ){
    objid = db_lget_int("checkout",0);
    zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid);
  }else if( name_to_uuid(&uuid, 0)==0 ){
    objid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
    zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid);
  }else{
    if( mode==3 || mode==4 ){
      fossil_fatal("cannot compute descendents or ancestors of a date");
    }
    zDate = mprintf("(SELECT julianday(%Q, 'utc'))", zOrigin);
  }
  zSQL = mprintf(
    "SELECT blob.rid, uuid, datetime(event.mtime,'localtime'),"
    "       comment || ' (by ' || user || ')',"
    "       (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),"
    "       (SELECT count(*) FROM plink WHERE cid=blob.rid)"
    "  FROM event, blob"
    " WHERE event.type='ci' AND blob.rid=event.objid"
    "   AND event.mtime %s %s",

    (mode==1 || mode==4) ? "<=" : ">=", zDate

  );
  if( mode==3 || mode==4 ){
    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
    if( mode==3 ){
      compute_descendents(objid, n);
    }else{
      compute_ancestors(objid, n);







|







 







|









>
>








<

>
>
>
>

>
>
>
>
>
>
>
>





>






>

>
>



>










|
<
<
<
<
<






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







 







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












>
>



>









>







 







|

|
|
|
|
<
<
<
<
>

|


|








|
>
|











|

>
>
>
>
|
>
>
>
>
>
>

>
>
>
>
>
|
>






>







 







>

|


|
>
>
>
>
>
>








>
>
>
>
>
>
>
>
>




>
>
>

>
>
>
>
>
>
>
>








>
>

|








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

>



>


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







 







|












<
<
<
<
<
<
<
|
>
|
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144





145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166


167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
...
275
276
277
278
279
280
281
282
283
284
285
286
287




288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
...
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484


485
486
487
488
489
490
491
492
493
494
495
496


497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
...
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604







605
606
607
608
609
610
611
612
613
614
615
/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
  char zShortUuid[UUID_SIZE+1];
  sprintf(zShortUuid, "%.10s", zUuid);
  if( g.okHistory ){
    @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">[%s(zShortUuid)]</a>
  }else{
    @ <b>[%s(zShortUuid)]</b>
  }
}

/*
** Generate a hyperlink that invokes javascript to highlight
................................................................................
      @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
    }
  }
}

/*
** Output a timeline in the web format given a query.  The query
** should return these columns:
**
**    0.  rid
**    1.  UUID
**    2.  Date/Time
**    3.  Comment string
**    4.  User
**    5.  Number of non-merge children
**    6.  Number of parents
**    7.  True if is a leaf
**    8.  background color
**    9.  type ("ci", "w")
*/
void www_print_timeline(
  Stmt *pQuery,
  int *pFirstEvent,
  int *pLastEvent,
  int (*xCallback)(int, Blob*),
  Blob *pArg
 ){

  int cnt = 0;
  int wikiFlags;
  int mxWikiLen;
  Blob comment;
  char zPrevDate[20];
  zPrevDate[0] = 0;

  mxWikiLen = db_get_int("timeline-max-comment", 0);
  if( db_get_boolean("timeline-block-markup", 0) ){
    wikiFlags = WIKI_INLINE;
  }else{
    wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
  }

  db_multi_exec(
     "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);"
     "DELETE FROM seen;"
  );
  @ <table cellspacing=0 border=0 cellpadding=0>
  blob_zero(&comment);
  while( db_step(pQuery)==SQLITE_ROW ){
    int rid = db_column_int(pQuery, 0);
    const char *zUuid = db_column_text(pQuery, 1);
    int nPChild = db_column_int(pQuery, 5);
    int nParent = db_column_int(pQuery, 6);
    int isLeaf = db_column_int(pQuery, 7);
    const char *zBgClr = db_column_text(pQuery, 8);
    const char *zDate = db_column_text(pQuery, 2);
    const char *zType = db_column_text(pQuery, 9);
    const char *zUser = db_column_text(pQuery, 4);
    if( cnt==0 && pFirstEvent ){
      *pFirstEvent = rid;
    }
    cnt++;
    if( pLastEvent ){
      *pLastEvent = rid;
    }
    db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
    if( xCallback ){
      xCallback(rid, pArg);
    }
    if( memcmp(zDate, zPrevDate, 10) ){
      sprintf(zPrevDate, "%.10s", zDate);
      @ <tr><td colspan=3>
      @   <div class="divider">%s(zPrevDate)</div>





      @ </td></tr>
    }
    @ <tr>
    @ <td valign="top">%s(&zDate[11])</td>
    @ <td width="20" align="center" valign="top">
    @ <font id="m%d(rid)" size="+1" color="white">*</font></td>
    if( zBgClr && zBgClr[0] ){
      @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
    }else{
      @ <td valign="top" align="left">
    }
    if( zType[0]=='c' ){
      hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid);
      if( nParent>1 ){
        @ <b>Merge</b> 
      }
      if( nPChild>1 ){
        @ <b>Fork</b>
      }
      if( isLeaf ){
        @ <b>Leaf</b>
      }


    }else{
      hyperlink_to_uuid(zUuid);
    }
    db_column_blob(pQuery, 3, &comment);
    if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
      Blob truncated;
      blob_zero(&truncated);
      blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
      blob_append(&truncated, "...", 3);
      wiki_convert(&truncated, 0, wikiFlags);
      blob_reset(&truncated);
    }else{
      wiki_convert(&comment, 0, wikiFlags);
    }
    blob_reset(&comment);
    @ (by %h(zUser))</td></tr>
  }
  @ </table>
}

/*
** Generate javascript code that records the parents and children
** of the version rid.
................................................................................
    blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
    zSep = ",";
  }
  db_finalize(&q);
  blob_appendf(pOut, "];\n");
  return 0;
}

/*
** Return a pointer to a constant string that forms the basis
** for a timeline query for the WWW interface.
*/
const char *timeline_query_for_www(void){
  static const char zBaseSql[] =
    @ SELECT
    @   blob.rid,
    @   uuid,
    @   datetime(event.mtime,'localtime') AS timestamp,
    @   coalesce(ecomment, comment),
    @   coalesce(euser, user),
    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim=1),
    @   (SELECT count(*) FROM plink WHERE cid=blob.rid),
    @   NOT EXISTS (SELECT 1 FROM plink WHERE pid=blob.rid),
    @   coalesce(bgcolor, brbgcolor),
    @   event.type
    @  FROM event JOIN blob 
    @ WHERE blob.rid=event.objid
  ;
  return zBaseSql;
}

/*
** WEBPAGE: timeline
**
** Query parameters:
**
**    d=STARTDATE    date in iso8601 notation.          dflt: newest event
**    n=INTEGER      number of events to show.          dflt: 25
**    e=INTEGER      starting event id.                 dflt: nil
**    u=NAME         show only events from user.        dflt: nil
**    a              show events after and including.   dflt: false
**    r              show only related events.          dflt: false
**    y=TYPE         show only TYPE ('ci' or 'w')       dflt: nil
**    s              show the SQL                       dflt: nil
*/
void page_timeline(void){
  Stmt q;
  Blob sql;
  char *zSQL;
  Blob scriptInit;
  char zDate[100];
  const char *zStart = P("d");
  int nEntry = atoi(PD("n","20"));
  const char *zUser = P("u");
  int objid = atoi(PD("e","0"));
  int relatedEvents = P("r")!=0;
  int afterFlag = P("a")!=0;
  const char *zType = P("y");
  int firstEvent;
  int lastEvent;

  /* To view the timeline, must have permission to read project data.
  */
  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }
................................................................................

  style_header("Timeline");
  if( !g.okHistory &&
      db_exists("SELECT 1 FROM user"
                " WHERE login='anonymous'"
                "   AND cap LIKE '%%h%%'") ){
    @ <p><b>Note:</b> You will be able to access <u>much</u> more
    @ historical information if <a href="%s(g.zTop)/login">login</a>.</p>
  }
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  if( zType ){
    blob_appendf(&sql, " AND event.type=%Q", zType);




  }
  if( zUser ){
    blob_appendf(&sql, " AND event.user=%Q", zUser);
  }
  if( objid ){
    char *z = db_text(0, "SELECT datetime(event.mtime, 'localtime') FROM event"
                         " WHERE objid=%d", objid);
    if( z ){
      zStart = z;
    }
  }
  if( zStart ){
    while( isspace(zStart[0]) ){ zStart++; }
    if( zStart[0] ){
      blob_appendf(&sql, 
         " AND event.mtime %s (SELECT julianday(%Q, 'utc'))",
                          afterFlag ? ">=" : "<=", zStart);
    }
  }
  if( relatedEvents && objid ){
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    if( afterFlag ){
      compute_descendents(objid, nEntry);
    }else{
      compute_ancestors(objid, nEntry);
    }
    blob_append(&sql, " AND event.objid IN ok", -1);
  }
  if( afterFlag ){
    blob_appendf(&sql, " ORDER BY event.mtime ASC LIMIT %d",
                 nEntry);
  }else{
    blob_appendf(&sql, " ORDER BY event.mtime DESC LIMIT %d",
                 nEntry);
  }
  zSQL = blob_str(&sql);
  if( afterFlag ){
    zSQL = mprintf("SELECT * FROM (%s) ORDER BY timestamp DESC", zSQL);
  }
  db_prepare(&q, zSQL);
  if( P("s")!=0 ){
    @ <hr><p>%h(zSQL)</p><hr>
  }
  blob_zero(&sql);
  if( afterFlag ){
    free(zSQL);
  }
  zDate[0] = 0;
  blob_zero(&scriptInit);
  zDate[0] = 0;
  www_print_timeline(&q, &firstEvent, &lastEvent,
                     save_parentage_javascript, &scriptInit);
  db_finalize(&q);
  @ <p>firstEvent=%d(firstEvent) lastEvent=%d(lastEvent)</p>
  if( zStart==0 ){
    zStart = zDate;
  }
  @ <script>
  @ var parentof = new Object();
  @ var childof = new Object();
  cgi_append_content(blob_buffer(&scriptInit), blob_size(&scriptInit));
................................................................................
  @ <form method="GET" action="%s(g.zBaseURL)/timeline">
  @ Start Date:
  @ <input type="text" size="30" value="%h(zStart)" name="d">
  @ Number Of Entries:  
  @ <input type="text" size="4" value="%d(nEntry)" name="n">
  @ <br><input type="submit" value="Submit">
  @ </form>
  @ <table><tr><td>
  @ <form method="GET" action="%s(g.zBaseURL)/timeline">
  @ <input type="hidden" value="%d(lastEvent)" name="e">
  @ <input type="hidden" value="%d(nEntry)" name="n">
  @ <input type="submit" value="Next %d(nEntry) Rows">
  @ </form></td><td>
  @ <form method="GET" action="%s(g.zBaseURL)/timeline">
  @ <input type="hidden" value="%d(firstEvent)" name="e">
  @ <input type="hidden" value="%d(nEntry)" name="n">
  @ <input type="hidden" value="1" name="a">
  @ <input type="submit" value="Previous %d(nEntry) Rows">
  @ </form></td></tr></table>
  style_footer();
}

/*
** The input query q selects various records.  Print a human-readable
** summary of those records.
**
** Limit the number of entries printed to nLine.
** 
** The query should return these columns:
**
**    0.  rid
**    1.  uuid
**    2.  Date/Time
**    3.  Comment string and user
**    4.  Number of non-merge children
**    5.  Number of parents
*/
void print_timeline(Stmt *q, int mxLine){
  int nLine = 0;
  char zPrevDate[20];
  const char *zCurrentUuid=0;
  Stmt currentQ;
  int rid = db_lget_int("checkout", 0);
  zPrevDate[0] = 0;

  db_prepare(&currentQ,
    "SELECT uuid"
    "  FROM blob WHERE rid=%d", rid
  );
  if( db_step(&currentQ)==SQLITE_ROW ){
    zCurrentUuid = db_column_text(&currentQ, 0);
  }

  while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){
    const char *zId = db_column_text(q, 1);
    const char *zDate = db_column_text(q, 2);
    const char *zCom = db_column_text(q, 3);
    int nChild = db_column_int(q, 4);
    int nParent = db_column_int(q, 5);
    char *zFree = 0;
    int n = 0;
    char zPrefix[80];
    char zUuid[UUID_SIZE+1];
    
    sprintf(zUuid, "%.10s", zId);
    if( memcmp(zDate, zPrevDate, 10) ){
      printf("=== %.10s ===\n", zDate);
      memcpy(zPrevDate, zDate, 10);
      nLine++;
    }
    if( zCom==0 ) zCom = "";
    printf("%.8s ", &zDate[11]);


    zPrefix[0] = 0;
    if( nParent>1 ){
      sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
      n = strlen(zPrefix);
    }
    if( nChild>1 ){
      sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*FORK* ");
      n = strlen(zPrefix);
    }
    if( strcmp(zCurrentUuid,zId)==0 ){
      sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* ");
      n += strlen(zPrefix);


    }
    zFree = sqlite3_mprintf("[%.10s] %s%s", zUuid, zPrefix, zCom);
    nLine += comment_print(zFree, 9, 79);
    sqlite3_free(zFree);
  }
  db_finalize(&currentQ);
}

/*
** Return a pointer to a static string that forms the basis for
** a timeline query for display on a TTY.
*/
const char *timeline_query_for_tty(void){
  static const char zBaseSql[] = 
    @ SELECT
    @   blob.rid,
    @   uuid,
    @   datetime(event.mtime,'localtime'),
    @   coalesce(ecomment,comment) || ' (by ' || coalesce(euser,user,'?') ||')',
    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),
    @   (SELECT count(*) FROM plink WHERE cid=blob.rid)
    @ FROM event, blob
    @ WHERE blob.rid=event.objid
  ;
  return zBaseSql;
}

/*
** COMMAND: timeline
**
** Usage: %fossil timeline ?WHEN? ?UUID|DATETIME? ?-n|--count N?
**
** Print a summary of activity going backwards in date and time
................................................................................
  k = strlen(zOrigin);
  blob_zero(&uuid);
  blob_append(&uuid, zOrigin, -1);
  if( strcmp(zOrigin, "now")==0 ){
    if( mode==3 || mode==4 ){
      fossil_fatal("cannot compute descendents or ancestors of a date");
    }
    zDate = mprintf("(SELECT datetime('now'))");
  }else if( strncmp(zOrigin, "current", k)==0 ){
    objid = db_lget_int("checkout",0);
    zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid);
  }else if( name_to_uuid(&uuid, 0)==0 ){
    objid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
    zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid);
  }else{
    if( mode==3 || mode==4 ){
      fossil_fatal("cannot compute descendents or ancestors of a date");
    }
    zDate = mprintf("(SELECT julianday(%Q, 'utc'))", zOrigin);
  }







  zSQL = mprintf("%s AND event.mtime %s %s",
     timeline_query_for_tty(),
     (mode==1 || mode==4) ? "<=" : ">=",
     zDate
  );
  if( mode==3 || mode==4 ){
    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
    if( mode==3 ){
      compute_descendents(objid, n);
    }else{
      compute_ancestors(objid, n);

Added src/tkt.c.











































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used render and control ticket entry
** and display pages.
*/
#include "config.h"
#include "tkt.h"
#include <assert.h>

/*
** The list of database user-defined fields in the TICKET table.
** The real table also contains some addition fields for internal
** used.  The internal-use fields begin with "tkt_".
*/
static int nField = 0;
static char **azField = 0;    /* Names of database fields */
static char **azValue = 0;    /* Original values */
static char **azAppend = 0;   /* Value to be appended */

/*
** A subscript interpreter used for processing Tickets.
*/
static struct Subscript *pInterp = 0;

/*
** Compare two entries in azField for sorting purposes
*/
static int nameCmpr(const void *a, const void *b){
  return strcmp(*(char**)a, *(char**)b);
}

/*
** Obtain a list of all fields of the TICKET table.  Put them 
** in sorted order in azField[].
**
** Also allocate space for azValue[] and azAppend[] and initialize
** all the values there to zero.
*/
static void getAllTicketFields(void){
  Stmt q;
  int i;
  if( nField>0 ) return;
  db_prepare(&q, "PRAGMA table_info(ticket)");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zField = db_column_text(&q, 1);
    if( strncmp(zField,"tkt_",4)==0 ) continue;
    if( nField%10==0 ){
      azField = realloc(azField, sizeof(azField)*3*(nField+10) );
      if( azField==0 ){
        fossil_fatal("out of memory");
      }
    }
    azField[nField] = mprintf("%s", zField);
    nField++;
  }
  db_finalize(&q);
  qsort(azField, nField, sizeof(azField[0]), nameCmpr);
  azAppend = &azField[nField];
  memset(azAppend, 0, sizeof(azAppend[0])*nField);
  azValue = &azAppend[nField];
  for(i=0; i<nField; i++){
    azValue[i] = "";
  }
}

/*
** Return the index into azField[] of the given field name.
** Return -1 if zField is not in azField[].
*/
static int fieldId(const char *zField){
  int i;
  for(i=0; i<nField; i++){
    if( strcmp(azField[i], zField)==0 ) return i;
  }
  return -1;
}

/*
** Query the database for all TICKET fields for the specific
** ticket whose name is given by the "name" CGI parameter.
** Load the values for all fields into the interpreter.
**
** Only load those fields which do not already exist as
** variables.
*/
static void initializeVariablesFromDb(void){
  const char *zName;
  Stmt q;
  int i, n, size, j;

  zName = PD("name","");
  db_prepare(&q, "SELECT * FROM ticket WHERE tkt_uuid GLOB '%q*'", zName);
  if( db_step(&q)==SQLITE_ROW ){
    n = db_column_count(&q);
    for(i=0; i<n; i++){
      const char *zVal = db_column_text(&q, i);
      const char *zName = db_column_name(&q, i);
      if( zVal==0 ) zVal = "";
      for(j=0; j<nField; j++){
        if( strcmp(azField[j],zName)==0 ){
          azValue[j] = mprintf("%s", zVal);
          break;
        }
      }
      if( SbS_Fetch(pInterp, zName, -1, &size)==0 ){
        SbS_Store(pInterp, db_column_name(&q,i), zVal, 1);
      }
    }
  }else{
    db_finalize(&q);
    db_prepare(&q, "PRAGMA table_info(ticket)");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zField = db_column_text(&q, 1);
      if( SbS_Fetch(pInterp, zField, -1, &size)==0 ){
        SbS_Store(pInterp, zField, "", 0);
      }
    }
  }
  db_finalize(&q);
}

/*
** Transfer all CGI parameters to variables in the interpreter.
*/
static void initializeVariablesFromCGI(void){
  int i;
  const char *z;

  for(i=0; (z = cgi_parameter_name(i))!=0; i++){
    SbS_Store(pInterp, z, P(z), 0);
  }
}

/*
** Rebuild all tickets named in the _pending_ticket table.
**
** This routine is called just prior to commit after new
** out-of-sequence ticket changes have been added.
*/
static int ticket_rebuild_at_commit(void){
  Stmt q;
  db_multi_exec(
    "DELETE FROM ticket WHERE tkt_uuid IN _pending_ticket"
  );
  db_prepare(&q, "SELECT uuid FROM _pending_ticket");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    ticket_rebuild_entry(zUuid);
  }
  db_multi_exec(
    "DELETE FROM _pending_ticket"
  );
  return 0;
}

/*
** Update an entry of the TICKET table according to the information
** in the control file given in p.  Attempt to create the appropriate
** TICKET table entry if createFlag is true.  If createFlag is false,
** that means we already know the entry exists and so we can save the
** work of trying to create it.
*/
void ticket_insert(Manifest *p, int createFlag, int checkTime){
  Blob sql;
  Stmt q;
  int i;
  const char *zSep;

  getAllTicketFields();
  if( createFlag ){  
    db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) "
                  "VALUES(%Q, 0)", p->zTicketUuid);
  }
  blob_zero(&sql);
  blob_appendf(&sql, "UPDATE ticket SET tkt_mtime=:mtime");
  zSep = "SET";
  for(i=0; i<p->nField; i++){
    const char *zName = p->aField[i].zName;
    if( zName[0]=='+' ){
      zName++;
      if( fieldId(zName)<0 ) continue;
      blob_appendf(&sql,", %s=%s || %Q", zName, zName, p->aField[i].zValue);
    }else{
      if( fieldId(zName)<0 ) continue;
      blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue);
    }
  }
  blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime",
                     p->zTicketUuid);
  db_prepare(&q, "%s", blob_str(&sql));
  db_bind_double(&q, ":mtime", p->rDate);
  db_step(&q);
  db_finalize(&q);
  if( checkTime && db_changes()==0 ){
    static int isInit = 0;
    if( !isInit ){
      db_multi_exec("CREATE TEMP TABLE _pending_ticket(uuid TEXT UNIQUE)");
      db_commit_hook(ticket_rebuild_at_commit, 1);
      isInit = 1;
    }
    db_multi_exec("INSERT OR IGNORE INTO _pending_ticket "
                  "VALUES(%Q)", p->zTicketUuid);
  }
  blob_reset(&sql);
}

/*
** Rebuild an entire entry in the TICKET table
*/
void ticket_rebuild_entry(const char *zTktUuid){
  char *zTag = mprintf("tkt-%s", zTktUuid);
  int tagid = tag_findid(zTag, 1);
  Stmt q;
  Manifest manifest;
  Blob content;
  int createFlag = 1;
  
  db_multi_exec(
     "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
  );
  db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
  while( db_step(&q)==SQLITE_ROW ){
    int rid = db_column_int(&q, 0);
    content_get(rid, &content);
    manifest_parse(&manifest, &content);
    ticket_insert(&manifest, createFlag, 0);
    manifest_clear(&manifest);
    createFlag = 0;
  }
  db_finalize(&q);
}

/*
** Create the subscript interpreter and load the ticket configuration.
*/
void ticket_init(void){
  char *zConfig;
  if( pInterp ) return;
  pInterp = SbS_Create();
  zConfig = db_text((char*)zDefaultTicketConfig,
             "SELECT value FROM config WHERE name='ticket-configuration'");
  SbS_Eval(pInterp, zConfig, -1);
}

/*
** Recreate the ticket table.
*/
void ticket_create_table(int separateConnection){
  char *zSql;
  int nSql;

  db_multi_exec("DROP TABLE IF EXISTS ticket;");
  ticket_init();
  zSql = (char*)SbS_Fetch(pInterp, "ticket_sql", -1, &nSql);
  if( zSql==0 ){
    fossil_panic("no ticket_sql defined by ticket configuration");
  }
  if( separateConnection ){
    zSql = mprintf("%.*s", nSql, zSql);
    db_init_database(g.zRepositoryName, zSql, 0);
    free(zSql);
  }else{
    db_multi_exec("%.*s", nSql, zSql);
  }
}

/*
** Repopulate the ticket table
*/
void ticket_rebuild(void){
  Stmt q;
  db_begin_transaction();
  db_prepare(&q,"SELECT tagname FROM tag WHERE tagname GLOB 'tkt-*'");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    int len;
    zName += 4;
    len = strlen(zName);
    if( len<20 || !validate16(zName, len) ) continue;
    ticket_rebuild_entry(zName);
  }
  db_finalize(&q);
  db_end_transaction(0);
}

/*
** WEBPAGE: tktview
**
** View a ticket.
*/
void tktview_page(void){
  char *zScript;
  int nScript;
  login_check_credentials();
  if( !g.okRdTkt ){ login_needed(); return; }
  if( g.okWrTkt ){
    style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
        g.zTop, PD("name",""));
  }
  style_header("View Ticket");
  ticket_init();
  initializeVariablesFromDb();
  zScript = (char*)SbS_Fetch(pInterp, "tktview_template", -1, &nScript);
  zScript = mprintf("%.*s", nScript, zScript);
  SbS_Render(pInterp, zScript);
  style_footer();
}



/*
** Subscript command:   STRING FIELD append_field
**
** FIELD is the name of a database column to which we might want
** to append text.  STRING is the text to be appended to that
** column.  The append does not actually occur until the
** submit_ticket_change verb is run.
*/
static int appendRemarkCmd(struct Subscript *p, void *notUsed){
  int idx;
  const char *zField, *zValue;
  int nField, nValue;

  if( SbS_RequireStack(p, 2, "append_field") ) return 1;
  zField = SbS_StackValue(p, 0, &nField);
  for(idx=0; idx<nField; idx++){
    if( strncmp(azField[idx], zField, nField)==0 && azField[idx][nField]==0 ){
      break;
    }
  }
  if( idx>=nField ){
    SbS_SetErrorMessage(p, "no such TICKET column: %.*s", nField, zField);
    return SBS_ERROR;
  }
  zValue = SbS_StackValue(p, 1, &nValue);
  azAppend[idx] = mprintf("%.*s", nValue, zValue);
  SbS_Pop(p, 2);
  return SBS_OK;
}

/*
** Subscript command:   submit_ticket
**
** Construct and submit a new ticket artifact.
*/
static int submitTicketCmd(struct Subscript *p, void *pUuid){
  char *zDate;
  const char *zUuid;
  int i;
  int rid;
  Blob tktchng, cksum;

  zUuid = (const char *)pUuid;
  blob_zero(&tktchng);
  zDate = db_text(0, "SELECT datetime('now')");
  zDate[10] = 'T';
  blob_appendf(&tktchng, "D %s\n", zDate);
  free(zDate);
  for(i=0; i<nField; i++){
    const char *zValue;
    int nValue;
    if( azAppend[i] ){
      blob_appendf(&tktchng, "J +%s %z\n", azField[i],
                   fossilize(azAppend[i], -1));
    }else{
      zValue = SbS_Fetch(p, azField[i], -1, &nValue);
      if( zValue ){
        while( nValue>0 && isspace(zValue[nValue-1]) ){ nValue--; }
        if( strncmp(zValue, azValue[i], nValue)
                || strlen(azValue[i])!=nValue ){
          blob_appendf(&tktchng, "J %s %z\n",
             azField[i], fossilize(zValue,nValue));
        }
      }
    }
  }
  if( *(char**)pUuid ){
    zUuid = db_text(0, 
       "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", P("name")
    );
  }else{
    zUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
  }
  *(const char**)pUuid = zUuid;
  blob_appendf(&tktchng, "K %s\n", zUuid);
  blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : "");
  md5sum_blob(&tktchng, &cksum);
  blob_appendf(&tktchng, "Z %b\n", &cksum);

  if( strncmp(g.zPath,"debug_",6)==0 ){
    @ <hr><pre>
    @ %h(blob_str(&tktchng))
    @ </pre><hr>
    blob_zero(&tktchng);
    SbS_Pop(p, 1);
    return SBS_OK;
  }

  rid = content_put(&tktchng, 0, 0);
  if( rid==0 ){
    fossil_panic("trouble committing ticket: %s", g.zErrMsg);
  }
  manifest_crosslink(rid, &tktchng);
  return SBS_RETURN;
}


/*
** WEBPAGE: tktnew
** WEBPAGE: debug_tktnew
**
** Enter a new ticket.  the tktnew_template script in the ticket
** configuration is used.  The /tktnew page is the official ticket
** entry page.  The /debug_tktnew page is used for debugging the
** tktnew_template in the ticket configuration.  /debug_tktnew works
** just like /tktnew except that it does not really save the new ticket
** when you press submit - it just prints the ticket artifact at the
** top of the screen.
*/
void tktnew_page(void){
  char *zScript;
  int nScript;
  char *zNewUuid = 0;

  login_check_credentials();
  if( !g.okNewTkt ){ login_needed(); return; }
  style_header("New Ticket");
  ticket_init();
  getAllTicketFields();
  initializeVariablesFromCGI();
  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
  zScript = (char*)SbS_Fetch(pInterp, "tktnew_template", -1, &nScript);
  zScript = mprintf("%.*s", nScript, zScript);
  SbS_Store(pInterp, "login", g.zLogin, 0);
  SbS_Store(pInterp, "date", db_text(0, "SELECT datetime('now')"), 2);
  SbS_AddVerb(pInterp, "submit_ticket", submitTicketCmd, (void*)&zNewUuid);
  if( SbS_Render(pInterp, zScript)==SBS_RETURN && zNewUuid ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zNewUuid));
    return;
  }
  @ </form>
  style_footer();
}

/*
** WEBPAGE: tktedit
** WEBPAGE: debug_tktedit
**
** Edit a ticket.  The ticket is identified by the name CGI parameter.
** /tktedit is the official page.  The /debug_tktedit page does the same
** thing except that it does not save the ticket change record when you
** press submit - it instead prints the ticket change record at the top
** of the page.  The /debug_tktedit page is intended to be used when
** debugging ticket configurations.
*/
void tktedit_page(void){
  char *zScript;
  int nScript;
  int nName;
  const char *zName;
  int nRec;

  login_check_credentials();
  if( !g.okApndTkt && !g.okWrTkt ){ login_needed(); return; }
  style_header("Edit Ticket");
  zName = P("name");
  if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
          || !validate16(zName,nName) ){
    @ <font color="red"><b>Not a valid ticket id: \"%h(zName)\"</b></font>
    style_footer();
    return;
  }
  nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
                zName);
  if( nRec==0 ){
    @ <font color="red"><b>No such ticket: \"%h(zName)\"</b></font>
    style_footer();
    return;
  }
  if( nRec>1 ){
    @ <font color="red"><b>%d(nRec) tickets begin with: \"%h(zName)\"</b></font>
    style_footer();
    return;
  }
  ticket_init();
  getAllTicketFields();
  initializeVariablesFromCGI();
  initializeVariablesFromDb();
  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
  @ <input type="hidden" name="name" value="%s(zName)">
  zScript = (char*)SbS_Fetch(pInterp, "tktedit_template", -1, &nScript);
  zScript = mprintf("%.*s", nScript, zScript);
  SbS_Store(pInterp, "login", g.zLogin, 0);
  SbS_Store(pInterp, "date", db_text(0, "SELECT datetime('now')"), 2);
  SbS_AddVerb(pInterp, "append_field", appendRemarkCmd, 0);
  SbS_AddVerb(pInterp, "submit_ticket", submitTicketCmd, (void*)&zName);
  if( SbS_Render(pInterp, zScript)==SBS_RETURN && zName ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zName));
    return;
  }
  @ </form>
  style_footer();
}

/*
** Check the ticket configuration in zConfig to see if it appears to
** be well-formed.  If everything is OK, return NULL.  If something is
** amiss, then return a pointer to a string (obtained from malloc) that
** describes the problem.
*/
char *ticket_config_check(const char *zConfig){
  struct Subscript *p;
  char *zErr = 0;
  const char *z;
  int n;
  int i;
  int rc;
  sqlite3 *db;
  static const char *azRequired[] = {
     "tktnew_template",
     "tktview_template",
     "tktedit_template",
  };
  
  p = SbS_Create();
  rc = SbS_Eval(p, zConfig, strlen(zConfig));
  if( rc!=SBS_OK ){
    zErr = mprintf("%s", SbS_GetErrorMessage(p));
    SbS_Destroy(p);
    return zErr;
  }
  for(i=0; i<sizeof(azRequired)/sizeof(azRequired[0]); i++){
    z = SbS_Fetch(p, azRequired[i], -1, &n);
    if( z==0 ){
      zErr = mprintf("missing definition: %s", azRequired[i]);
      SbS_Destroy(p);
      return zErr;
    }
  }
  z = SbS_Fetch(p, "ticket_sql", -1, &n);
  if( z==0 ){
    zErr = mprintf("missing definition: ticket_sql");
    SbS_Destroy(p);
    return zErr;
  }
  rc = sqlite3_open(":memory:", &db);
  if( rc==SQLITE_OK ){
    char *zSql = mprintf("%.*s", n, z);
    rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
    if( rc!=SQLITE_OK ){
      sqlite3_close(db);
      SbS_Destroy(p);
      return zErr;
    }
    /* TODO: verify that the TICKET table exists and has required fields */
    sqlite3_close(db);
  }
  SbS_Destroy(p);
  return 0;
}

Added src/tktconfig.c.



































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
** 
** This file contains a string constant that is the default ticket
** configuration.
*/
#include "config.h"
#include "tktconfig.h"

/*
** This is a sample "ticket configuration" file for fossil.
**
** There is considerable flexibility in how tickets are defined
** in fossil.  Each repository can define its own ticket setup
** differently.  Each repository has an instance of a file, like
** this one that defines how that repository deals with tickets.
**
** This file is in the form of a script in an exceedingly 
** minimalist scripting language called "subscript".  Here are
** the rules:
**
** SYNTAX
**
**     *  The script consists of a sequence of whitespace
**        separated tokens.  Whitespace is ignored, except
**        as its role as a token separator.
**
**     *  Lines that begin with '#' are considered to be whitespace.
**
**     *  Text within matching {...} is consider to be a single "string"
**        token, even if the text spans multiple lines and includes
**        embedded whitespace.  The outermost {...} are not part of
**        the text of the token.
**
**     *  A token that begins with "/" is a string token.
**
**     *  A token that looks like a number is a string token.
**
**     *  Tokens that do not fall under the previous three rules
**        are "verb" tokens.
**
**  PROCESSING:
**
**     *  When a script is processed, the engine reads tokens
**        one by one.
**
**     *  String tokens are pushed onto the stack.
**
**     *  Verb tokens which correspond to the names of variables
**        cause the corresponding variable to be pushed onto the
**        stack.
**
**     *  Verb tokens which correspond to procedures cause the
**        procedures to run.  The procedures might push or pull 
**        values from the stack.
**
** There are just a handful of verbs.  For the purposes of this
** configuration script, there is only a single verb: "set".  The
** "set" verb pops two arguments from the stack.  The topmost is
** the name of a variable.  The second element is the value.  The
** "set" verb sets the value of the named variable.
**
**      VALUE NAME set
**
** This configuration file just sets the values of various variables.
** Some of the variables have special meanings.  The content of some
** of the variables are additional subscript scripts.
*/

/* @-comment: ** */
const char zDefaultTicketConfig[] = 
@ ############################################################################
@ # Every ticket configuration *must* define an SQL statement that creates
@ # the TICKET table.  This table must have three columns named
@ # tkt_id, tkt_uuid, and tkt_mtime.  tkt_id must be the integer primary
@ # key and tkt_uuid and tkt_mtime must be unique.  A configuration should
@ # define addition columns as necessary.  All columns should be in all
@ # lower-case letters and should not begin with "tkt".
@ #
@ {
@    CREATE TABLE ticket(
@      -- Do not change any column that begins with tkt_
@      tkt_id INTEGER PRIMARY KEY,
@      tkt_uuid TEXT,
@      tkt_mtime DATE,
@      -- Add as many field as required below this line
@      type TEXT,
@      status TEXT,
@      subsystem TEXT,
@      priority TEXT,
@      severity TEXT,
@      foundin TEXT,
@      contact TEXT,
@      title TEXT,
@      comment TEXT,
@      -- Do not alter this UNIQUE clause:
@      UNIQUE(tkt_uuid, tkt_mtime)
@    );
@    -- Add indices as desired
@ } /ticket_sql set
@ 
@ ############################################################################
@ # You can define additional variables here.  These variables will be
@ # accessible to the page templates when they run.
@ #
@ {
@    Code_Defect
@    Build_Problem
@    Documentation
@    Feature_Request
@    Incident
@ } /type_choices set
@ {Immediate High Medium Low Zero} /priority_choices set
@ {Critical Severe Important Minor Cosmetic} /severity_choices set
@ {
@   Open
@   Fixed
@   Rejected
@   Unable_To_Reproduce
@   Works_As_Designed
@   External_Bug
@   Not_A_Bug
@   Duplicate
@   Overcome_By_Events
@   Drive_By_Patch
@ } /resolution_choices set
@ {
@   Open
@   Verified
@   In_Process
@   Deferred
@   Fixed
@   Tested
@   Closed
@ } /status_choices set
@ {one two three} /subsystem_choices set
@ 
@ ##########################################################################
@ # The "tktnew_template" variable is set to text which is a template for
@ # the HTML of the "New Ticket" page.  Within this template, text contained
@ # within [...] is subscript.  That subscript runs when the page is
@ # rendered.
@ #
@ {
@   <!-- load database field names not found in CGI with an empty string -->
@   <!-- start a form -->
@   [{
@      {Open} /status set
@       submit_ticket
@   } /submit exists if]
@   <table cellpadding="5">
@   <tr>
@   <td colspan="2">
@   Enter a one-line summary of the problem:<br>
@   <input type="text" name="title" size="60" value="[{} /title get html]">
@   </td>
@   </tr>
@   
@   <tr>
@   <td align="right">Type:
@   [/type type_choices 1 combobox]
@   </td>
@   <td>What type of ticket is this?</td>
@   </tr>
@   
@   <tr>
@   <td align="right">Version: 
@   <input type="text" name="foundin" size="20" value="[{} /foundin get html]">
@   </td>
@   <td>In what version or build number do you observer the problem?</td>
@   </tr>
@   
@   <tr>
@   <td align="right">Severity:
@   [/severity severity_choices 1 combobox]
@   </td>
@   <td>How debilitating is the problem?  How badly does the problem
@   effect the operation of the product?</td>
@   </tr>
@   
@   <tr>
@   <td align="right">EMail:
@   <input type="text" name="contact" value="[{} /contact get html]" size="30">
@   </td>
@   <td>Not publically visible. Used by developers to contact you with
@   questions.</td>
@   </tr>
@   
@   <tr>
@   <td colspan="2">
@   Enter a detailed description of the problem.
@   For code defects, be sure to provide details on exactly how
@   the problem can be reproduced.  Provide as much detail as
@   possible.
@   <br>
@   <textarea name="comment" cols="80"
@    rows="[{} /comment get linecount 50 max 10 min html]"
@    wrap="virtual" class="wikiedit">[{} /comment get html]</textarea><br>
@   <input type="submit" name="preview" value="Preview">
@   </tr>
@ 
@   [/preview exists enable_output]
@   <tr><td colspan="2">
@   Description Preview:<br><hr>
@   [{} /comment get wiki]
@   <hr>
@   </td></tr>
@   [1 enable_output]
@   
@   <tr>
@   <td align="right">
@   <input type="submit" name="submit" value="Submit">
@   </td>
@   <td>After filling in the information above, press this button to create
@   the new ticket</td>
@   </tr>
@   </table>
@   <!-- end of form -->
@ } /tktnew_template set
@ 
@ ##########################################################################
@ # The template for the "edit ticket" page
@ #
@ # Then generated text is inserted inside a form which feeds back to itself.
@ # All CGI parameters are loaded into variables.  All database files are
@ # loaded into variables if they have not previously been loaded by
@ # CGI parameters.
@ {
@   [
@     login /username get /username set
@     {
@       {
@         username login eq /samename set
@         {
@            "\n\n<hr><i>" login " added on " date ":</i><br>\n"
@            cmappnd 6 concat /comment append_field
@         } samename if
@         {
@            "\n\n<hr><i>" login " claiming to be " username " added on " date
@            "</i><br>\n" cmappnd 8 concat /comment append_field
@         } samename not if
@       } 0 {} /cmappnd get length lt if
@       submit_ticket
@     } /submit exists if
@   ]
@   <table cellpadding="5">
@   <tr><td align="right">Title:</td><td>
@   <input type="text" name="title" value="[title html]" size="60">
@   </td></tr>
@   <tr><td align="right">Status:</td><td>
@   [/status status_choices 1 combobox]
@   </td></tr>
@   <tr><td align="right">Type:</td><td>
@   [/type type_choices 1 combobox]
@   </td></tr>
@   <tr><td align="right">Severity:</td><td>
@   [/severity severity_choices 1 combobox]
@   </td></tr>
@   <tr><td align="right">Priority:</td><td>
@   [/priority priority_choices 1 combobox]
@   </td></tr>
@   <tr><td align="right">Resolution:</td><td>
@   [/resolution resolution_choices 1 combobox]
@   </td></tr></