Fossil with Commonmark

Changes On Branch StvPrivateHook2
Login

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

Changes In Branch StvPrivateHook2 Excluding Merge-Ins

This is equivalent to a diff from f88368742d to e6dce6a16a

2010-10-29
21:11
merge from trunk and add sqlite shell to windows make check-in: 6d334ac9ed user: wolfgang tags: StvPrivateHook2
2010-10-28
17:41
merge from trunk Leaf check-in: e6dce6a16a user: wolfgang tags: StvPrivateHook2
14:41
Fix a few harmless compiler warnings. check-in: d03718ad5f user: drh tags: trunk
2010-10-27
20:39
added missing dependency in dmc windows make for resource file check-in: 304c71a09c user: wolfgang tags: StvPrivateHook2
2010-10-17
16:37
merge from old hook branch check-in: 9cf288de27 user: wolfgang tags: StvPrivateHook2
2010-10-16
19:07
PellesC doesn't have pgmptr, update Makefile Leaf check-in: f88368742d user: wolfgang tags: wolfgangHelpCmd
17:33
merge from trunk check-in: 586b0eb144 user: wolfgang tags: wolfgangHelpCmd

Changes to Makefile.

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58













59
39
40
41
42
43
44
45



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







-
-
-










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

#### 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 $(LDFLAGS)
HOST_OS!= uname -s
LIB.SunOS= -lsocket -lnsl
LIB += $(LIB.$(HOST_OS))

# If using HTTPS:
LIB += -lcrypto -lssl

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

# You should not need to change anything below this line
###############################################################################
#
# Automatic platform-specific options.
HOST_OS!= uname -s

LIB.SunOS= -lsocket -lnsl
LIB += $(LIB.$(HOST_OS))

TCC.DragonFly += -DUSE_PREAD
TCC.FreeBSD += -DUSE_PREAD
TCC.NetBSD += -DUSE_PREAD
TCC.OpenBSD += -DUSE_PREAD
TCC += $(TCC.$(HOST_OS))

include $(SRCDIR)/main.mk

Changes to src/branch.c.

33
34
35
36
37
38
39
40
41

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


40
41
42
43
44
45
46
47







-
-
+







  char *zUuid;           /* Artifact ID of origin */
  Stmt q;                /* Generic query */
  const char *zBranch;   /* Name of the new branch */
  char *zDate;           /* Date that branch was created */
  char *zComment;        /* Check-in comment for the new branch */
  const char *zColor;    /* Color of the new branch */
  Blob branch;           /* manifest for the new branch */
  Blob parent;           /* root check-in manifest */
  Manifest mParent;      /* Parsed parent manifest */
  Manifest *pParent;     /* Parsed parent manifest */
  Blob mcksum;           /* Self-checksum on the manifest */
  const char *zDateOvrd; /* Override date string */
  const char *zUserOvrd; /* Override user name */
 
  noSign = find_option("nosign","",0)!=0;
  zColor = find_option("bgcolor","c",1);
  zDateOvrd = find_option("date-override",0,1);
69
70
71
72
73
74
75





76
77
78



79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96




97
98

99
100
101
102




103
104
105
106


107
108
109
110
111
112
113
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92






93





94
95
96
97


98




99
100
101
102
103
104


105
106
107
108
109
110
111
112
113







+
+
+
+
+



+
+
+







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


-
-
+
+








  user_select();
  db_begin_transaction();
  rootid = name_to_rid(g.argv[4]);
  if( rootid==0 ){
    fossil_fatal("unable to locate check-in off of which to branch");
  }

  pParent = manifest_get(rootid, CFTYPE_MANIFEST);
  if( pParent==0 ){
    fossil_fatal("%s is not a valid check-in", g.argv[4]);
  }

  /* Create a manifest for the new branch */
  blob_zero(&branch);
  if( pParent->zBaseline ){
    blob_appendf(&branch, "B %s\n", pParent->zBaseline);
  }
  zComment = mprintf("Create new branch named \"%h\"", zBranch);
  blob_appendf(&branch, "C %F\n", zComment);
  zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
  zDate[10] = 'T';
  blob_appendf(&branch, "D %s\n", zDate);

  /* Copy all of the content from the parent into the branch */
  content_get(rootid, &parent);
  manifest_parse(&mParent, &parent);
  if( mParent.type!=CFTYPE_MANIFEST ){
    fossil_fatal("%s is not a valid check-in", g.argv[4]);
  }
  for(i=0; i<mParent.nFile; ++i){
  for(i=0; i<pParent->nFile; ++i){
    if( mParent.aFile[i].zPerm[0] ){
      blob_appendf(&branch, "F %F %s %s\n",
                   mParent.aFile[i].zName,
                   mParent.aFile[i].zUuid,
                   mParent.aFile[i].zPerm);
    blob_appendf(&branch, "F %F", pParent->aFile[i].zName);
    if( pParent->aFile[i].zUuid ){
      blob_appendf(&branch, " %s", pParent->aFile[i].zUuid);
      if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){
    }else{
      blob_appendf(&branch, "F %F %s\n",
        blob_appendf(&branch, " %s", pParent->aFile[i].zPerm);
                   mParent.aFile[i].zName,
                   mParent.aFile[i].zUuid);
    }
  }
      }
    }
    blob_append(&branch, "\n", 1);
  }
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
  blob_appendf(&branch, "P %s\n", zUuid);
  blob_appendf(&branch, "R %s\n", mParent.zRepoCksum);
  manifest_clear(&mParent);
  blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
  manifest_destroy(pParent);

  /* Add the symbolic branch name and the "branch" tag to identify
  ** this as a new branch */
  if( zColor!=0 ){
    blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
  }
  blob_appendf(&branch, "T *branch * %F\n", zBranch);

Changes to src/browse.c.

69
70
71
72
73
74
75
76

77
78
79
80
81
82




83
84



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

104
105
106
107
108
109
110
111
112
113
114

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



134
135
136

137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166



167
168
169
170
171

172
173



174
175

176


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



193
194
195
196
197













198
199



200
201
202
203
204
205
206
207

208
209
210
211
212




213
214
215
216
217


218
219
220
221
222
223
224
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84
85
86


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

118

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135



136
137
138

139

140
141
142
143
144
145
146
147
148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180


181
182
183
184
185
186

187
188
189
190
191
192
193
194
195
196
197
198
199
200

201
202
203
204
205
206





207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226






227
228




229
230
231
232
233
234
235


236
237
238
239
240
241
242
243
244







-
+






+
+
+
+
-
-
+
+
+



















+








-

-
+
















-
-
-
+
+
+
-

-
+








-
+



















+


+
+
+





+
-
-
+
+
+


+
-
+
+












-



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


+
+
+


-
-
-
-
-
-
+

-
-
-
-
+
+
+
+



-
-
+
+







** to the "dir" page for the directory.
**
** There is no hyperlink on the file element of the path.
**
** The computed string is appended to the pOut blob.  pOut should
** have already been initialized.
*/
void hyperlinked_path(const char *zPath, Blob *pOut){
void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
  int i, j;
  char *zSep = "";

  for(i=0; zPath[i]; i=j){
    for(j=i; zPath[j] && zPath[j]!='/'; j++){}
    if( zPath[j] && g.okHistory ){
      if( zCI ){
        blob_appendf(pOut, "%s<a href=\"%s/dir?ci=%S&amp;name=%#T\">%#h</a>", 
                     zSep, g.zBaseURL, zCI, j, zPath, j-i, &zPath[i]);
      }else{
      blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", 
                   zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
        blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", 
                     zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
      }
    }else{
      blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
    }
    zSep = "/";
    while( zPath[j]=='/' ){ j++; }
  }
}


/*
** WEBPAGE: dir
**
** Query parameters:
**
**    name=PATH        Directory to display.  Required.
**    ci=LABEL         Show only files in this check-in.  Optional.
*/
void page_dir(void){
  const char *zD = P("name");
  int nD = zD ? strlen(zD)+1 : 0;
  int mxLen;
  int nCol, nRow;
  int cnt, i;
  char *zPrefix;
  Stmt q;
  const char *zCI = P("ci");
  int rid = 0;
  char *zUuid = 0;
  Blob content;
  Blob dirname;
  Manifest m;
  Manifest *pM = 0;
  const char *zSubdirLink;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("File List");
  sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
                          pathelementFunc, 0, 0);

  /* If the name= parameter is an empty string, make it a NULL pointer */
  if( zD && strlen(zD)==0 ){ zD = 0; }

  /* If a specific check-in is requested, fetch and parse it.  If the
  ** specific check-in does not exist, clear zCI.  zCI==0 will cause all
  ** files from all check-ins to be displayed.
  */
  if( zCI ){
    if( (rid = name_to_rid(zCI))==0 || content_get(rid, &content)==0 ){
      zCI = 0;  /* No artifact named zCI exists */
    }else if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){
    pM = manifest_get_by_name(zCI, &rid);
    if( pM ){
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
      zCI = 0;  /* The artifact exists but is not a manifest */
    }else{
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
      zCI = 0;
    }
  }


  /* Compute the title of the page */  
  blob_zero(&dirname);
  if( zD ){
    blob_append(&dirname, "in directory ", -1);
    hyperlinked_path(zD, &dirname);
    hyperlinked_path(zD, &dirname, zCI);
    zPrefix = mprintf("%h/", zD);
  }else{
    blob_append(&dirname, "in the top-level directory", -1);
    zPrefix = "";
  }
  if( zCI ){
    char zShort[20];
    memcpy(zShort, zUuid, 10);
    zShort[10] = 0;
    @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%s/dir?ci=%S&amp;name=%T", g.zTop, zUuid, zPrefix);
    if( zD ){
      style_submenu_element("Top", "Top", "%s/dir?ci=%S", g.zTop, zUuid);
      style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
    }else{
      style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
    }
  }else{
    int hasTrunk;
    @ <h2>The union of all files from all check-ins
    @ %s(blob_str(&dirname))</h2>
    hasTrunk = db_exists(
                  "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'",
                  TAG_BRANCH);
    zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
    if( zD ){
      style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
      style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
                            g.zBaseURL, zD);
      if( hasTrunk ){
      style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
                             g.zBaseURL,zD);
        style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
                               g.zBaseURL,zD);
      }
    }else{
      style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
      if( hasTrunk ){
      style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
        style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
      }
    }
  }

  /* Compute the temporary table "localfiles" containing the names
  ** of all files and subdirectories in the zD[] directory.  
  **
  ** Subdirectory names begin with "/".  This causes them to sort
  ** first and it also gives us an easy way to distinguish files
  ** from directories in the loop that follows.
  */
  db_multi_exec(
     "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
     "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);"
  );
  if( zCI ){
    Stmt ins;
    ManifestFile *pFile;
    ManifestFile *pPrev = 0;
    int nPrev = 0;
    int i;
    db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)");
    for(i=0; i<m.nFile; i++){
      db_bind_text(&ins, ":x", m.aFile[i].zName);
      db_bind_text(&ins, ":u", m.aFile[i].zUuid);
    int c;

    db_prepare(&ins,
       "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)"
    );
    manifest_file_rewind(pM);
    while( (pFile = manifest_file_next(pM,0))!=0 ){
      if( nD>0 && memcmp(pFile->zName, zD, nD-1)!=0 ) continue;
      if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 ){
        continue;
      }
      db_bind_text(&ins, ":x", &pFile->zName[nD]);
      db_bind_text(&ins, ":u", pFile->zUuid);
      db_step(&ins);
      db_reset(&ins);
      pPrev = pFile;
      for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
      if( c=='/' ) nPrev++;
    }
    db_finalize(&ins);
  }else{
    db_multi_exec(
      "INSERT INTO allfiles SELECT name, NULL FROM filename"
    );
  }
  if( zD ){
  }else if( zD ){
    db_multi_exec(
       "INSERT OR IGNORE INTO localfiles "
       "  SELECT pathelement(x,%d), u FROM allfiles"
       "   WHERE x GLOB '%q/*'",
       strlen(zD)+1, zD
      "INSERT OR IGNORE INTO localfiles"
      " SELECT pathelement(name,%d), NULL FROM filename"
      "  WHERE name GLOB '%q/*'",
      nD, zD
    );
  }else{
    db_multi_exec(
       "INSERT OR IGNORE INTO localfiles "
       "  SELECT pathelement(x,0), u FROM allfiles"
      "INSERT OR IGNORE INTO localfiles"
      " SELECT pathelement(name,0), NULL FROM filename"
    );
  }

  /* Generate a multi-column table listing the contents of zD[]
  ** directory.
  */
  mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
244
245
246
247
248
249
250

251
252
253
264
265
266
267
268
269
270
271
272
273
274







+



      @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a></li>
    }else{
      @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
      @     </a></li>
    }
  }
  db_finalize(&q);
  manifest_destroy(pM);
  @ </ul></td></tr></table>
  style_footer_cmdref("ls",0);
}

Changes to src/checkin.c.

262
263
264
265
266
267
268

269
270
271
272
273
274
275
276
277
278
279
280

281
282
283
284
285


286
287
288
289
290
291
292
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288
289
290
291
292
293
294
295







+











-
+





+
+







void extra_cmd(void){
  Blob path;
  Blob repo;
  Stmt q;
  int n;
  const char *zIgnoreFlag = find_option("ignore",0,1);
  int allFlag = find_option("dotfiles",0,0)!=0;
  int outputManifest = db_get_boolean("manifest",0);

  db_must_be_within_tree();
  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
  n = strlen(g.zLocalRoot);
  blob_init(&path, g.zLocalRoot, n-1);
  if( zIgnoreFlag==0 ){
    zIgnoreFlag = db_get("ignore-glob", 0);
  }
  vfile_scan(0, &path, blob_size(&path), allFlag);
  db_prepare(&q, 
      "SELECT x FROM sfile"
      " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_',"
      " WHERE x NOT IN ('%s','%s','_FOSSIL_',"
                       "'_FOSSIL_-journal','.fos','.fos-journal',"
                       "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal',"
                       "'.fos-shm')"
      "   AND NOT %s"
      " ORDER BY 1",
      outputManifest ? "manifest" : "_FOSSIL_",
      outputManifest ? "manifest.uuid" : "_FOSSIL_",
      glob_expr("x", zIgnoreFlag)
  );
  if( file_tree_name(g.zRepositoryName, &repo, 0) ){
    db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
  }
  while( db_step(&q)==SQLITE_ROW ){
    printf("%s\n", db_column_text(&q, 0));
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
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







-
-
-
+
-
-
-

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







  assert( strlen(zDate)==19 );
  assert( zDate[10]==' ' );
  zDate[10] = 'T';
  return zDate;
}

/*
** Return TRUE (non-zero) if a file named "zFilename" exists in
** the checkout identified by vid.
**
** Create a manifest.
** The original purpose of this routine was to check for the presence of
** a "checked-in" file named "manifest" or "manifest.uuid" so as to avoid
** overwriting that file with automatically generated files.
*/
static void create_manifest(
  Blob *pOut,                 /* Write the manifest here */
  const char *zBaselineUuid,  /* UUID of baseline, or zero */
  Manifest *pBaseline,        /* Make it a delta manifest if not zero */
  Blob *pComment,             /* Check-in comment text */
  int vid,                    /* blob-id of the parent manifest */
  int verifyDate,             /* Verify that child is younger */
  Blob *pCksum,               /* Repository checksum.  May be 0 */
  const char *zDateOvrd,      /* Date override.  If 0 then use 'now' */
  const char *zUserOvrd,      /* User override.  If 0 then use g.zLogin */
int file_exists_in_checkout(int vid, const char *zFilename){
  return db_exists("SELECT 1 FROM vfile WHERE vid=%d AND pathname=%Q",
                   vid, zFilename);
}
  const char *zBranch,        /* Branch name.  May be 0 */
  const char *zBgColor,       /* Background color.  May be 0 */
  int *pnFBcard               /* Number of generated B- and F-cards */
){
  char *zDate;                /* Date of the check-in */
  char *zParentUuid;          /* UUID of parent check-in */
  Blob filename;              /* A single filename */
  int nBasename;              /* Size of base filename */
  Stmt q;                     /* Query of files changed */
  Stmt q2;                    /* Query of merge parents */
  Blob mcksum;                /* Manifest checksum */
  ManifestFile *pFile;        /* File from the baseline */
  int nFBcard = 0;            /* Number of B-cards and F-cards */

  assert( pBaseline==0 || pBaseline->zBaseline==0 );
  assert( pBaseline==0 || zBaselineUuid!=0 );
  blob_zero(pOut);
  zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
  if( pBaseline ){
    blob_appendf(pOut, "B %s\n", zBaselineUuid);
    manifest_file_rewind(pBaseline);
    pFile = manifest_file_next(pBaseline, 0);
    nFBcard++;
  }else{
    pFile = 0;
  }
  blob_appendf(pOut, "C %F\n", blob_str(pComment));
  zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
  blob_appendf(pOut, "D %s\n", zDate);
  zDate[10] = ' ';
  db_prepare(&q,
    "SELECT pathname, uuid, origname, blob.rid, isexe"
    "  FROM vfile JOIN blob ON vfile.mrid=blob.rid"
    " WHERE NOT deleted AND vfile.vid=%d"
    " ORDER BY 1", vid);
  blob_zero(&filename);
  blob_appendf(&filename, "%s", g.zLocalRoot);
  nBasename = blob_size(&filename);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    const char *zOrig = db_column_text(&q, 2);
    int frid = db_column_int(&q, 3);
    int isexe = db_column_int(&q, 4);
    const char *zPerm;
    int cmp;
    blob_append(&filename, zName, -1);
#if !defined(_WIN32)
    /* For unix, extract the "executable" permission bit directly from
    ** the filesystem.  On windows, the "executable" bit is retained
    ** unchanged from the original. */
    isexe = file_isexe(blob_str(&filename));
#endif
    if( isexe ){
      zPerm = " x";
    }else{
      zPerm = "";
    }
    if( !g.markPrivate ) content_make_public(frid);
    while( pFile && strcmp(pFile->zName,zName)<0 ){
      blob_appendf(pOut, "F %F\n", pFile->zName);
      pFile = manifest_file_next(pBaseline, 0);
      nFBcard++;
    }
    cmp = 1;
    if( pFile==0
      || (cmp = strcmp(pFile->zName,zName))!=0
      || strcmp(pFile->zUuid, zUuid)!=0
    ){
      blob_resize(&filename, nBasename);
      if( zOrig==0 || strcmp(zOrig,zName)==0 ){
        blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm);
      }else{
        if( zPerm[0]==0 ){ zPerm = " w"; }
        blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
      }
      nFBcard++;
    }
    if( cmp==0 ) pFile = manifest_file_next(pBaseline,0);
  }
  blob_reset(&filename);
  db_finalize(&q);
  while( pFile ){
    blob_appendf(pOut, "F %F\n", pFile->zName);
    pFile = manifest_file_next(pBaseline, 0);
    nFBcard++;
  }
  blob_appendf(pOut, "P %s", zParentUuid);
  if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate);
  free(zParentUuid);
  db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
  db_bind_int(&q2, ":id", 0);
  while( db_step(&q2)==SQLITE_ROW ){
    char *zMergeUuid;
    int mid = db_column_int(&q2, 0);
    if( !g.markPrivate && content_is_private(mid) ) continue;
    zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
    if( zMergeUuid ){
      blob_appendf(pOut, " %s", zMergeUuid);
      if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
      free(zMergeUuid);
    }
  }
  db_finalize(&q2);
  free(zDate);

  blob_appendf(pOut, "\n");
  if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum);
  if( zBranch && zBranch[0] ){
    Stmt q;
    if( zBgColor && zBgColor[0] ){
      blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor);
    }
    blob_appendf(pOut, "T *branch * %F\n", zBranch);
    blob_appendf(pOut, "T *sym-%F *\n", zBranch);

    /* Cancel all other symbolic tags */
    db_prepare(&q,
        "SELECT tagname FROM tagxref, tag"
        " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
        "   AND tagtype>0 AND tagname GLOB 'sym-*'"
        "   AND tagname!='sym-'||%Q"
        " ORDER BY tagname",
        vid, zBranch);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zTag = db_column_text(&q, 0);
      blob_appendf(pOut, "T -%F *\n", zTag);
    }
    db_finalize(&q);
  }  
  blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
  md5sum_blob(pOut, &mcksum);
  blob_appendf(pOut, "Z %b\n", &mcksum);
  if( pnFBcard ) *pnFBcard = nFBcard;
}


/*
** COMMAND: ci
** COMMAND: commit
**
** Usage: %fossil commit ?OPTIONS? ?FILE...?
**
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
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







+
+



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



+
+

+
+
-
+





-
-
+

-


+
+



+
+
+
+
+
+















+
+

+
+
+
+
+
+
+
+
+
+







**
**    --comment|-m COMMENT-TEXT
**    --branch NEW-BRANCH-NAME
**    --bgcolor COLOR
**    --nosign
**    --force|-f
**    --private
**    --baseline
**    --delta
**    
*/
void commit_cmd(void){
  int rc;
  int vid, nrid, nvid;
  Blob comment;
  const char *zComment;
  Stmt q;
  int hasChanges;        /* True if unsaved changes exist */
  int vid;               /* blob-id of parent version */
  int nrid;              /* blob-id of a modified file */
  int nvid;              /* Blob-id of the new check-in */
  Blob comment;          /* Check-in comment */
  const char *zComment;  /* Check-in comment */
  Stmt q;                /* Query to find files that have been modified */
  Stmt q2;
  char *zUuid, *zDate;
  char *zUuid;           /* UUID of the new check-in */
  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 */
  int forceDelta = 0;    /* Force a delta-manifest */
  int forceBaseline = 0; /* Force a baseline-manifest */
  char *zManifestFile;   /* Name of the manifest file */
  int useCksum;          /* True if checksums should be computed and verified */
  int outputManifest;    /* True to output "manifest" and "manifest.uuid" */
  int nBasename;         /* Length of "g.zLocalRoot/" */
  int testRun;           /* True for a test run.  Debugging only */
  const char *zBranch;   /* Create a new branch with this name */
  const char *zBgColor;  /* Set background color when branching */
  const char *zDateOvrd; /* Override date string */
  const char *zUserOvrd; /* Override user name */
  const char *zComFile;  /* Read commit message from this file */
  Blob filename;         /* complete filename */
  Blob manifest;
  Blob manifest;         /* Manifest in baseline form */
  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 */
  int szD;               /* Size of the delta manifest */
  int szB;               /* Size of the baseline manifest */
 
  url_proxy_options();
  noSign = find_option("nosign",0,0)!=0;
  forceDelta = find_option("delta",0,0)!=0;
  forceBaseline = find_option("baseline",0,0)!=0;
  if( forceDelta && forceBaseline ){
    fossil_fatal("cannot use --delta and --baseline together");
  }
  testRun = find_option("test",0,0)!=0;
  zComment = find_option("comment","m",1);
  forceFlag = find_option("force", "f", 0)!=0;
  zBranch = find_option("branch","b",1);
  zBgColor = find_option("bgcolor",0,1);
  zComFile = find_option("message-file", "M", 1);
  if( find_option("private",0,0) ){
    g.markPrivate = 1;
    if( zBranch==0 ) zBranch = "private";
    if( zBgColor==0 ) zBgColor = "#fec084";  /* Orange */
  }
  zDateOvrd = find_option("date-override",0,1);
  zUserOvrd = find_option("user-override",0,1);
  db_must_be_within_tree();
  noSign = db_get_boolean("omitsign", 0)|noSign;
  if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
  useCksum = db_get_boolean("repo-cksum", 1);
  outputManifest = db_get_boolean("manifest", 0);
  verify_all_options();

  /* So that older versions of Fossil (that do not understand delta-
  ** manifest) can continue to use this repository, do not create a new
  ** delta-manifest unless this repository already contains one or more
  ** delta-manifets, or unless the delta-manifest is explicitly requested
  ** by the --delta option.
  */
  if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
    forceBaseline = 1;
  }

  /* Get the ID of the parent manifest artifact */
  vid = db_lget_int("checkout", 0);
  if( content_is_private(vid) ){
    g.markPrivate = 1;
  }

683
684
685
686
687
688
689
690

691
692
693

694
695
696
697
698
699
700
848
849
850
851
852
853
854

855
856
857

858
859
860
861
862
863
864
865







-
+


-
+







  /*
  ** Check that the user exists.
  */
  if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
    fossil_fatal("no such user: %s", g.zLogin);
  }
  
  rc = unsaved_changes();
  hasChanges = unsaved_changes();
  db_begin_transaction();
  db_record_repository_filename(0);
  if( rc==0 && !isAMerge && !forceFlag ){
  if( hasChanges==0 && !isAMerge && !forceFlag ){
    fossil_fatal("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 ){
722
723
724
725
726
727
728
729

730
731
732
733
734
735
736
887
888
889
890
891
892
893

894
895
896
897
898
899
900
901







-
+







  */
  if( db_exists("SELECT 1 FROM tagxref"
                " WHERE tagid=%d AND rid=%d AND tagtype>0",
                TAG_CLOSED, vid) ){
    fossil_fatal("cannot commit against a closed leaf");
  }

  vfile_aggregate_checksum_disk(vid, &cksum1);
  if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1);
  if( zComment ){
    blob_zero(&comment);
    blob_append(&comment, zComment, -1);
  }else if( zComFile ){
    blob_zero(&comment);
    blob_read_from_file(&comment, zComFile);
  }else{
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
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







-
+
-



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

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

-
-
+
-
-
-








-
+
+
+
+
+
+
+
+
+















-
+




















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

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

-
-
-
-
+
+
+
+
+







+
+
+
+









      content_deltify(rid, nrid, 0);
    }
    db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
  }
  db_finalize(&q);

  /* Create the manifest */
  /* Create the new manifest */
  blob_zero(&manifest);
  if( blob_size(&comment)==0 ){
    blob_append(&comment, "(no comment)", -1);
  }
  blob_appendf(&manifest, "C %F\n", blob_str(&comment));
  if( forceDelta ){
  zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
  blob_appendf(&manifest, "D %s\n", zDate);
  zDate[10] = ' ';
  db_prepare(&q,
    "SELECT pathname, uuid, origname, blob.rid, isexe"
    "  FROM vfile JOIN blob ON vfile.mrid=blob.rid"
    " WHERE NOT deleted AND vfile.vid=%d"
    " ORDER BY 1", vid);
  blob_zero(&filename);
    blob_zero(&manifest);
  blob_appendf(&filename, "%s", g.zLocalRoot);
  nBasename = blob_size(&filename);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    const char *zOrig = db_column_text(&q, 2);
    int frid = db_column_int(&q, 3);
    int isexe = db_column_int(&q, 4);
    const char *zPerm;
    blob_append(&filename, zName, -1);
#if !defined(_WIN32)
    /* For unix, extract the "executable" permission bit directly from
    ** the filesystem.  On windows, the "executable" bit is retained
    ** unchanged from the original. */
    isexe = file_isexe(blob_str(&filename));
#endif
    if( isexe ){
      zPerm = " x";
    }else{
  }else{
      zPerm = "";
    }
    blob_resize(&filename, nBasename);
    if( zOrig==0 || strcmp(zOrig,zName)==0 ){
      blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm);
    create_manifest(&manifest, 0, 0, &comment, vid,
                    !forceFlag, useCksum ? &cksum1 : 0,
                    zDateOvrd, zUserOvrd, zBranch, zBgColor, &szB);
  }

  /* See if a delta-manifest would be more appropriate */
  if( !forceBaseline ){
    const char *zBaselineUuid;
    Manifest *pParent;
    Manifest *pBaseline;
    pParent = manifest_get(vid, CFTYPE_MANIFEST);
    if( pParent && pParent->zBaseline ){
      zBaselineUuid = pParent->zBaseline;
      pBaseline = manifest_get_by_name(zBaselineUuid, 0);
    }else{
      if( zPerm[0]==0 ){ zPerm = " w"; }
      blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
    }
    if( !g.markPrivate ) content_make_public(frid);
  }
  blob_reset(&filename);
  db_finalize(&q);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
      zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
  blob_appendf(&manifest, "P %s", zUuid);

  if( !forceFlag ){
    checkin_verify_younger(vid, zUuid, zDate);
      pBaseline = pParent;
    }
    if( pBaseline ){
      Blob delta;
      create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
    db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
    db_bind_int(&q2, ":id", 0);
                      !forceFlag, useCksum ? &cksum1 : 0,
    while( db_step(&q2)==SQLITE_ROW ){
      int mid = db_column_int(&q2, 0);
      if( !g.markPrivate && content_is_private(mid) ) continue;
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
      if( zUuid ){
        blob_appendf(&manifest, " %s", zUuid);
        checkin_verify_younger(mid, zUuid, zDate);
                      zDateOvrd, zUserOvrd, zBranch, zBgColor, &szD);
      /*
      ** At this point, two manifests have been constructed, either of
      ** which would work for this checkin.  The first manifest (held
        free(zUuid);
      }
    }
    db_finalize(&q2);
  }

  blob_appendf(&manifest, "\n");
  blob_appendf(&manifest, "R %b\n", &cksum1);
      ** in the "manifest" variable) is a baseline manifest and the second
      ** (held in variable named "delta") is a delta manifest.  The
      ** question now is: which manifest should we use?
      **
      ** Let B be the number of F-cards in the baseline manifest and
      ** let D be the number of F-cards in the delta manifest, plus one for
  if( zBranch && zBranch[0] ){
    Stmt q;
    if( zBgColor && zBgColor[0] ){
      blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor);
    }
    blob_appendf(&manifest, "T *branch * %F\n", zBranch);
      ** the B-card.  (B is held in the szB variable and D is held in the
      ** szD variable.)  Assume that all delta manifests adds X new F-cards.
      ** Then to minimize the total number of F- and B-cards in the repository,
      ** we should use the delta manifest if and only if:
      **
      **      D*D < B*X - X*X
      **
      ** X is an unknown here, but for most repositories, we will not be
      ** far wrong if we assume X=3.
      */
      if( forceDelta || (szD*szD)<(szB*3-9) ){
        blob_reset(&manifest);
        manifest = delta;
      }else{
        blob_reset(&delta);
    blob_appendf(&manifest, "T *sym-%F *\n", zBranch);

      }
    /* Cancel all other symbolic tags */
    db_prepare(&q,
        "SELECT tagname FROM tagxref, tag"
        " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
        "   AND tagtype>0 AND tagname GLOB 'sym-*'"
        "   AND tagname!='sym-'||%Q"
        " ORDER BY tagname",
    }else if( forceDelta ){
        vid, zBranch);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zTag = db_column_text(&q, 0);
      blob_appendf(&manifest, "T -%F *\n", zTag);
      fossil_panic("unable to find a baseline-manifest for the delta");
    }
    db_finalize(&q);
  }  
  }
  blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
  md5sum_blob(&manifest, &mcksum);
  blob_appendf(&manifest, "Z %b\n", &mcksum);
  if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
    Blob ans;
    blob_zero(&ans);
    prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
    if( blob_str(&ans)[0]!='y' ){
      fossil_exit(1);
    }
  }
  if( !file_exists_in_checkout(vid, "manifest") ){

  /* If the --test option is specified, output the manifest file
  ** and rollback the transaction.  
  */
  if( testRun ){
    blob_write_to_file(&manifest, "");
  }

  if( outputManifest ){
    zManifestFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManifestFile);
    blob_reset(&manifest);
    blob_read_from_file(&manifest, zManifestFile);
    free(zManifestFile);
  }
  nvid = content_put(&manifest, 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("New_Version: %s\n", zUuid);
  if( !file_exists_in_checkout(vid, "manifest.uuid") ){
  if( outputManifest ){
    zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
    blob_zero(&muuid);
    blob_appendf(&muuid, "%s\n", zUuid);
    blob_write_to_file(&muuid, zManifestFile);
    free(zManifestFile);
    blob_reset(&muuid);
  }

  
  /* Update the vfile and vmerge tables */
  db_multi_exec(
    "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND file_is_selected(id);"
    "DELETE FROM vmerge WHERE file_is_selected(id) OR id=0;"
    "UPDATE vfile SET vid=%d;"
    "UPDATE vfile SET rid=mrid, chnged=0, deleted=0, origname=NULL"
    " WHERE file_is_selected(id);"
    , vid, nvid
  );
  db_lset_int("checkout", nvid);

  if( useCksum ){
  /* Verify that the repository checksum matches the expected checksum
  ** calculated before the checkin started (and stored as the R record
  ** of the manifest file).
  */
  vfile_aggregate_checksum_repository(nvid, &cksum2);
  if( blob_compare(&cksum1, &cksum2) ){
    fossil_panic("tree checksum does not match repository after commit");
  }
    /* Verify that the repository checksum matches the expected checksum
    ** calculated before the checkin started (and stored as the R record
    ** of the manifest file).
    */
    vfile_aggregate_checksum_repository(nvid, &cksum2);
    if( blob_compare(&cksum1, &cksum2) ){
      fossil_panic("tree checksum does not match repository after commit");
    }

  /* Verify that the manifest checksum matches the expected checksum */
  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);
  }
  if( blob_compare(&cksum1, &cksum2) ){
    fossil_panic("tree checksum does not match manifest after commit: "
                 "%b versus %b", &cksum1, &cksum2);
  }
    /* Verify that the manifest checksum matches the expected checksum */
    vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
    if( blob_compare(&cksum1, &cksum1b) ){
      fossil_panic("manifest checksum does not agree with manifest: "
                   "%b versus %b", &cksum1, &cksum1b);
    }
    if( blob_compare(&cksum1, &cksum2) ){
      fossil_panic("tree checksum does not match manifest after commit: "
                   "%b versus %b", &cksum1, &cksum2);
    }

  /* Verify that the commit did not modify any disk images. */
  vfile_aggregate_checksum_disk(nvid, &cksum2);
  if( blob_compare(&cksum1, &cksum2) ){
    fossil_panic("tree checksums before and after commit do not match");
    /* Verify that the commit did not modify any disk images. */
    vfile_aggregate_checksum_disk(nvid, &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_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
  if( testRun ){
    db_end_transaction(1);
    exit(1);
  }
  db_end_transaction(0);

  if( !g.markPrivate ){
    autosync(AUTOSYNC_PUSH);  
  }
  if( count_nonbranch_children(vid)>1 ){
    printf("**** warning: a fork has occurred *****\n");
  }
}

Changes to src/checkout.c.

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

90
91
92
93
94
95
96


97
98








99
100
101
102
103
104
105
106
107
108
































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



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





159








160
161
162
163
164
165
166
76
77
78
79
80
81
82


83
84
85


86

87
88
89
90
91
92
93
94


95
96
97
98
99
100
101
102
103
104
105
106
107





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






145

























146
147
148

149
150
151


152
153
154
155
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178







-
-



-
-
+
-






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





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





-
-
-
-
-
-

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



-
-







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







  return vid;
}

/*
** Load a vfile from a record ID.
*/
void load_vfile_from_rid(int vid){
  Blob manifest;

  if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
    return;
  }
  content_get(vid, &manifest);
  vfile_build(vid, &manifest);
  vfile_build(vid);
  blob_reset(&manifest);
}

/*
** Set or clear the vfile.isexe flag for a file.
*/
static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
  static Stmt s;
  db_static_prepare(&s,
  db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
                onoff, vid, zFilename);
    "UPDATE vfile SET isexe=:isexe"
    " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
  );
  db_bind_int(&s, ":isexe", onoff);
  db_bind_int(&s, ":vid", vid);
  db_bind_text(&s, ":path", zFilename);
  db_step(&s);
  db_reset(&s);
}

/*
** Set or clear the execute permission bit (as appropriate) for all
** files in the current check-out.
**
** If the checkout does not have explicit files named "manifest" and
** "manifest.uuid" then automatically generate files with those names
** containing, respectively, the text of the manifest and the artifact
** ID of the manifest.
*/
void checkout_set_all_exe(int vid){
  Blob filename;
  int baseLen;
  Manifest *pManifest;
  ManifestFile *pFile;

  /* Check the EXE permission status of all files
  */
  pManifest = manifest_get(vid, CFTYPE_MANIFEST);
  if( pManifest==0 ) return;
  blob_zero(&filename);
  blob_appendf(&filename, "%s/", g.zLocalRoot);
  baseLen = blob_size(&filename);
  manifest_file_rewind(pManifest);
  while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
    int isExe;
    blob_append(&filename, pFile->zName, -1);
    isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
    file_setexe(blob_str(&filename), isExe);
    set_or_clear_isexe(pFile->zName, vid, isExe);
    blob_resize(&filename, baseLen);
  }
  blob_reset(&filename);
  manifest_destroy(pManifest);
}


/*
** If the "manifest" setting is true, then automatically generate
** files named "manifest" and "manifest.uuid" containing, respectively,
** the text of the manifest and the artifact ID of the manifest.
*/
void manifest_to_disk(int vid){
  char *zManFile;
  Blob manifest;
  Blob hash;
  Blob filename;
  int baseLen;
  int i;
  int seenManifest = 0;
  int seenManifestUuid = 0;
  Manifest m;

  /* Check the EXE permission status of all files
  */
  blob_zero(&manifest);
  content_get(vid, &manifest);
  manifest_parse(&m, &manifest);
  blob_zero(&filename);
  blob_appendf(&filename, "%s/", g.zLocalRoot);
  baseLen = blob_size(&filename);
  for(i=0; i<m.nFile; i++){ 
    int isExe;
    blob_append(&filename, m.aFile[i].zName, -1);
    isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
    file_setexe(blob_str(&filename), isExe);
    set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
    blob_resize(&filename, baseLen);
    if( memcmp(m.aFile[i].zName, "manifest", 8)==0 ){
      if( m.aFile[i].zName[8]==0 ) seenManifest = 1;
      if( strcmp(&m.aFile[i].zName[8], ".uuid")==0 ) seenManifestUuid = 1;
    }
  }
  blob_reset(&filename);
  manifest_clear(&m);

  blob_zero(&manifest);
  content_get(vid, &manifest);
  if( db_get_boolean("manifest",0) ){
    blob_zero(&manifest);
    content_get(vid, &manifest);
  if( !seenManifest ){
    zManFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManFile);
    free(zManFile);
  }
  if( !seenManifestUuid ){
    blob_zero(&hash);
    sha1sum_blob(&manifest, &hash);
    zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
    blob_append(&hash, "\n", 1);
    blob_write_to_file(&hash, zManFile);
    free(zManFile);
    blob_reset(&hash);
  }else{
    if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
      zManFile = mprintf("%smanifest", g.zLocalRoot);
      unlink(zManFile);
      free(zManFile);
  }
    }
    if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
      zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
      unlink(zManFile);
      free(zManFile);
    }
  }
    
}

/*
** COMMAND: checkout
** COMMAND: co
**
** Usage: %fossil checkout VERSION ?-f|--force? ?--keep?
226
227
228
229
230
231
232

233
234
235
236
237
238
239
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252







+







  if( !keepFlag ){
    uncheckout(prior);
  }
  db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
  if( !keepFlag ){
    vfile_to_disk(vid, 0, 1, promptFlag);
  }
  checkout_set_all_exe(vid);
  manifest_to_disk(vid);
  db_lset_int("checkout", vid);
  undo_reset();
  db_multi_exec("DELETE FROM vmerge");
  if( !keepFlag ){
    vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
    vfile_aggregate_checksum_disk(vid, &cksum2);

Changes to src/configure.c.

72
73
74
75
76
77
78

79
80
81
82
83
84
85
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86







+







  { "css",                    CONFIGSET_SKIN },
  { "header",                 CONFIGSET_SKIN },
  { "footer",                 CONFIGSET_SKIN },
  { "logo-mimetype",          CONFIGSET_SKIN },
  { "logo-image",             CONFIGSET_SKIN },
  { "project-name",           CONFIGSET_PROJ },
  { "project-description",    CONFIGSET_PROJ },
  { "manifest",               CONFIGSET_PROJ },
  { "index-page",             CONFIGSET_SKIN },
  { "timeline-block-markup",  CONFIGSET_SKIN },
  { "timeline-max-comment",   CONFIGSET_SKIN },
  { "ticket-table",           CONFIGSET_TKT  },
  { "ticket-common",          CONFIGSET_TKT  },
  { "ticket-newpage",         CONFIGSET_TKT  },
  { "ticket-viewpage",        CONFIGSET_TKT  },

Changes to src/content.c.

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







-
+

-
+




+
+
+
+





+
-
+

-







  }else{
    bag_insert(&contentCache.available, rid);
  }
  return rc;
}

/*
** COMMAND:  artifact
** COMMAND: artifact
**
** Usage: %fossil artifact ARTIFACT-ID  ?OUTPUT-FILENAME?
** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS?
**
** Extract an artifact by its SHA1 hash and write the results on
** standard output, or if the optional 4th argument is given, in
** the named output file.
**
** Options:
**
**    -R|--repository FILE       Extract artifacts from repository FILE
*/
void artifact_cmd(void){
  int rid;
  Blob content;
  const char *zFile;
  db_find_and_open_repository(1);
  if( g.argc!=4 && g.argc!=3 ) usage("RECORDID ?FILENAME?");
  if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?");
  zFile = g.argc==4 ? g.argv[3] : "-";
  db_must_be_within_tree();
  rid = name_to_rid(g.argv[2]);
  content_get(rid, &content);
  blob_write_to_file(&content, zFile);
}

/*
** COMMAND:  test-content-rawget
319
320
321
322
323
324
325









326
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
323
324
325
326
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







+
+
+
+
+
+
+
+
+




+
+
+
+
+







+

+



+
+

-




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













+
+
+





+
+
+
+
+
+
+







  rid = name_to_rid(g.argv[2]);
  blob_zero(&content);
  db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid);
  blob_uncompress(&content, &content);
  blob_write_to_file(&content, zFile);
}

/*
** The following flag is set to disable the automatic calls to
** manifest_crosslink() when a record is dephantomized.  This
** flag can be set (for example) when doing a clone when we know
** that rebuild will be run over all records at the conclusion
** of the operation.
*/
static int ignoreDephantomizations = 0;

/*
** When a record is converted from a phantom to a real record,
** if that record has other records that are derived by delta,
** then call manifest_crosslink() on those other records.
**
** If the formerly phantom record or any of the other records
** derived by delta from the former phantom are a baseline manifest,
** then also invoke manifest_crosslink() on the delta-manifests
** associated with that baseline.
**
** Tail recursion is used to minimize stack depth.
*/
void after_dephantomize(int rid, int linkFlag){
  Stmt q;
  int nChildAlloc = 0;
  int *aChild = 0;
  Blob content;

  if( ignoreDephantomizations ) return;
  while( rid ){
    int nChildUsed = 0;
    int i;

    /* Parse the object rid itself */
    if( linkFlag ){
      Blob content;
      content_get(rid, &content);
      manifest_crosslink(rid, &content);
      blob_reset(&content);
    }

    /* Parse all delta-manifests that depend on baseline-manifest rid */
    db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid);
    while( db_step(&q)==SQLITE_ROW ){
      int child = db_column_int(&q, 0);
      if( nChildUsed>=nChildAlloc ){
        nChildAlloc = nChildAlloc*2 + 10;
        aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild));
      }
      aChild[nChildUsed++] = child;
    }
    db_finalize(&q);
    for(i=0; i<nChildUsed; i++){
      content_get(aChild[i], &content);
      manifest_crosslink(aChild[i], &content);
      blob_reset(&content);
    }
    if( nChildUsed ){
      db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid);
    }

    /* Recursively dephantomize all artifacts that are derived by
    ** delta from artifact rid */
    nChildUsed = 0;
    db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
    while( db_step(&q)==SQLITE_ROW ){
      int child = db_column_int(&q, 0);
      if( nChildUsed>=nChildAlloc ){
        nChildAlloc = nChildAlloc*2 + 10;
        aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild));
      }
      aChild[nChildUsed++] = child;
    }
    db_finalize(&q);
    for(i=1; i<nChildUsed; i++){
      after_dephantomize(aChild[i], 1);
    }

    /* Tail recursion for the common case where only a single artifact
    ** is derived by delta from rid... */
    rid = nChildUsed>0 ? aChild[0] : 0;
    linkFlag = 1;
  }
  free(aChild);
}

/*
** Turn dephantomization processing on or off.
*/
void content_enable_dephantomize(int onoff){
  ignoreDephantomizations = !onoff;
}

/*
** Write content into the database.  Return the record ID.  If the
** content is already in the database, just return the record ID.
**
** If srcId is specified, then pBlob is delta content from
** the srcId record.  srcId might be a phantom.

Changes to src/db.c.

1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521







1522
1523
1524
1525
1526
1527



1528
1529










1530
1531
1532
1533
1534
1535
1536
1508
1509
1510
1511
1512
1513
1514







1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525


1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547







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




-
-
+
+
+


+
+
+
+
+
+
+
+
+
+







  char const *name;     /* Name of the setting */
  char const *var;      /* Internal variable name used by db_set() */
  int width;            /* Width of display.  0 for boolean values */
  char const *def;      /* Default value */
};
#endif /* INTERFACE */
struct stControlSettings const ctrlSettings[] = {
  { "auto-captcha",  "autocaptcha",    0, "0"                   },
  { "auto-shun",     0,                0, "1"                   },
  { "autosync",      0,                0, "0"                   },
  { "binary-glob",   0,                0, "1"                   },
  { "clearsign",     0,                0, "0"                   },
  { "diff-command",  0,               16, "diff"                },
  { "dont-push",     0,                0, "0"                   },
  { "auto-captcha",  "autocaptcha",    0, "on"                  },
  { "auto-shun",     0,                0, "on"                  },
  { "autosync",      0,                0, "on"                  },
  { "binary-glob",   0,               32, ""                    },
  { "clearsign",     0,                0, "off"                 },
  { "diff-command",  0,               16, ""                    },
  { "dont-push",     0,                0, "off"                 },
  { "editor",        0,               16, ""                    },
  { "gdiff-command", 0,               16, "gdiff"               },
  { "ignore-glob",   0,               40, ""                    },
  { "http-port",     0,               16, "8080"                },
  { "localauth",     0,                0, "0"                   },
  { "mtime-changes", 0,                0, "0"                   },
  { "localauth",     0,                0, "off"                 },
  { "manifest",      0,                0, "off"                 },
  { "mtime-changes", 0,                0, "off"                 },
  { "pgp-command",   0,               32, "gpg --clearsign -o " },
  { "proxy",         0,               32, "off"                 },
  { "push-hook-cmd", 0,               32, ""                    },
  { "push-hook-force",
                     0,                0, ""                    },
  { "push-hook-pattern-client",
                     0,               32, ""                    },
  { "push-hook-pattern-server",
                     0,               32, ""                    },
  { "push-hook-privilege",
                     0,               1,  ""                    },
  { "repo-cksum",    0,                0, "on"                  },
  { "ssh-command",   0,               32, ""                    },
  { "web-browser",   0,               32, ""                    },
  { 0,0,0,0 }
};

/*
** COMMAND: settings
1584
1585
1586
1587
1588
1589
1590




1591
1592
1593
1594
1595
1596
1597
1598
1599
1600






































1601
1602
1603
1604
1605
1606
1607
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660







+
+
+
+










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







**                  Example:  *.o,*.obj,*.exe
**
**    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.
**
**    manifest      If enabled, automatically create files "manifest" and
**                  "manifest.uuid" in every checkout.  The SQLite and
**                  Fossil repositories both require this.  Default: off.
**
**    mtime-changes Use file modification times (mtimes) to detect when
**                  files have been modified.  (Default "on".)
**
**    pgp-command   Command used to clear-sign manifests at check-in.
**                  The default is "gpg --clearsign -o ".
**
**    proxy         URL of the HTTP proxy.  If undefined or "off" then
**                  the "http_proxy" environment variable is consulted.
**                  If the http_proxy environment variable is undefined
**                  then a direct HTTP connection is used.
**
**    push-hook-cmd this is the command line, that will be activated
**                  as push hook. Output redirects should be added to
**                  this command line.
**                  The complete command line looks like:
**                    command name: the configured value for push-hook-cmd
**                    argument 1:   timestamp followed by random-number
**                    argument 2:   pattern sent by client
**                  As fallback, stdin/stderr are redirected to files
**                    hook-log-<timestamp followed by random-number>
**
**    push-hook-force
**                  if this is set on the client, it will request always
**                  the hook activation, even if no files where pushed on
**                  the sync.
**                  if this is set on the server, it will accept hook
**                  activiation, even if no files where pushed.
**                     Default: on
**
**    push-hook-pattern-client
**                  if set, a client push will sent this message to the
**                  server, to activate the push hook command.
**                     Default: on
**
**    push-hook-pattern-server
**                  if set, and a client send this pattern at the end of
**                  a push, the push hook command will be executed. This
**                  might be a prefix of the pattern, sent by the client.
**
**    push-hook-privilege
**                  if set, the user doing the push needs this privilege
**                  to trigger the hook. Valid privileges are:
**                    s (setup), a (admin), i (checkin) or o (checkout)
**
**    repo-cksum    Compute checksums over all files in each checkout
**                  as a double-check of correctness.  Defaults to "on".
**                  Disable on large repositories for a performance
**                  improvement.
**
**    ssh-command   Command used to talk to a remote machine with
**                  the "ssh://" protocol.
**
**    web-browser   A shell command used to launch your preferred
**                  web browser when given a URL as an argument.
**                  Defaults to "start" on windows, "open" on Mac,
1625
1626
1627
1628
1629
1630
1631

1632
1633
1634
1635
1636
1637
1638




1639
1640
1641
1642
1643

1644
1645



1646
1647
1648
1649
1650
1651
1652
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714







+







+
+
+
+





+


+
+
+







  }
  if( g.argc==2 ){
    for(i=0; ctrlSettings[i].name; i++){
      print_setting(ctrlSettings[i].name);
    }
  }else if( g.argc==3 || g.argc==4 ){
    const char *zName = g.argv[2];
    int isManifest;
    int n = strlen(zName);
    for(i=0; ctrlSettings[i].name; i++){
      if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
    }
    if( !ctrlSettings[i].name ){
      fossil_fatal("no such setting: %s", zName);
    }
    isManifest = strcmp(ctrlSettings[i].name, "manifest")==0;
    if( isManifest && globalFlag ){
      fossil_fatal("cannot set 'manifest' globally");
    }
    if( unsetFlag ){
      db_unset(ctrlSettings[i].name, globalFlag);
    }else if( g.argc==4 ){
      db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
    }else{
      isManifest = 0;
      print_setting(ctrlSettings[i].name);
    }
    if( isManifest ){
      manifest_to_disk(db_lget_int("checkout", 0));
    }
  }else{
    usage("?PROPERTY? ?VALUE?");
  }
}

/*
** The input in a a timespan measured in days.  Return a string which

Changes to src/delta.c.

193
194
195
196
197
198
199
200




201
202
203
204
205




206
207
208
209
210




211
212
213

214
215
216
217



218
219
220

221
222
223
224
225
226
227
193
194
195
196
197
198
199

200
201
202
203
204




205
206
207
208
209
210
211
212

213
214
215
216
217
218
219
220
221



222
223
224
225
226

227
228
229
230
231
232
233
234







-
+
+
+
+

-
-
-
-
+
+
+
+




-
+
+
+
+



+

-
-
-
+
+
+


-
+







}

/*
** Compute a 32-bit checksum on the N-byte buffer.  Return the result.
*/
static unsigned int checksum(const char *zIn, size_t N){
  const unsigned char *z = (const unsigned char *)zIn;
  unsigned sum = 0;
  unsigned sum0 = 0;
  unsigned sum1 = 0;
  unsigned sum2 = 0;
  unsigned sum3 = 0;
  while(N >= 16){
    sum += ((unsigned)z[0] + z[4] + z[8] + z[12]) << 24;
    sum += ((unsigned)z[1] + z[5] + z[9] + z[13]) << 16;
    sum += ((unsigned)z[2] + z[6] + z[10]+ z[14]) << 8;
    sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
    sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
    sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
    sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
    sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
    z += 16;
    N -= 16;
  }
  while(N >= 4){
    sum += (z[0]<<24) | (z[1]<<16) | (z[2]<<8) | z[3];
    sum0 += z[0];
    sum1 += z[1];
    sum2 += z[2];
    sum3 += z[3];
    z += 4;
    N -= 4;
  }
  sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
  switch(N){
    case 3:   sum += (z[2] << 8);
    case 2:   sum += (z[1] << 16);
    case 1:   sum += (z[0] << 24);
    case 3:   sum3 += (z[2] << 8);
    case 2:   sum3 += (z[1] << 16);
    case 1:   sum3 += (z[0] << 24);
    default:  ;
  }
  return sum;
  return sum3;
}

/*
** Create a new delta.
**
** The delta is written into a preallocated buffer, zDelta, which 
** should be at least 60 bytes longer than the target file, zOut.
510
511
512
513
514
515
516

517

518
519
520
521
522
523
524
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533







+

+







  int lenSrc,            /* Length of the source file */
  const char *zDelta,    /* Delta to apply to the pattern */
  int lenDelta,          /* Length of the delta */
  char *zOut             /* Write the output into this preallocated buffer */
){
  unsigned int limit;
  unsigned int total = 0;
#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
  char *zOrigOut = zOut;
#endif

  limit = getInt(&zDelta, &lenDelta);
  if( *zDelta!='\n' ){
    /* ERROR: size integer not terminated by "\n" */
    return -1;
  }
  zDelta++; lenDelta--;
565
566
567
568
569
570
571

572
573
574
575

576
577
578
579
580
581
582
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593







+




+







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

Changes to src/diffcmd.c.

33
34
35
36
37
38
39

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







+







int portable_system(const char *zOrigCmd){
  int rc;
#if defined(_WIN32)
  /* On windows, we have to put double-quotes around the entire command.
  ** Who knows why - this is just the way windows works.
  */
  char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
  fflush(0);
  rc = system(zNewCmd);
  free(zNewCmd);
#else
  /* On unix, evaluate the command directly.
  */
  rc = system(zOrigCmd);
#endif 
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
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







-
-
+
+



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

-
+

-
+


-
+


-
+

-
+

-
+

-
+

-
+

-
-
+
+

-
-
+
+

-
+
-
-
-
-
+
+
+


-
-
+
+







*/
static void diff_all_two_versions(
  const char *zFrom,
  const char *zTo,
  const char *zDiffCmd,
  int diffFlags
){
  Manifest mFrom, mTo;
  int iFrom, iTo;
  Manifest *pFrom, *pTo;
  ManifestFile *pFromFile, *pToFile;
  int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
  int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;

  pFrom = manifest_get_by_name(zFrom, 0);
  manifest_from_name(zFrom, &mFrom);
  manifest_from_name(zTo, &mTo);
  iFrom = iTo = 0;
  while( iFrom<mFrom.nFile || iTo<mTo.nFile ){
  manifest_file_rewind(pFrom);
  pFromFile = manifest_file_next(pFrom,0);
  pTo = manifest_get_by_name(zTo, 0);
  manifest_file_rewind(pTo);
  pToFile = manifest_file_next(pTo,0);

  while( pFromFile || pToFile ){
    int cmp;
    if( iFrom>=mFrom.nFile ){
    if( pFromFile==0 ){
      cmp = +1;
    }else if( iTo>=mTo.nFile ){
    }else if( pToFile==0 ){
      cmp = -1;
    }else{
      cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
      cmp = strcmp(pFromFile->zName, pToFile->zName);
    }
    if( cmp<0 ){
      printf("DELETED %s\n", mFrom.aFile[iFrom].zName);
      printf("DELETED %s\n", pFromFile->zName);
      if( asNewFlag ){
        diff_manifest_entry(&mFrom.aFile[iFrom], 0, zDiffCmd, ignoreEolWs);
        diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs);
      }
      iFrom++;
      pFromFile = manifest_file_next(pFrom,0);
    }else if( cmp>0 ){
      printf("ADDED   %s\n", mTo.aFile[iTo].zName);
      printf("ADDED   %s\n", pToFile->zName);
      if( asNewFlag ){
        diff_manifest_entry(0, &mTo.aFile[iTo], zDiffCmd, ignoreEolWs);
        diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs);
      }
      iTo++;
    }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
      pToFile = manifest_file_next(pTo,0);
    }else if( strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
      /* No changes */
      iFrom++;
      iTo++;
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }else{
      printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
      printf("CHANGED %s\n", pFromFile->zName);
      diff_manifest_entry(&mFrom.aFile[iFrom], &mTo.aFile[iTo],
                          zDiffCmd, ignoreEolWs);
      iFrom++;
      iTo++;
      diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs);
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }
  }
  manifest_clear(&mFrom);
  manifest_clear(&mTo);
  manifest_destroy(pFrom);
  manifest_destroy(pTo);
}

/*
** COMMAND: diff
** COMMAND: gdiff
**
** Usage: %fossil diff|gdiff ?options? ?FILE?

Changes to src/doc.c.

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







-
-
+
+





-
+
-
-
-
+








-
-
-
+
+
+
+




-
+







                    " WHERE vid=%d AND fname=%Q", vid, zName);
    if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
      goto doc_not_found;
    }

    if( rid==0 ){
      Stmt s;
      Blob baseline;
      Manifest m;
      Manifest *pM;
      ManifestFile *pFile;

      /* Add the vid baseline to the cache */
      if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){
        db_multi_exec("DELETE FROM vcache");
      }
      if( content_get(vid, &baseline)==0 ){
      pM = manifest_get(vid, CFTYPE_MANIFEST);
        goto doc_not_found;
      }
      if( manifest_parse(&m, &baseline)==0 || m.type!=CFTYPE_MANIFEST ){
      if( pM==0 ){
        goto doc_not_found;
      }
      db_prepare(&s,
        "INSERT INTO vcache(vid,fname,rid)"
        " SELECT %d, :fname, rid FROM blob"
        "  WHERE uuid=:uuid",
        vid
      );
      for(i=0; i<m.nFile; i++){
        db_bind_text(&s, ":fname", m.aFile[i].zName);
        db_bind_text(&s, ":uuid", m.aFile[i].zUuid);
      manifest_file_rewind(pM);
      while( (pFile = manifest_file_next(pM,0))!=0 ){
        db_bind_text(&s, ":fname", pFile->zName);
        db_bind_text(&s, ":uuid", pFile->zUuid);
        db_step(&s);
        db_reset(&s);
      }
      db_finalize(&s);
      manifest_clear(&m);
      manifest_destroy(pM);

      /* Try again to find the file */
      rid = db_int(0, "SELECT rid FROM vcache"
                      " WHERE vid=%d AND fname=%Q", vid, zName);
    }
    if( rid==0 ){
      goto doc_not_found;

Changes to src/encode.c.

265
266
267
268
269
270
271
272
273



274
275
276
277
278
279
280
265
266
267
268
269
270
271


272
273
274
275
276
277
278
279
280
281







-
-
+
+
+







}

/*
** Decode a fossilized string in-place.
*/
void defossilize(char *z){
  int i, j, c;
  for(i=j=0; z[i]; i++){
    c = z[i];
  for(i=0; (c=z[i])!=0 && c!='\\'; i++){}
  if( c==0 ) return;
  for(j=i; (c=z[i])!=0; i++){
    if( c=='\\' && z[i+1] ){
      i++;
      switch( z[i] ){
        case 'n':  c = '\n';  break;
        case 's':  c = ' ';   break;
        case 't':  c = '\t';  break;
        case 'r':  c = '\r';  break;

Changes to src/event.c.

61
62
63
64
65
66
67
68

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

68

69
70
71
72
73
74
75







-
+
-







  int rid = 0;             /* rid of the event artifact */
  char *zUuid;             /* UUID corresponding to rid */
  const char *zEventId;    /* Event identifier */
  char *zETime;            /* Time of the event */
  char *zATime;            /* Time the artifact was created */
  int specRid;             /* rid specified by aid= parameter */
  int prevRid, nextRid;    /* Previous or next edits of this event */
  Manifest m;              /* Parsed event artifact */
  Manifest *pEvent;        /* Parsed event artifact */
  Blob content;            /* Original event artifact content */
  Blob fullbody;           /* Complete content of the event body */
  Blob title;              /* Title extracted from the event body */
  Blob tail;               /* Event body that comes after the title */
  Stmt q1;                 /* Query to search for the event */
  int showDetail;          /* True to show details */


111
112
113
114
115
116
117
118
119
120

121
122

123
124
125

126
127
128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
110
111
112
113
114
115
116



117


118
119
120

121
122
123
124
125
126
127
128
129
130
131

132
133
134
135
136
137
138
139







-
-
-
+
-
-
+


-
+










-
+







    return;
  }
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  showDetail = atoi(PD("detail","0"));

  /* Extract the event content.
  */
  memset(&m, 0, sizeof(m));
  blob_zero(&m.content);
  content_get(rid, &content);
  pEvent = manifest_get(rid, CFTYPE_EVENT);
  manifest_parse(&m, &content);
  if( m.type!=CFTYPE_EVENT ){
  if( pEvent==0 ){
    fossil_panic("Object #%d is not an event", rid);
  }
  blob_init(&fullbody, m.zWiki, -1);
  blob_init(&fullbody, pEvent->zWiki, -1);
  if( wiki_find_title(&fullbody, &title, &tail) ){
    style_header(blob_str(&title));
  }else{
    style_header("Event %S", zEventId);
    tail = fullbody;
  }
  if( g.okWrWiki && g.okWrite && nextRid==0 ){
    style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
                          g.zTop, zEventId);
  }
  zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
  zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
  style_submenu_element("Context", "Context", "%s/timeline?c=%T",
                        g.zTop, zETime);
  if( g.okHistory ){
    if( showDetail ){
      style_submenu_element("Plain", "Plain", "%s/event?name=%s&amp;aid=%s",
                            g.zTop, zEventId, zUuid);
      if( nextRid ){
164
165
166
167
168
169
170
171

172
173
174

175
176
177
178
179



180
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
160
161
162
163
164
165
166

167
168
169

170
171
172



173
174
175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200







-
+


-
+


-
-
-
+
+
+








-
+








-
+







  }

  if( showDetail && g.okHistory ){
    int i;
    const char *zClr = 0;
    Blob comment;

    zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate);
    zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate);
    @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
    @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>]
    @ entered by user <b>%h(m.zUser)</b> on
    @ entered by user <b>%h(pEvent->zUser)</b> on
    @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
    @ <blockquote>
    for(i=0; i<m.nTag; i++){
      if( strcmp(m.aTag[i].zName,"+bgcolor")==0 ){
        zClr = m.aTag[i].zValue;
    for(i=0; i<pEvent->nTag; i++){
      if( strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){
        zClr = pEvent->aTag[i].zValue;
      }
    }
    if( zClr && zClr[0]==0 ) zClr = 0;
    if( zClr ){
      @ <div style="background-color: %h(zClr);">
    }else{
      @ <div>
    }
    blob_init(&comment, m.zComment, -1);
    blob_init(&comment, pEvent->zComment, -1);
    wiki_convert(&comment, 0, WIKI_INLINE);
    blob_reset(&comment);
    @ </div>
    @ </blockquote><hr />
  }  

  wiki_convert(&tail, 0, 0);
  style_footer();
  manifest_clear(&m);
  manifest_destroy(pEvent);
}

/*
** WEBPAGE: eventedit
** URL: /eventedit?name=EVENTID
**
** Edit an event.  If name is omitted, create a new event.
257
258
259
260
261
262
263
264

265
266
267
268

269
270
271


272
273

274
275

276
277
278
279
280
281
282
253
254
255
256
257
258
259

260




261



262
263
264

265
266

267
268
269
270
271
272
273
274







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

-
+

-
+







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


  /* If editing an existing event, extract the key fields to use as
  ** a starting point for the edit.
  */
  if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
    Manifest m;
    Manifest *pEvent;
    Blob content;
    memset(&m, 0, sizeof(m));
    blob_zero(&m.content);
    content_get(rid, &content);
    pEvent = manifest_get(rid, CFTYPE_EVENT);
    manifest_parse(&m, &content);
    if( m.type==CFTYPE_EVENT ){
      if( zBody==0 ) zBody = m.zWiki;
    if( pEvent && pEvent->type==CFTYPE_EVENT ){
      if( zBody==0 ) zBody = pEvent->zWiki;
      if( zETime==0 ){
        zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
        zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
      }
      if( zComment==0 ) zComment = m.zComment;
      if( zComment==0 ) zComment = pEvent->zComment;
    }
    if( zTags==0 ){
      zTags = db_text(0,
        "SELECT group_concat(substr(tagname,5),', ')"
        "  FROM tagxref, tag"
        " WHERE tagxref.rid=%d"
        "   AND tagxref.tagid=tag.tagid"

Changes to src/finfo.c.

132
133
134
135
136
137
138
139

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

139
140
141
142
143
144
145
146







-
+







    "   AND event.objid=mlink.mid"
    " ORDER BY event.mtime DESC /*sort*/",
    TAG_BRANCH,
    zFilename
  );
  blob_zero(&title);
  blob_appendf(&title, "History of ");
  hyperlinked_path(zFilename, &title);
  hyperlinked_path(zFilename, &title, 0);
  @ <h2>%b(&title)</h2>
  blob_reset(&title);
  pGraph = graph_init();
  @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
  @ <table class="timelineTable">
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);

Changes to src/http.c.

265
266
267
268
269
270
271
272

273
274
275
276
277
278
279
265
266
267
268
269
270
271

272
273
274
275
276
277
278
279







-
+







      }
      z[j] = z[i];
    }
    z[j] = 0;
    fossil_fatal("server sends error: %s", z);
  }
  if( g.fHttpTrace ){
    printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply));
    /*printf("HTTP RECEIVE:\n%s\n=======================\n",blob_str(pReply));*/
  }else{
    blob_uncompress(pReply, pReply);
  }

  /*
  ** Close the connection to the server if appropriate.
  **

Changes to src/info.c.

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
543
544
545
546
547
548
549


550



551


552
553

554
555
556
557
558

559
560
561
562
563
564
565
566







-
-
+
-
-
-
+
-
-
+

-
+




-
+







  }else{
    style_header("Wiki Information");
    rid = 0;
  }
  db_finalize(&q);
  showTags(rid, "wiki-*");
  if( rid ){
    Blob content;
    Manifest m;
    Manifest *pWiki;
    memset(&m, 0, sizeof(m));
    blob_zero(&m.content);
    content_get(rid, &content);
    pWiki = manifest_get(rid, CFTYPE_WIKI);
    manifest_parse(&m, &content);
    if( m.type==CFTYPE_WIKI ){
    if( pWiki ){
      Blob wiki;
      blob_init(&wiki, m.zWiki, -1);
      blob_init(&wiki, pWiki->zWiki, -1);
      @ <div class="section">Content</div>
      wiki_convert(&wiki, 0, 0);
      blob_reset(&wiki);
    }
    manifest_clear(&m);
    manifest_destroy(pWiki);
  }
  style_footer_cmdref("info",0);
}

/*
** Show a webpage error message
*/
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
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







-
+

-




-
+



-
+

-
-
-
+







  style_footer();
}

/*
** Find an checkin based on query parameter zParam and parse its
** manifest.  Return the number of errors.
*/
static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){
static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
  int rid;
  Blob content;

  *pRid = rid = name_to_rid_www(zParam);
  if( rid==0 ){
    webpage_error("Missing \"%s\" query parameter.", zParam);
    return 1;
    return 0;
  }
  if( !is_a_version(rid) ){
    webpage_error("Artifact %s is not a checkin.", P(zParam));
    return 1;
    return 0;
  }
  content_get(rid, &content);
  manifest_parse(pM, &content);
  return 0;
  return manifest_get(rid, CFTYPE_MANIFEST);
}

/*
** Output a description of a check-in
*/
void checkin_description(int rid){
  Stmt q;
637
638
639
640
641
642
643
644
645


646
647
648
649
650
651
652
630
631
632
633
634
635
636


637
638
639
640
641
642
643
644
645







-
-
+
+







** Show all differences between two checkins.  
*/
void vdiff_page(void){
  int ridFrom, ridTo;
  int showDetail = atoi(PD("detail","0"));
  const char *zFrom = P("from");
  const char *zTo = P("to");
  int iFrom, iTo;
  Manifest mFrom, mTo;
  Manifest *pFrom, *pTo;
  ManifestFile *pFileFrom, *pFileTo;

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

  if( !zFrom || !zFrom[0] || !zTo || !zTo[0] ){
    /* if from or to or both are bissing, show a form to enter
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
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








+
-
-
+
+
+
-
-
+
-
+







+
-
-
+
+
+
+

-
+

-
+


-
+


-
-
-
+
+
+

-
-
-
-
+
+
+
+

-
-
+
+

-
-
-
-
-
+
+
+
+
+


-
-
+
+







    @  name="to" value="%s(zTo?zTo:"")" /></td><td></td></tr>
    @ <tr><td>details:</td><td><input type="checkbox" name="detail"
    @  checked="checked" value="1" /></td></tr>
    @ <tr><td></td><td></td><td>
    @  <input type="submit" name="diff" value="diff" /></td></tr></table>
    @ </div></form>
    style_footer_cmdref("diff",0);
    return;
  }
  }else if(    vdiff_parse_manifest("from", &ridFrom, &mFrom) 
            || vdiff_parse_manifest("to", &ridTo, &mTo)
  pFrom = vdiff_parse_manifest("from", &ridFrom);
  if( pFrom==0 ) return;
  pTo = vdiff_parse_manifest("to", &ridTo);
  ){
    return;
  if( pTo==0 ) return;
  }
  showDetail = atoi(PD("detail","0"));
  style_header("Check-in Differences");
  @ <h2>Difference From:</h2><blockquote>
  checkin_description(ridFrom);
  @ </blockquote><h2>To:</h2><blockquote>
  checkin_description(ridTo);
  @ </blockquote><hr /><p>

  manifest_file_rewind(pFrom);
  iFrom = iTo = 0;
  while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
  pFileFrom = manifest_file_next(pFrom, 0);
  manifest_file_rewind(pTo);
  pFileTo = manifest_file_next(pTo, 0);
  while( pFileFrom || pFileTo ){
    int cmp;
    if( iFrom>=mFrom.nFile ){
    if( pFileFrom==0 ){
      cmp = +1;
    }else if( iTo>=mTo.nFile ){
    }else if( pFileTo==0 ){
      cmp = -1;
    }else{
      cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
      cmp = strcmp(pFileFrom->zName, pFileTo->zName);
    }
    if( cmp<0 ){
      append_file_change_line(mFrom.aFile[iFrom].zName, 
                              mFrom.aFile[iFrom].zUuid, 0, 0);
      iFrom++;
      append_file_change_line(pFileFrom->zName, 
                              pFileFrom->zUuid, 0, 0);
      pFileFrom = manifest_file_next(pFrom, 0);
    }else if( cmp>0 ){
      append_file_change_line(mTo.aFile[iTo].zName, 
                              0, mTo.aFile[iTo].zUuid, 0);
      iTo++;
    }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
      append_file_change_line(pFileTo->zName, 
                              0, pFileTo->zUuid, 0);
      pFileTo = manifest_file_next(pTo, 0);
    }else if( strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
      /* No changes */
      iFrom++;
      iTo++;
      pFileFrom = manifest_file_next(pFrom, 0);
      pFileTo = manifest_file_next(pTo, 0);
    }else{
      append_file_change_line(mFrom.aFile[iFrom].zName, 
                              mFrom.aFile[iFrom].zUuid,
                              mTo.aFile[iTo].zUuid, showDetail);
      iFrom++;
      iTo++;
      append_file_change_line(pFileFrom->zName, 
                              pFileFrom->zUuid,
                              pFileTo->zUuid, showDetail);
      pFileFrom = manifest_file_next(pFrom, 0);
      pFileTo = manifest_file_next(pTo, 0);
    }
  }
  manifest_clear(&mFrom);
  manifest_clear(&mTo);
  manifest_destroy(pFrom);
  manifest_destroy(pTo);

  style_footer_cmdref("diff",0);
}

/*
** Write a description of an object to the www reply.
**
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
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







-
-
+
-
+






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







** Look for "ci" and "filename" query parameters.  If found, try to
** use them to extract the record ID of an artifact for the file.
*/
int artifact_from_ci_and_filename(void){
  const char *zFilename;
  const char *zCI;
  int cirid;
  Blob content;
  Manifest m;
  Manifest *pManifest;
  int i;
  ManifestFile *pFile;

  zCI = P("ci");
  if( zCI==0 ) return 0;
  zFilename = P("filename");
  if( zFilename==0 ) return 0;
  cirid = name_to_rid_www("ci");
  if( !content_get(cirid, &content) ) return 0;
  if( !manifest_parse(&m, &content) ) return 0;
  if( m.type!=CFTYPE_MANIFEST ) return 0;
  for(i=0; i<m.nFile; i++){
    if( strcmp(zFilename, m.aFile[i].zName)==0 ){
      return db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", m.aFile[i].zUuid);
  pManifest = manifest_get(cirid, CFTYPE_MANIFEST);
  if( pManifest==0 ) return 0;
  manifest_file_rewind(pManifest);
  while( (pFile = manifest_file_next(pManifest,0))!=0 ){
    if( strcmp(zFilename, pFile->zName)==0 ){
      int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
      manifest_destroy(pManifest);
      return rid;
    }
  }
  return 0;
}


/*
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203

1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220


1221
1222
1223
1224
1225
1226
1227
1228


1229
1230
1231


1232
1233

1234
1235
1236


1237
1238
1239
1240

1241
1242
1243
1244
1245
1246
1247


1248
1249
1250
1251
1252
1253
1254
1190
1191
1192
1193
1194
1195
1196

1197
1198
1199

1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215


1216
1217



1218
1219
1220


1221
1222
1223
1224

1225
1226
1227

1228
1229


1230
1231
1232
1233
1234

1235
1236
1237
1238
1239
1240


1241
1242
1243
1244
1245
1246
1247
1248
1249







-



-
+















-
-
+
+
-
-
-



-
-
+
+


-
+
+

-
+

-
-
+
+



-
+





-
-
+
+







** WEBPAGE: tinfo
** URL: /tinfo?name=ARTIFACTID
**
** Show the details of a ticket change control artifact.
*/
void tinfo_page(void){
  int rid;
  Blob content;
  char *zDate;
  const char *zUuid;
  char zTktName[20];
  Manifest m;
  Manifest *pTktChng;

  login_check_credentials();
  if( !g.okRdTkt ){ login_needed(); return; }
  rid = name_to_rid_www("name");
  if( rid==0 ){ fossil_redirect_home(); }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( g.okAdmin ){
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
  content_get(rid, &content);
  if( manifest_parse(&m, &content)==0 ){
  pTktChng = manifest_get(rid, CFTYPE_TICKET);
  if( pTktChng==0 ){
    fossil_redirect_home();
  }
  if( m.type!=CFTYPE_TICKET ){
    fossil_redirect_home();
  }
  style_header("Ticket Change Details");
  zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
  memcpy(zTktName, m.zTicketUuid, 10);
  zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
  memcpy(zTktName, pTktChng->zTicketUuid, 10);
  zTktName[10] = 0;
  if( g.okHistory ){
    @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2>
    @ <h2>Changes to ticket
    @ <a href="%s(pTktChng->zTicketUuid)">%s(zTktName)</a></h2>
    @
    @ <p>By %h(m.zUser) on %s(zDate).  See also:
    @ <p>By %h(pTktChng->zUser) on %s(zDate).  See also:
    @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
    @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a>
    @ </p>
    @ <a href="%s(g.zTop)/tkthistory/%s(pTktChng->zTicketUuid)">ticket
    @ history</a></p>
  }else{
    @ <h2>Changes to ticket %s(zTktName)</h2>
    @
    @ <p>By %h(m.zUser) on %s(zDate).
    @ <p>By %h(pTktChng->zUser) on %s(zDate).
    @ </p>
  }
  @
  @ <ol>
  free(zDate);
  ticket_output_change_artifact(&m);
  manifest_clear(&m);
  ticket_output_change_artifact(pTktChng);
  manifest_destroy(pTktChng);
  style_footer_cmdref("info",0);
}


/*
** WEBPAGE: info
** URL: info/ARTIFACTID

Changes to src/manifest.c.

24
25
26
27
28
29
30

31
32
33
34
35
36
37











38
39
40
41
42
43
44



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

61
62
63
64
65

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


74





75
76
77
78
79
80
81
82







+







+
+
+
+
+
+
+
+
+
+
+







+
+
+














-
-
+
-
-
-
-
-
+







#include "manifest.h"
#include <assert.h>

#if INTERFACE
/*
** Types of control files
*/
#define CFTYPE_ANY        0
#define CFTYPE_MANIFEST   1
#define CFTYPE_CLUSTER    2
#define CFTYPE_CONTROL    3
#define CFTYPE_WIKI       4
#define CFTYPE_TICKET     5
#define CFTYPE_ATTACHMENT 6
#define CFTYPE_EVENT      7

/*
** A single F-card within a manifest
*/
struct ManifestFile { 
  char *zName;           /* Name of a file */
  char *zUuid;           /* UUID of the file */
  char *zPerm;           /* File permissions */
  char *zPrior;          /* Prior name if the name was changed */
};


/*
** A parsed manifest or cluster.
*/
struct Manifest {
  Blob content;         /* The original content blob */
  int type;             /* Type of artifact.  One of CFTYPE_xxxxx */
  int rid;              /* The blob-id for this manifest */
  char *zBaseline;      /* Baseline manifest.  The B card. */
  Manifest *pBaseline;  /* The actual baseline manifest */
  char *zComment;       /* Decoded comment.  The C card. */
  double rDate;         /* Date and time from D card.  0.0 if no D card. */
  char *zUser;          /* Name of the user from the U card. */
  char *zRepoCksum;     /* MD5 checksum of the baseline content.  R card. */
  char *zWiki;          /* Text of the wiki page.  W card. */
  char *zWikiTitle;     /* Name of the wiki page. L card. */
  double rEventDate;    /* Date of an event.  E card. */
  char *zEventId;       /* UUID for an event.  E card. */
  char *zTicketUuid;    /* UUID for a ticket. K card. */
  char *zAttachName;    /* Filename of an attachment. A card. */
  char *zAttachSrc;     /* UUID of document being attached. A card. */
  char *zAttachTarget;  /* Ticket or wiki that attachment applies to.  A card */
  int nFile;            /* Number of F cards */
  int nFileAlloc;       /* Slots allocated in aFile[] */
  struct ManifestFile { 
    char *zName;           /* Name of a file */
  int iFile;            /* Index of current file in iterator */
    char *zUuid;           /* UUID of the file */
    char *zPerm;           /* File permissions */
    char *zPrior;          /* Prior name if the name was changed */
    int iRename;           /* index of renamed name in prior/next manifest */
  } *aFile;             /* One entry for each F card */
  ManifestFile *aFile;  /* One entry for each F-card */
  int nParent;          /* Number of parents. */
  int nParentAlloc;     /* Slots allocated in azParent[] */
  char **azParent;      /* UUIDs of parents.  One for each P card argument */
  int nCChild;          /* Number of cluster children */
  int nCChildAlloc;     /* Number of closts allocated in azCChild[] */
  char **azCChild;      /* UUIDs of referenced objects in a cluster. M cards */
  int nTag;             /* Number of T Cards */
85
86
87
88
89
90
91
92

93
94
95
96
97

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











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
























137
138
139
140
141
142
143

144

145
146
147
148
149




150
151
152
153
154
155
156
157
158
159
160
161
162


163
164
165
166
167
168
169
170
171




























































































172
173
174
175
176
177
178
95
96
97
98
99
100
101

102
103
104

105

106
107
108
109
110
111
112








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




















129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162




163
164
165
166
167
168
169
170
171
172
173
174
175
176
177


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287







-
+


-

-
+






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





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






-
+

+

-
-
-
-
+
+
+
+











-
-
+
+









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







};
#endif

/*
** A cache of parsed manifests.  This reduces the number of
** calls to manifest_parse() when doing a rebuild.
*/
#define MX_MANIFEST_CACHE 4
#define MX_MANIFEST_CACHE 6
static struct {
  int nxAge;
  int aRid[MX_MANIFEST_CACHE];
  int aAge[MX_MANIFEST_CACHE];
  Manifest aLine[MX_MANIFEST_CACHE];
  Manifest *apManifest[MX_MANIFEST_CACHE];
} manifestCache;


/*
** 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);
  memset(p, 0, sizeof(*p));
void manifest_destroy(Manifest *p){
  if( p ){
    blob_reset(&p->content);
    free(p->aFile);
    free(p->azParent);
    free(p->azCChild);
    free(p->aTag);
    free(p->aField);
    if( p->pBaseline ) manifest_destroy(p->pBaseline);
    fossil_free(p);
  }
}

/*
** Add an element to the manifest cache using LRU replacement.
*/
void manifest_cache_insert(int rid, Manifest *p){
  int i;
  for(i=0; i<MX_MANIFEST_CACHE; i++){
    if( manifestCache.aRid[i]==0 ) break;
  }
  if( i>=MX_MANIFEST_CACHE ){
    int oldest = 0;
    int oldestAge = manifestCache.aAge[0];
    for(i=1; i<MX_MANIFEST_CACHE; i++){
      if( manifestCache.aAge[i]<oldestAge ){
        oldest = i;
        oldestAge = manifestCache.aAge[i];
      }
    }
    manifest_clear(&manifestCache.aLine[oldest]);
    i = oldest;
  }
  manifestCache.aAge[i] = ++manifestCache.nxAge;
  manifestCache.aRid[i] = rid;
  manifestCache.aLine[i] = *p;
void manifest_cache_insert(Manifest *p){
  while( p ){
    int i;
    Manifest *pBaseline = p->pBaseline;
    p->pBaseline = 0;
    for(i=0; i<MX_MANIFEST_CACHE; i++){
      if( manifestCache.apManifest[i]==0 ) break;
    }
    if( i>=MX_MANIFEST_CACHE ){
      int oldest = 0;
      int oldestAge = manifestCache.aAge[0];
      for(i=1; i<MX_MANIFEST_CACHE; i++){
        if( manifestCache.aAge[i]<oldestAge ){
          oldest = i;
          oldestAge = manifestCache.aAge[i];
        }
      }
      manifest_destroy(manifestCache.apManifest[oldest]);
      i = oldest;
    }
    manifestCache.aAge[i] = ++manifestCache.nxAge;
    manifestCache.apManifest[i] = p;
    p = pBaseline;
  }
}

/*
** Try to extract a line from the manifest cache. Return 1 if found.
** Return 0 if not found.
*/
int manifest_cache_find(int rid, Manifest *p){
static Manifest *manifest_cache_find(int rid){
  int i;
  Manifest *p;
  for(i=0; i<MX_MANIFEST_CACHE; i++){
    if( manifestCache.aRid[i]==rid ){
      *p = manifestCache.aLine[i];
      manifestCache.aRid[i] = 0;
      return 1;
    if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){
      p = manifestCache.apManifest[i];
      manifestCache.apManifest[i] = 0;
      return p;
    }
  }
  return 0;
}

/*
** Clear the manifest cache.
*/
void manifest_cache_clear(void){
  int i;
  for(i=0; i<MX_MANIFEST_CACHE; i++){
    if( manifestCache.aRid[i]>0 ){
      manifest_clear(&manifestCache.aLine[i]);
    if( manifestCache.apManifest[i] ){
      manifest_destroy(manifestCache.apManifest[i]);
    }
  }
  memset(&manifestCache, 0, sizeof(manifestCache));
}

#ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
# define md5sum_init(X)
# define md5sum_step_text(X,Y)
#endif

/*
** Remove the PGP signature from the artifact, if there is one.
*/
static void remove_pgp_signature(char **pz, int *pn){
  char *z = *pz;
  int n = *pn;
  int i;
  if( memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return;
  for(i=34; i<n && (z[i-1]!='\n' || z[i-2]!='\n'); i++){}
  if( i>=n ) return;
  z += i;
  n -= i;
  *pz = z;
  for(i=n-1; i>=0; i--){
    if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){
      n = i+1;
      break;
    }
  }
  *pn = n;
  return;
}

/*
** Verify the Z-card checksum on the artifact, if there is such a
** checksum.  Return 0 if there is no Z-card.  Return 1 if the Z-card
** exists and is correct.  Return 2 if the Z-card exists and has the wrong
** value.
**
**   0123456789 123456789 123456789 123456789 
**   Z aea84f4f863865a8d59d0384e4d2a41c
*/
static int verify_z_card(const char *z, int n){
  if( n<35 ) return 0;
  if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0;
  md5sum_init();
  md5sum_step_text(z, n-35);
  if( memcmp(&z[n-33], md5sum_finish(0), 32)==0 ){
    return 1;
  }else{
    return 2;
  }
}

/*
** A structure used for rapid parsing of the Manifest file
*/
typedef struct ManifestText ManifestText;
struct ManifestText {
  char *z;           /* The first character of the next token */
  char *zEnd;        /* One character beyond the end of the manifest */
  int atEol;         /* True if z points to the start of a new line */
};

/*
** Return a pointer to the next token.  The token is zero-terminated.
** Return NULL if there are no more tokens on the current line.
*/
static char *next_token(ManifestText *p, int *pLen){
  char *z;
  char *zStart;
  int c;
  if( p->atEol ) return 0;
  zStart = z = p->z;
  while( (c=(*z))!=' ' && c!='\n' ){ z++; }
  *z = 0;
  p->z = &z[1];
  p->atEol = c=='\n';
  if( pLen ) *pLen = z - zStart;
  return zStart;
}

/*
** Return the card-type for the next card.  Or, return 0 if there are no
** more cards or if we are not at the end of the current card.
*/
static char next_card(ManifestText *p){
  char c;
  if( !p->atEol || p->z>=p->zEnd ) return 0;
  c = p->z[0];
  if( p->z[1]==' ' ){
    p->z += 2;
    p->atEol = 0;
  }else if( p->z[1]=='\n' ){
    p->z += 2;
    p->atEol = 1;
  }else{
    c = 0;
  }
  return c;
}

/*
** 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.
**
193
194
195
196
197
198
199
200
201


202
203
204

205





206
207
208
209
210






















211
212
213
214



215
216

217
218
219
220

221
222
223



224
225


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

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





259
260
261
262
263
264
265
266
267
268
269

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







-
-
+
+


-
+

+
+
+
+
+


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




+
+
+


+



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

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










-
-
-
+
+
+
+
+

-
-
-
-





-
+



-
-
+







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









-

-
-
+
+
-












-
-
-
+
-
-
-
-
+
+













-
-
-
+
-
-
-
-
-
+

+
-
+
-





-
+






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




+
+
+
+
+
-
-
-
+
+
+




-
-











-
















-
-
-
-
+
+
+
-
-
+







** 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;
static Manifest *manifest_parse(Blob *pContent, int rid){
  Manifest *p;
  int seenZ = 0;
  int i, lineNo=0;
  Blob line, token, a1, a2, a3, a4;
  ManifestText x;
  char cPrevType = 0;
  char cType;
  char *z;
  int n;
  char *zUuid;
  int sz = 0;

  /* Every control artifact ends with a '\n' character.  Exit early
  ** if that is not the case for this artifact. */
  i = blob_size(pContent);
  if( i<=0 || blob_buffer(pContent)[i-1]!='\n' ){
  ** if that is not the case for this artifact.
  */
  z = blob_buffer(pContent);
  n = blob_size(pContent);
  if( n<=0 || z[n-1]!='\n' ){
    blob_reset(pContent);
    return 0;
  }

  /* Strip off the PGP signature if there is one.  Then verify the
  ** Z-card.
  */
  remove_pgp_signature(&z, &n);
  if( verify_z_card(z, n)==0 ){
    blob_reset(pContent);
    return 0;
  }

  /* Verify that the first few characters of the artifact look like
  ** a control artifact.
  */
  if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
    blob_reset(pContent);
    return 0;
  }

  /* Allocate a Manifest object to hold the parsed control artifact.
  */
  p = fossil_malloc( sizeof(*p) );
  memset(p, 0, sizeof(*p));
  memcpy(&p->content, pContent, sizeof(p->content));
  p->rid = rid;
  blob_zero(pContent);
  pContent = &p->content;

  blob_zero(&a1);
  /* Begin parsing, card by card.
  blob_zero(&a2);
  blob_zero(&a3);
  md5sum_init();
  */
  x.z = z;
  x.zEnd = &z[n];
  while( blob_line(pContent, &line) ){
    char *z = blob_buffer(&line);
  x.atEol = 1;
  while( (cType = next_card(&x))!=0 && cType>=cPrevType ){
    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] ){
    switch( cType ){
      /*
      **     A <filename> <target> ?<source>?
      **
      ** Identifies an attachment to either a wiki page or a ticket.
      ** <source> is the artifact that is the attachment.  <source>
      ** is omitted to delete an attachment.  <target> is the name of
      ** a wiki page or ticket to which that attachment is connected.
      */
      case 'A': {
        char *zName, *zTarget, *zSrc;
        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;
        int nTarget = 0, nSrc = 0;
        zName = next_token(&x, 0);
        zTarget = next_token(&x, &nTarget);
        zSrc = next_token(&x, &nSrc);
        if( zName==0 || zTarget==0 ) goto manifest_syntax_error;      
        if( p->zAttachName!=0 ) goto manifest_syntax_error;
        zName = blob_terminate(&a1);
        zTarget = blob_terminate(&a2);
        blob_token(&line, &a3);
        zSrc = blob_terminate(&a3);
        defossilize(zName);
        if( !file_is_simple_pathname(zName) ){
          goto manifest_syntax_error;
        }
        defossilize(zTarget);
        if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
        if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
           && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
          goto manifest_syntax_error;
        }
        if( blob_size(&a3)>0
         && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
        if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
          goto manifest_syntax_error;
        }
        p->zAttachName = (char*)file_tail(zName);
        p->zAttachSrc = zSrc;
        p->zAttachTarget = zTarget;
        break;
      }

      /*
      **    B <uuid>
      **
      ** A B-line gives the UUID for the baselinen of a delta-manifest.
      */
      case 'B': {
        if( p->zBaseline ) goto manifest_syntax_error;
        p->zBaseline = next_token(&x, &sz);
        if( p->zBaseline==0 ) goto manifest_syntax_error;
        if( sz!=UUID_SIZE ) goto manifest_syntax_error;
        if( !validate16(p->zBaseline, UUID_SIZE) ) 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 = next_token(&x, 0);
        if( p->zComment==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( 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);
        p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0));
        if( p->rDate<=0.0 ) goto manifest_syntax_error;
        break;
      }

      /*
      **     E <timestamp> <uuid>
      **
      ** An "event" card that contains the timestamp of the event in the 
      ** format YYYY-MM-DDtHH:MM:SS and a unique identifier for the event.
      ** The event timestamp is distinct from the D timestamp.  The D
      ** timestamp is when the artifact was created whereas the E timestamp
      ** is when the specific event is said to occur.
      */
      case 'E': {
        char *zEDate;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( p->rEventDate!=0.0 ) goto manifest_syntax_error;
        if( p->rEventDate>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;
        if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
        zEDate = blob_terminate(&a1);
        p->rEventDate = db_double(0.0, "SELECT julianday(%Q)", zEDate);
        p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
        if( p->rEventDate<=0.0 ) goto manifest_syntax_error;
        p->zEventId = next_token(&x, &sz);
        if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
        if( sz!=UUID_SIZE ) goto manifest_syntax_error;
        p->zEventId = blob_terminate(&a2);
        if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error;
        break;
      }

      /*
      **     F <filename> <uuid> ?<permissions>? ?<old-name>?
      **     F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
      **
      ** Identifies a file in a manifest.  Multiple F lines are
      ** allowed in a manifest.  F lines are not allowed in any
      ** other control file.  The filename and old-name are fossil-encoded.
      */
      case 'F': {
        char *zName, *zUuid, *zPerm, *zPriorName;
        char *zName, *zPerm, *zPriorName;
        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);
        blob_token(&line, &a3);
        zName = next_token(&x,0);
        zPerm = blob_terminate(&a3);
        if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
        if( zName==0 ) 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;
        }
        zUuid = next_token(&x, &sz);
        if( p->zBaseline==0 || zUuid!=0 ){
          if( sz!=UUID_SIZE ) goto manifest_syntax_error;
          if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
        }
        blob_token(&line, &a4);
        zPriorName = blob_terminate(&a4);
        if( zPriorName[0] ){
        zPerm = next_token(&x,0);
        zPriorName = next_token(&x,0);
        if( zPriorName ){
          defossilize(zPriorName);
          if( !file_is_simple_pathname(zPriorName) ){
            goto manifest_syntax_error;
          }
        }else{
          zPriorName = 0;
        }
        if( p->nFile>=p->nFileAlloc ){
          p->nFileAlloc = p->nFileAlloc*2 + 10;
          p->aFile = fossil_realloc(p->aFile, 
                                    p->nFileAlloc*sizeof(p->aFile[0]) );
        }
        i = p->nFile++;
        p->aFile[i].zName = zName;
        p->aFile[i].zUuid = zUuid;
        p->aFile[i].zPerm = zPerm;
        p->aFile[i].zPrior = zPriorName;
        p->aFile[i].iRename = -1;
        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.  If <value> is omitted then it is understood to be an
      ** empty string.
      */
      case 'J': {
        char *zName, *zValue;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        blob_token(&line, &a2);
        if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
        zName = next_token(&x,0);
        zValue = next_token(&x,0);
        if( zName==0 ) goto manifest_syntax_error;
        zName = blob_terminate(&a1);
        zValue = blob_terminate(&a2);
        if( zValue==0 ) zValue = "";
        defossilize(zValue);
        if( p->nField>=p->nFieldAlloc ){
          p->nFieldAlloc = p->nFieldAlloc*2 + 10;
          p->aField = fossil_realloc(p->aField,
                               p->nFieldAlloc*sizeof(p->aField[0]) );
        }
        i = p->nField++;
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
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







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










-

-
-
+
+
-














-
+
-
-
+
-
-
+







      /*
      **    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 = next_token(&x, &sz);
        if( sz!=UUID_SIZE ) goto manifest_syntax_error;
        if( !validate16(p->zTicketUuid, 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 = next_token(&x,0);
        if( p->zWikiTitle==0 ) goto manifest_syntax_error;
        p->zWikiTitle = blob_terminate(&a1);
        defossilize(p->zWikiTitle);
        if( !wiki_name_is_wellformed((const unsigned char *)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;
        zUuid = next_token(&x, &sz);
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( zUuid==0 ) goto manifest_syntax_error;
        zUuid = blob_terminate(&a1);
        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
        if( sz!=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 = fossil_realloc(p->azCChild
                                 , p->nCChildAlloc*sizeof(p->azCChild[0]) );
        }
        i = p->nCChild++;
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
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







-
-
+
-
-
+
-



















-

-
+
-
-
+
-




















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

-
+







      **     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) ){
        while( (zUuid = next_token(&x, &sz))!=0 ){
          char *zUuid;
          if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
          if( sz!=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 = fossil_realloc(p->azParent,
                               p->nParentAlloc*sizeof(char*));
          }
          i = p->nParent++;
          p->azParent[i] = zUuid;
        }
        break;
      }

      /*
      **     R <md5sum>
      **
      ** Specify the MD5 checksum over the name and content of all files
      ** in the 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;
        p->zRepoCksum = next_token(&x, &sz);
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
        if( sz!=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;
        char *zName, *zValue;
        md5sum_step_text(blob_buffer(&line), blob_size(&line));
        if( blob_token(&line, &a1)==0 ){
          goto manifest_syntax_error;
        zName = next_token(&x, 0);
        if( zName==0 ) goto manifest_syntax_error;
        }
        if( blob_token(&line, &a2)==0 ){
          goto manifest_syntax_error;
        zUuid = next_token(&x, &sz);
        if( zUuid==0 ) goto manifest_syntax_error;
        }
        zName = blob_terminate(&a1);
        zUuid = blob_terminate(&a2);
        if( blob_token(&line, &a3)==0 ){
          zValue = 0;
        zValue = next_token(&x, 0);
        }else{
          zValue = blob_terminate(&a3);
          defossilize(zValue);
        if( zValue ) defossilize(zValue);
        }
        if( blob_size(&a2)==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
        if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
          /* A valid uuid */
        }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){
        }else if( sz==1 && zUuid[0]=='*' ){
          zUuid = 0;
        }else{
          goto manifest_syntax_error;
        }
        defossilize(zName);
        if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
          goto manifest_syntax_error;
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
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







-

-
+
+


-


-











+
-
+

-
-
-
-
+
+
+
+
+
+



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
















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








-
+

-
+







      **     U ?<login>?
      **
      ** Identify the user who created this control file by their
      ** login.  Only one U line is allowed.  Prohibited in clusters.
      ** If the user name is omitted, take that to be "anonymous".
      */
      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 ){
        p->zUser = next_token(&x, 0);
        if( p->zUser==0 ){
          p->zUser = "anonymous";
        }else{
          p->zUser = blob_terminate(&a1);
          defossilize(p->zUser);
        }
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        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': {
        char *zSize;
        int size;
        int size, c;
        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;
        zSize = next_token(&x, 0);
        if( zSize==0 ) goto manifest_syntax_error;
        if( x.atEol==0 ) goto manifest_syntax_error;
        for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
           size = size*10 + c - '0';
        }
        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;
        if( (&x.z[size+1])>=x.zEnd ) 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;
        p->zWiki = x.z;
        x.z += size;
        if( x.z[0]!='\n' ) goto manifest_syntax_error;
        x.z[0] = 0;
        x.z++;
        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': {
#ifndef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
        int rc;
        Blob hash;
#endif
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        zUuid = next_token(&x, &sz);
        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;
        if( sz!=32 ) goto manifest_syntax_error;
        if( !validate16(zUuid, 32) ) goto manifest_syntax_error;
#ifndef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
        md5sum_finish(&hash);
        rc = blob_compare(&hash, &a1);
        blob_reset(&hash);
        if( rc!=0 ) goto manifest_syntax_error;
#endif
        seenZ = 1;
        break;
      }
      default: {
        goto manifest_syntax_error;
      }
    }
  }
  if( !seenHeader ) goto manifest_syntax_error;
  if( x.z<x.zEnd ) goto manifest_syntax_error;

  if( p->nFile>0 || p->zRepoCksum!=0 ){
  if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
    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->zWiki ) goto manifest_syntax_error;
    if( p->zWikiTitle ) goto manifest_syntax_error;
    if( p->zEventId ) goto manifest_syntax_error;
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
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







-
+




-
+


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









-
+



+



-





-
-
+
+

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







    if( p->nField>0 ) goto manifest_syntax_error;
    if( p->zTicketUuid ) goto manifest_syntax_error;
    if( p->zWikiTitle ) goto manifest_syntax_error;
    if( p->zTicketUuid ) goto manifest_syntax_error;
    p->type = CFTYPE_MANIFEST;
  }
  md5sum_init();
  return 1;
  return p;

manifest_syntax_error:
  /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
  md5sum_init();
  manifest_clear(p);
  manifest_destroy(p);
  return 0;
}

/*
** Get a manifest given the rid for the control artifact.  Return
** a pointer to the manifest on success or NULL if there is a failure.
*/
Manifest *manifest_get(int rid, int cfType){
  Blob content;
  Manifest *p;
  p = manifest_cache_find(rid);
  if( p ){
    if( cfType!=CFTYPE_ANY && cfType!=p->type ){
      manifest_cache_insert(p);
      p = 0;
    }
    return p;
  }
  content_get(rid, &content);
  p = manifest_parse(&content, rid);
  if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
    manifest_destroy(p);
    p = 0;
  }
  return p;
}

/*
** Given a checkin name, load and parse the manifest for that checkin.
** Throw a fatal error if anything goes wrong.
*/
Manifest *manifest_get_by_name(const char *zName, int *pRid){
  int rid;
  Manifest *p;

  rid = name_to_rid(zName);
  if( !is_a_version(rid) ){
    fossil_fatal("no such checkin: %s", zName);
  }
  if( pRid ) *pRid = rid;
  p = manifest_get(rid, CFTYPE_MANIFEST);
  if( p==0 ){
    fossil_fatal("cannot parse manifest for checkin: %s", zName);
  }
  return p;
}

/*
** COMMAND: test-parse-manifest
**
** Usage: %fossil test-parse-manifest FILENAME ?N?
**
** Parse the manifest and discarded.  Use for testing only.
*/
void manifest_test_parse_cmd(void){
  Manifest m;
  Manifest *p;
  Blob b;
  int i;
  int n = 1;
  sqlite3_open(":memory:", &g.db);
  if( g.argc!=3 && g.argc!=4 ){
    usage("FILENAME");
  }
  db_must_be_within_tree();
  blob_read_from_file(&b, g.argv[2]);
  if( g.argc>3 ) n = atoi(g.argv[3]);
  for(i=0; i<n; i++){
    Blob b2;
    blob_copy(&b2, &b);
    manifest_parse(&m, &b2);
    manifest_clear(&m);
    p = manifest_parse(&b2, 0);
    manifest_destroy(p);
  }
}

/*
** Fetch the baseline associated with the delta-manifest p.
** Return 0 on success.  If unable to parse the baseline,
** throw an error.  If the baseline is a manifest, throw an
** error if throwError is true, or record that p is an orphan
** and return 1 throwError is false.
*/
static int fetch_baseline(Manifest *p, int throwError){
  if( p->zBaseline!=0 && p->pBaseline==0 ){
    int rid = uuid_to_rid(p->zBaseline, 0);
    if( rid==0 && !throwError ){
      rid = content_new(p->zBaseline);
      db_multi_exec(
         "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)",
         rid, p->rid
      );
      return 1;
    }
    p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST);
    if( p->pBaseline==0 ){
      if( !throwError && db_exists("SELECT 1 FROM phantom WHERE rid=%d",rid) ){
        db_multi_exec(
           "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)",
           rid, p->rid
        );
        return 1;
      }    
      fossil_fatal("cannot access baseline manifest %S", p->zBaseline);
    }
  }
  return 0;
}

/*
** Rewind a manifest-file iterator back to the beginning of the manifest.
*/
void manifest_file_rewind(Manifest *p){
  p->iFile = 0;
  fetch_baseline(p, 1);
  if( p->pBaseline ){
    p->pBaseline->iFile = 0;
  }
}

/*
** Advance to the next manifest-file.
**
** Return NULL for end-of-records or if there is an error.  If an error
** occurs and pErr!=0 then store 1 in *pErr.
*/
ManifestFile *manifest_file_next(
  Manifest *p,   
  int *pErr
){
  ManifestFile *pOut = 0;
  if( pErr ) *pErr = 0;
  if( p->pBaseline==0 ){
    /* Manifest p is a baseline-manifest.  Just scan down the list
    ** of files. */
    if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
  }else{
    /* Manifest p is a delta-manifest.  Scan the baseline but amend the
    ** file list in the baseline with changes described by p.
    */
    Manifest *pB = p->pBaseline;
    int cmp;
    while(1){
      if( pB->iFile>=pB->nFile ){
        /* We have used all entries out of the baseline.  Return the next
        ** entry from the delta. */
        if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
        break;
      }else if( p->iFile>=p->nFile ){
        /* We have used all entries from the delta.  Return the next
        ** entry from the baseline. */
        if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++];
        break;
      }else if( (cmp = strcmp(pB->aFile[pB->iFile].zName,
                              p->aFile[p->iFile].zName)) < 0 ){
        /* The next baseline entry comes before the next delta entry.
        ** So return the baseline entry. */
        pOut = &pB->aFile[pB->iFile++];
        break;
      }else if( cmp>0 ){
        /* The next delta entry comes before the next baseline
        ** entry so return the delta entry */
        pOut = &p->aFile[p->iFile++];
        break;
      }else if( p->aFile[p->iFile].zUuid ){
        /* The next delta entry is a replacement for the next baseline
        ** entry.  Skip the baseline entry and return the delta entry */
        pB->iFile++;
        pOut = &p->aFile[p->iFile++];
        break;
      }else{
        /* The next delta entry is a delete of the next baseline
        ** entry.  Skip them both.  Repeat the loop to find the next
        ** non-delete entry. */
        pB->iFile++;
        p->iFile++;
        continue;
      }
    }
  }
  return pOut;
}

/*
** Translate a filename into a filename-id (fnid).  Create a new fnid
** if no previously exists.
*/
static int filename_to_fnid(const char *zFilename){
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
1045
1046
1047
1048
1049
1050
1051


1052
1053
1054

1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065

1066
1067
1068
1069
1070

1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091




1092
1093
1094
1095
1096
1097
1098
1099

1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149




1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178

1179
1180

1181
1182
1183
1184
1185



1186
1187
1188
1189
1190
1191
1192
1193

1194
1195
1196

1197
1198
1199

1200
1201
1202

1203
1204
1205
1206


1207
1208
1209



1210








1211
1212
1213
1214
1215


1216


1217


1218
1219



1220



1221






1222
1223
1224
1225
1226
1227
1228






1229
1230
1231
1232


1233
1234
1235




1236
1237
1238
1239
1240
1241


1242








1243



1244
1245
1246
1247
1248
1249
1250
1251







-
-
+
+

-
+










-
+




-
+




















-
-
-
-
+
+
+
+
+
+
+

-
+





+
+
+
+
+
+
+
+
+
+








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














-


-
+
+
+
+

-
-
-
+
+
+
+
+
+


-
+


-
+


-
+


-
+
+

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


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








/*
** Add a single entry to the mlink table.  Also add the filename to
** the filename table if it is not there already.
*/
static void add_one_mlink(
  int mid,                  /* The record ID of the manifest */
  const char *zFromUuid,    /* UUID for the mlink.pid field */
  const char *zToUuid,      /* UUID for the mlink.fid field */
  const char *zFromUuid,    /* UUID for the mlink.pid. "" to add file */
  const char *zToUuid,      /* UUID for the mlink.fid. "" to delele */
  const char *zFilename,    /* Filename */
  const char *zPrior        /* Previous filename.  NULL if unchanged */
  const char *zPrior        /* Previous filename. NULL if unchanged */
){
  int fnid, pfnid, pid, fid;
  static Stmt s1;

  fnid = filename_to_fnid(zFilename);
  if( zPrior==0 ){
    pfnid = 0;
  }else{
    pfnid = filename_to_fnid(zPrior);
  }
  if( zFromUuid==0 ){
  if( zFromUuid==0 || zFromUuid[0]==0 ){
    pid = 0;
  }else{
    pid = uuid_to_rid(zFromUuid, 1);
  }
  if( zToUuid==0 ){
  if( zToUuid==0 || zToUuid[0]==0 ){
    fid = 0;
  }else{
    fid = uuid_to_rid(zToUuid, 1);
  }
  db_static_prepare(&s1,
    "INSERT INTO mlink(mid,pid,fid,fnid,pfnid)"
    "VALUES(:m,:p,:f,:n,:pfn)"
  );
  db_bind_int(&s1, ":m", mid);
  db_bind_int(&s1, ":p", pid);
  db_bind_int(&s1, ":f", fid);
  db_bind_int(&s1, ":n", fnid);
  db_bind_int(&s1, ":pfn", pfnid);
  db_exec(&s1);
  if( pid && fid ){
    content_deltify(pid, fid, 0);
  }
}

/*
** Locate a file named zName in the aFile[] array of the given
** manifest.  We assume that filenames are in sorted order.
** Use a binary search.  Return turn the index of the matching
** entry.  Or return -1 if not found.
** Do a binary search to find a file in the p->aFile[] array.  
**
** As an optimization, guess that the file we seek is at index p->iFile.
** That will usually be the case.  If it is not found there, then do the
** actual binary search.
**
** Update p->iFile to be the index of the file that is found.
*/
static int find_file_in_manifest(Manifest *p, const char *zName){
static ManifestFile *manifest_file_seek_base(Manifest *p, const char *zName){
  int lwr, upr;
  int c;
  int i;
  lwr = 0;
  upr = p->nFile - 1;
  if( p->iFile>=lwr && p->iFile<upr ){
    c = strcmp(p->aFile[p->iFile+1].zName, zName);
    if( c==0 ){
      return &p->aFile[++p->iFile];
    }else if( c>0 ){
      upr = p->iFile;
    }else{
      lwr = p->iFile+1;
    }
  }
  while( lwr<=upr ){
    i = (lwr+upr)/2;
    c = strcmp(p->aFile[i].zName, zName);
    if( c<0 ){
      lwr = i+1;
    }else if( c>0 ){
      upr = i-1;
    }else{
      p->iFile = i;
      return &p->aFile[i];
    }
  }
  return 0;
}

/*
** Locate a file named zName in the aFile[] array of the given manifest.
** Return a pointer to the appropriate ManifestFile object.  Return NULL
** if not found.
**
** This routine works even if p is a delta-manifest.  The pointer 
** returned might be to the baseline.
**
** We assume that filenames are in sorted order and use a binary search.
*/
ManifestFile *manifest_file_seek(Manifest *p, const char *zName){
  ManifestFile *pFile;
  
  pFile = manifest_file_seek_base(p, zName);
  if( pFile && pFile->zUuid==0 ) return 0;
  if( pFile==0 && p->zBaseline ){
    fetch_baseline(p, 1);
    pFile = manifest_file_seek_base(p->pBaseline, zName);
  }
      return i;
    }
  }
  return -1;
  return pFile;
}

/*
** This strcmp() function handles NULL arguments.  NULLs sort first.
*/
static int strcmp_null(const char *zOne, const char *zTwo){
  if( zOne==0 ){
    if( zTwo==0 ) return 0;
    return -1;
  }else if( zTwo==0 ){
    return +1;
  }else{
    return strcmp(zOne, zTwo);
  }
}

/*
** Add mlink table entries associated with manifest cid.  The
** parent manifest is pid.
**
** A single mlink entry is added for every file that changed content
** and/or name going from pid to cid.
**
** Deleted files have mlink.fid=0.
** Added files have mlink.pid=0.
** Edited files have both mlink.pid!=0 and mlink.fid!=0
*/
static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
  Manifest other;
  Blob otherContent;
  int otherRid;
  int i, j;
  int i, rc;
  ManifestFile *pChildFile, *pParentFile;
  Manifest **ppOther;
  static Stmt eq;

  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", cid) ){
    return;
  }
  db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid");
  db_bind_int(&eq, ":mid", cid);
  rc = db_step(&eq);
  db_reset(&eq);
  if( rc==SQLITE_ROW ) return;

  assert( pParent==0 || pChild==0 );
  if( pParent==0 ){
    pParent = &other;
    ppOther = &pParent;
    otherRid = pid;
  }else{
    pChild = &other;
    ppOther = &pChild;
    otherRid = cid;
  }
  if( manifest_cache_find(otherRid, &other)==0 ){
  if( (*ppOther = manifest_cache_find(otherRid))==0 ){
    content_get(otherRid, &otherContent);
    if( blob_size(&otherContent)==0 ) return;
    if( manifest_parse(&other, &otherContent)==0 ) return;
    *ppOther = manifest_parse(&otherContent, otherRid);
    if( *ppOther==0 ) return;
  }
  if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
  content_deltify(pid, cid, 0);

    manifest_destroy(*ppOther);
    return;
  }
  /* Use the iRename fields to find the cross-linkage between
  ** renamed files.  */
  for(j=0; j<pChild->nFile; j++){
  if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){
    const char *zPrior = pChild->aFile[j].zPrior;
    if( zPrior && zPrior[0] ){
      i = find_file_in_manifest(pParent, zPrior);
      if( i>=0 ){
        pChild->aFile[j].iRename = i;
        pParent->aFile[i].iRename = j;
      }
    }
    content_deltify(pid, cid, 0); 
  }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){
    content_deltify(pParent->pBaseline->rid, cid, 0);
  }
  
  }

  for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){
  /* Construct the mlink entries */
  for(i=j=0; i<pParent->nFile && j<pChild->nFile; ){
    if( pChildFile->zPrior ){
    int c;
    if( pParent->aFile[i].iRename>=0 ){
       pParentFile = manifest_file_seek(pParent, pChildFile->zPrior);
       if( pParentFile ){
      i++;
    }else if( (c = strcmp(pParent->aFile[i].zName, pChild->aFile[j].zName))<0 ){
      add_one_mlink(cid, pParent->aFile[i].zUuid,0,pParent->aFile[i].zName,0);
         add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
      i++;
    }else if( c>0 ){
      int rn = pChild->aFile[j].iRename;
                       pChildFile->zName, pChildFile->zPrior);
      if( rn>=0 ){
        add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, 
                      pChild->aFile[j].zName, pParent->aFile[rn].zName);
      }else{
        add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
      }
       }
    }else{
       pParentFile = manifest_file_seek(pParent, pChildFile->zName);
       if( pParentFile==0 ){
         if( pChildFile->zUuid ){
           add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0);
         }
      j++;
    }else{
      if( strcmp(pParent->aFile[i].zUuid, pChild->aFile[j].zUuid)!=0 ){
        add_one_mlink(cid, pParent->aFile[i].zUuid, pChild->aFile[j].zUuid, 
                      pChild->aFile[j].zName, 0);
      }
       }else if( strcmp_null(pChildFile->zUuid, pParentFile->zUuid)!=0 ){
         add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
                       pChildFile->zName, 0);
       }
      i++;
      j++;
    }
  }
  if( pParent->zBaseline && pChild->zBaseline ){
  while( i<pParent->nFile ){
    if( pParent->aFile[i].iRename<0 ){
      add_one_mlink(cid, pParent->aFile[i].zUuid, 0, pParent->aFile[i].zName,0);
    }
    for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){
      if( pParentFile->zUuid ) continue;
      pChildFile = manifest_file_seek(pChild, pParentFile->zName);
      if( pChildFile ){
        add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0);
      }
    i++;
  }
    }
  while( j<pChild->nFile ){
    int rn = pChild->aFile[j].iRename;
    if( rn>=0 ){
      add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, 
                    pChild->aFile[j].zName, pParent->aFile[rn].zName);
    }else{
      add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
    }
  }
    j++;
  }
  manifest_cache_insert(otherRid, &other);
  manifest_cache_insert(*ppOther);
}

/*
** True if manifest_crosslink_begin() has been called but
** manifest_crosslink_end() is still pending.
*/
static int manifest_crosslink_busy = 0;
1112
1113
1114
1115
1116
1117
1118
1119

1120
1121
1122
1123

1124
1125

1126
1127
1128
1129






1130
1131
1132
1133

1134
1135
1136
1137


1138
1139

1140
1141

1142
1143
1144
1145
1146
1147
1148

1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164


1165
1166
1167
1168
1169
1170
1171

1172
1173
1174
1175
1176
1177



















1178
1179

1180
1181



1182
1183
1184
1185
1186
1187



1188
1189

1190
1191
1192
1193


1194
1195
1196
1197
1198

1199
1200
1201
1202
1203

1204
1205
1206
1207


1208
1209
1210
1211
1212
1213
1214
1215


1216
1217
1218
1219
1220
1221
1222


1223
1224

1225
1226
1227
1228
1229
1230

1231
1232
1233
1234
1235
1236

1237
1238

1239
1240
1241
1242
1243
1244
1245
1246
1247

1248
1249
1250
1251
1252
1253
1254
1255
1256


1257
1258
1259
1260
1261
1262


1263
1264

1265
1266
1267
1268
1269
1270

1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286

1287
1288
1289
1290
1291
1292
1293
1294
1295

1296
1297
1298
1299
1300

1301
1302
1303
1304
1305


1306
1307
1308

1309
1310

1311
1312
1313
1314
1315
1316


1317
1318
1319
1320
1321
1322
1323
1324


1325
1326
1327


1328
1329
1330

1331
1332

1333
1334
1335

1336
1337
1338
1339
1340

1341
1342
1343
1344
1345

1346
1347

1348
1349
1350

1351
1352
1353
1354
1355

1356
1357
1358
1359
1360
1361
1362


1363
1364

1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1378
1379
1380
1381
1382
1383
1384

1385
1386
1387
1388

1389
1390

1391
1392
1393


1394
1395
1396
1397
1398
1399
1400
1401
1402

1403
1404
1405


1406
1407
1408

1409
1410

1411
1412
1413
1414
1415
1416
1417

1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432


1433
1434
1435
1436
1437
1438
1439
1440

1441
1442





1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462

1463
1464

1465
1466
1467
1468
1469
1470



1471
1472
1473
1474

1475
1476
1477


1478
1479
1480
1481
1482
1483

1484
1485
1486
1487
1488

1489
1490
1491


1492
1493
1494
1495
1496
1497
1498
1499


1500
1501
1502
1503
1504
1505
1506


1507
1508
1509

1510
1511
1512
1513
1514
1515

1516
1517
1518
1519
1520
1521

1522
1523

1524
1525
1526
1527
1528
1529
1530
1531
1532

1533
1534
1535
1536
1537
1538
1539
1540


1541
1542
1543
1544
1545
1546


1547
1548
1549

1550
1551
1552
1553
1554
1555

1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571

1572
1573
1574
1575
1576
1577
1578
1579
1580

1581
1582
1583
1584
1585

1586
1587
1588
1589


1590
1591
1592
1593

1594
1595

1596
1597
1598
1599
1600


1601
1602
1603
1604
1605
1606
1607
1608


1609
1610
1611


1612
1613
1614
1615

1616
1617

1618
1619
1620

1621
1622
1623
1624
1625

1626
1627
1628
1629
1630

1631
1632

1633
1634
1635

1636
1637
1638
1639
1640

1641
1642
1643
1644
1645
1646


1647
1648
1649

1650
1651
1652
1653




























-
+



-
+

-
+


-
-
+
+
+
+
+
+



-
+


-
-
+
+

-
+

-
+






-
+














-
-
+
+






-
+

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

-
+

-
+
+
+



-
-
-
+
+
+

-
+


-
-
+
+




-
+




-
+


-
-
+
+






-
-
+
+





-
-
+
+

-
+





-
+





-
+

-
+








-
+







-
-
+
+




-
-
+
+

-
+





-
+















-
+








-
+




-
+



-
-
+
+


-
+

-
+




-
-
+
+






-
-
+
+

-
-
+
+


-
+

-
+


-
+




-
+




-
+

-
+


-
+




-
+





-
-
+
+

-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
** Historical note:  This routine original processed manifests only.
** Processing for other control artifacts was added later.  The name
** of the routine, "manifest_crosslink", and the name of this source
** file, is a legacy of its original use.
*/
int manifest_crosslink(int rid, Blob *pContent){
  int i;
  Manifest m;
  Manifest *p;
  Stmt q;
  int parentid = 0;

  if( manifest_cache_find(rid, &m) ){
  if( (p = manifest_cache_find(rid))!=0 ){
    blob_reset(pContent);
  }else if( manifest_parse(&m, pContent)==0 ){
  }else if( (p = manifest_parse(pContent, rid))==0 ){
    return 0;
  }
  if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){
    manifest_clear(&m);
  if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
    manifest_destroy(p);
    return 0;
  }
  if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){
    manifest_destroy(p);
    return 0;
  }
  db_begin_transaction();
  if( m.type==CFTYPE_MANIFEST ){
  if( p->type==CFTYPE_MANIFEST ){
    if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
      char *zCom;
      for(i=0; i<m.nParent; i++){
        int pid = uuid_to_rid(m.azParent[i], 1);
      for(i=0; i<p->nParent; i++){
        int pid = uuid_to_rid(p->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);
                      "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate);
        if( i==0 ){
          add_mlink(pid, 0, rid, &m);
          add_mlink(pid, 0, rid, p);
          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);
        add_mlink(rid, p, cid, 0);
      }
      db_finalize(&q);
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment,"
                           "bgcolor,euser,ecomment)"
        "VALUES('ci',"
        "  coalesce("
        "    (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d),"
        "    %.17g"
        "  ),"
        "  %d,%Q,%Q,"
        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0),"
        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
        TAG_DATE, rid, m.rDate,
        rid, m.zUser, m.zComment, 
        TAG_DATE, rid, p->rDate,
        rid, p->zUser, p->zComment, 
        TAG_BGCOLOR, rid,
        TAG_USER, rid,
        TAG_COMMENT, rid
      );
      zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
                        " WHERE rowid=last_insert_rowid()");
      wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE);
      wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE);
      free(zCom);
    }
  }
  if( m.type==CFTYPE_CLUSTER ){
    tag_insert("cluster", 1, 0, rid, m.rDate, rid);
    for(i=0; i<m.nCChild; i++){

      /* If this is a delta-manifest, record the fact that this repository
      ** contains delta manifests, to free the "commit" logic to generate
      ** new delta manifests.
      */
      if( p->zBaseline!=0 ){
        static int once = 0;
        if( !once ){
          db_set_int("seen-delta-manifest", 1, 0);
          once = 0;
        }
      }
    }
  }
  if( p->type==CFTYPE_CLUSTER ){
    static Stmt del1;
    tag_insert("cluster", 1, 0, rid, p->rDate, rid);
    db_static_prepare(&del1, "DELETE FROM unclustered WHERE rid=:rid");
    for(i=0; i<p->nCChild; i++){
      int mid;
      mid = uuid_to_rid(m.azCChild[i], 1);
      mid = uuid_to_rid(p->azCChild[i], 1);
      if( mid>0 ){
        db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid);
        db_bind_int(&del1, ":rid", mid);
        db_step(&del1);
        db_reset(&del1);
      }
    }
  }
  if( m.type==CFTYPE_CONTROL
   || m.type==CFTYPE_MANIFEST
   || m.type==CFTYPE_EVENT
  if( p->type==CFTYPE_CONTROL
   || p->type==CFTYPE_MANIFEST
   || p->type==CFTYPE_EVENT
  ){
    for(i=0; i<m.nTag; i++){
    for(i=0; i<p->nTag; i++){
      int tid;
      int type;
      if( m.aTag[i].zUuid ){
        tid = uuid_to_rid(m.aTag[i].zUuid, 1);
      if( p->aTag[i].zUuid ){
        tid = uuid_to_rid(p->aTag[i].zUuid, 1);
      }else{
        tid = rid;
      }
      if( tid ){
        switch( m.aTag[i].zName[0] ){
        switch( p->aTag[i].zName[0] ){
          case '-':  type = 0;  break;  /* Cancel prior occurances */
          case '+':  type = 1;  break;  /* Apply to target only */
          case '*':  type = 2;  break;  /* Propagate to descendants */
          default:
            fossil_fatal("unknown tag type in manifest: %s", m.aTag);
            fossil_fatal("unknown tag type in manifest: %s", p->aTag);
            return 0;
        }
        tag_insert(&m.aTag[i].zName[1], type, m.aTag[i].zValue, 
                   rid, m.rDate, tid);
        tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, 
                   rid, p->rDate, tid);
      }
    }
    if( parentid ){
      tag_propagate_all(parentid);
    }
  }
  if( m.type==CFTYPE_WIKI ){
    char *zTag = mprintf("wiki-%s", m.zWikiTitle);
  if( p->type==CFTYPE_WIKI ){
    char *zTag = mprintf("wiki-%s", p->zWikiTitle);
    int tagid = tag_findid(zTag, 1);
    int prior;
    char *zComment;
    int nWiki;
    char zLength[40];
    while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
    nWiki = strlen(m.zWiki);
    while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
    nWiki = strlen(p->zWiki);
    sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
    tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
    tag_insert(zTag, 1, zLength, rid, p->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
      tagid, p->rDate
    );
    if( prior ){
      content_deltify(prior, rid, 0);
    }
    if( nWiki>0 ){
      zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
      zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
    }else{
      zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
      zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
    }
    db_multi_exec(
      "REPLACE INTO event(type,mtime,objid,user,comment,"
      "                  bgcolor,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),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
      m.rDate, rid, m.zUser, zComment, 
      p->rDate, rid, p->zUser, zComment, 
      TAG_BGCOLOR, rid,
      TAG_BGCOLOR, rid,
      TAG_USER, rid,
      TAG_COMMENT, rid
    );
    free(zComment);
  }
  if( m.type==CFTYPE_EVENT ){
    char *zTag = mprintf("event-%s", m.zEventId);
  if( p->type==CFTYPE_EVENT ){
    char *zTag = mprintf("event-%s", p->zEventId);
    int tagid = tag_findid(zTag, 1);
    int prior, subsequent;
    int nWiki;
    char zLength[40];
    while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
    nWiki = strlen(m.zWiki);
    while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
    nWiki = strlen(p->zWiki);
    sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
    tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
    tag_insert(zTag, 1, zLength, rid, p->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
      tagid, p->rDate
    );
    if( prior ){
      content_deltify(prior, rid, 0);
      db_multi_exec(
        "DELETE FROM event"
        " WHERE type='e'"
        "   AND tagid=%d"
        "   AND objid IN (SELECT rid FROM tagxref WHERE tagid=%d)",
        tagid, tagid
      );
    }
    subsequent = db_int(0,
      "SELECT rid FROM tagxref"
      " WHERE tagid=%d AND mtime>%.17g"
      " ORDER BY mtime",
      tagid, m.rDate
      tagid, p->rDate
    );
    if( subsequent ){
      content_deltify(rid, subsequent, 0);
    }else{
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
        "VALUES('e',%.17g,%d,%d,%Q,%Q,"
        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
        m.rEventDate, rid, tagid, m.zUser, m.zComment, 
        p->rEventDate, rid, tagid, p->zUser, p->zComment, 
        TAG_BGCOLOR, rid
      );
    }
  }
  if( m.type==CFTYPE_TICKET ){
  if( p->type==CFTYPE_TICKET ){
    char *zTag;

    assert( manifest_crosslink_busy==1 );
    zTag = mprintf("tkt-%s", m.zTicketUuid);
    tag_insert(zTag, 1, 0, rid, m.rDate, rid);
    zTag = mprintf("tkt-%s", p->zTicketUuid);
    tag_insert(zTag, 1, 0, rid, p->rDate, rid);
    free(zTag);
    db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
                  m.zTicketUuid);
                  p->zTicketUuid);
  }
  if( m.type==CFTYPE_ATTACHMENT ){
  if( p->type==CFTYPE_ATTACHMENT ){
    db_multi_exec(
       "INSERT INTO attachment(attachid, mtime, src, target,"
                                        "filename, comment, user)"
       "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
       rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
       (m.zComment ? m.zComment : ""), m.zUser
       rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
       (p->zComment ? p->zComment : ""), p->zUser
    );
    db_multi_exec(
       "UPDATE attachment SET isLatest = (mtime=="
          "(SELECT max(mtime) FROM attachment"
          "  WHERE target=%Q AND filename=%Q))"
       " WHERE target=%Q AND filename=%Q",
       m.zAttachTarget, m.zAttachName,
       m.zAttachTarget, m.zAttachName
       p->zAttachTarget, p->zAttachName,
       p->zAttachTarget, p->zAttachName
    );
    if( strlen(m.zAttachTarget)!=UUID_SIZE
     || !validate16(m.zAttachTarget, UUID_SIZE) 
    if( strlen(p->zAttachTarget)!=UUID_SIZE
     || !validate16(p->zAttachTarget, UUID_SIZE) 
    ){
      char *zComment;
      if( m.zAttachSrc && m.zAttachSrc[0] ){
      if( p->zAttachSrc && p->zAttachSrc[0] ){
        zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
             m.zAttachName, m.zAttachTarget);
             p->zAttachName, p->zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
             m.zAttachName, m.zAttachTarget);
             p->zAttachName, p->zAttachTarget);
      }
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('w',%.17g,%d,%Q,%Q)",
        m.rDate, rid, m.zUser, zComment
        p->rDate, rid, p->zUser, zComment
      );
      free(zComment);
    }else{
      char *zComment;
      if( m.zAttachSrc && m.zAttachSrc[0] ){
      if( p->zAttachSrc && p->zAttachSrc[0] ){
        zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
             m.zAttachName, m.zAttachTarget);
             p->zAttachName, p->zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
             m.zAttachName, m.zAttachTarget);
             p->zAttachName, p->zAttachTarget);
      }
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('t',%.17g,%d,%Q,%Q)",
        m.rDate, rid, m.zUser, zComment
        p->rDate, rid, p->zUser, zComment
      );
      free(zComment);
    }
  }
  db_end_transaction(0);
  if( m.type==CFTYPE_MANIFEST ){
    manifest_cache_insert(rid, &m);
  if( p->type==CFTYPE_MANIFEST ){
    manifest_cache_insert(p);
  }else{
    manifest_clear(&m);
    manifest_destroy(p);
  }
  return 1;
}

/*
** Given a checkin name, load and parse the manifest for that checkin.
** Throw a fatal error if anything goes wrong.
*/
void manifest_from_name(
  const char *zName,
  Manifest *pM
){
  int rid;
  Blob content;

  rid = name_to_rid(zName);
  if( !is_a_version(rid) ){
    fossil_fatal("no such checkin: %s", zName);
  }
  content_get(rid, &content);
  if( !manifest_parse(pM, &content) ){
    fossil_fatal("cannot parse manifest for checkin: %s", zName);
  }
}

Changes to src/md5.c.

39
40
41
42
43
44
45



46

47

48
49
50
51
52
53
54
55
56
57


58
59
60
61
62
63
64
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70







+
+
+

+
-
+










+
+







  int isInit;
  uint32 buf[4];
  uint32 bits[2];
  unsigned char in[64];
};
typedef struct Context MD5Context;

#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32)
# define byteReverse(A,B)
#else
/*
 * Convert an array of integers to little-endian.
 * Note: this code is harmless on little-endian machines.
 * Note: this code is a no-op on little-endian machines.
 */
static void byteReverse (unsigned char *buf, unsigned longs){
        uint32 t;
        do {
                t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
                            ((unsigned)buf[1]<<8 | buf[0]);
                *(uint32 *)buf = t;
                buf += 4;
        } while (--longs);
}
#endif

/* The four core functions - F1 is optimized somewhat */

/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
312
313
314
315
316
317
318




















319
320
321
322
323
324
325
318
319
320
321
322
323
324
325
326
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







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








/*
** Add the content of a blob to the incremental MD5 checksum.
*/
void md5sum_step_blob(Blob *p){
  md5sum_step_text(blob_buffer(p), blob_size(p));
}

/*
** For trouble-shooting only:
**
** Report the current state of the incremental checksum.
*/
const char *md5sum_current_state(void){
  unsigned int cksum = 0;
  unsigned int *pFirst, *pLast;
  static char zResult[12];

  pFirst = (unsigned int*)&incrCtx;
  pLast = (unsigned int*)((&incrCtx)+1);
  while( pFirst<pLast ){
    cksum += *pFirst;
    pFirst++;
  }
  sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum);
  return zResult;
}

/*
** Finish the incremental MD5 checksum.  Store the result in blob pOut
** if pOut!=0.  Also return a pointer to the result.  
**
** This resets the incremental checksum preparing for the next round
** of computation.  The return pointer points to a static buffer that

Changes to src/rebuild.c.

80
81
82
83
84
85
86















87
88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

112

113
114
115
116
117
118
119







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










-
+
-







static int totalSize;       /* Total number of artifacts to process */
static int processCnt;      /* Number processed so far */
static int ttyOutput;       /* Do progress output */
static Bag bagDone;         /* Bag of records rebuilt */

static char *zFNameFormat;  /* Format string for filenames on deconstruct */
static int prefixLength;    /* Length of directory prefix for deconstruct */


/*
** Draw the percent-complete message.
** The input is actually the permill complete.
*/
static void percent_complete(int permill){
  static int lastOutput = -1;
  if( permill>lastOutput ){
    printf("  %d.%d%% complete...\r", permill/10, permill%10);
    fflush(stdout);
    lastOutput = permill;
  }
}


/*
** Called after each artifact is processed
*/
static void rebuild_step_done(rid){
  /* assert( bag_find(&bagDone, rid)==0 ); */
  bag_insert(&bagDone, rid);
  if( ttyOutput ){
    processCnt++;
    if (!g.fQuiet) {
      printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize));
      percent_complete((processCnt*1000)/totalSize);
      fflush(stdout);
    }
  }
}

/*
** Rebuild cross-referencing information for the artifact
** rid with content pBase and all of its descendants.  This
165
166
167
168
169
170
171
172

173
174


175
176
177
178
179
180
181

182
183
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
179
180
181
182
183
184
185

186
187

188
189
190
191
192
193
194
195

196
197
198
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214







-
+

-
+
+






-
+










-
+







    }
    blob_reset(pUse);
    rebuild_step_done(rid);
  
    /* Call all children recursively */
    rid = 0;
    for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
      Stmt q2;
      static Stmt q2;
      int sz;
      db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
      db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid");
      db_bind_int(&q2, ":rid", cid);
      if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
        Blob delta, next;
        db_ephemeral_blob(&q2, 0, &delta);
        blob_uncompress(&delta, &delta);
        blob_delta_apply(pBase, &delta, &next);
        blob_reset(&delta);
        db_finalize(&q2);
        db_reset(&q2);
        if( i<nChild ){
          rebuild_step(cid, sz, &next);
        }else{
          /* Tail recursion */
          rid = cid;
          size = sz;
          blob_reset(pBase);
          *pBase = next;
        }
      }else{
        db_finalize(&q2);
        db_reset(&q2);
        blob_reset(pBase);
      }
    }
    bag_clear(&children);
  }
}

230
231
232
233
234
235
236

237
238
239
240
241
242

243
244
245
246
247
248
249
250
245
246
247
248
249
250
251
252
253
254
255
256
257

258

259
260
261
262
263
264
265







+





-
+
-







** ability of fossil to accept records in any order and still
** construct a sane repository.
*/
int rebuild_db(int randomize, int doOut){
  Stmt s;
  int errCnt = 0;
  char *zTable;
  int incrSize;

  bag_init(&bagDone);
  ttyOutput = doOut;
  processCnt = 0;
  if (!g.fQuiet) {
    printf("0 (0%%)...\r");
    percent_complete(0);
    fflush(stdout);
  }
  db_multi_exec(zSchemaUpdates);
  for(;;){
    zTable = db_text(0,
       "SELECT name FROM sqlite_master /*scan*/"
       " WHERE type='table'"
       " AND name NOT IN ('blob','delta','rcvfrom','user',"
268
269
270
271
272
273
274


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







+
+







     "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')"
  );
  totalSize = db_int(0, "SELECT count(*) FROM blob");
  incrSize = totalSize/100;
  totalSize += incrSize*2;
  db_prepare(&s,
     "SELECT rid, size FROM blob /*scan*/"
     " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
     "   AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)"
  );
  manifest_crosslink_begin();
  while( db_step(&s)==SQLITE_ROW ){
305
306
307
308
309
310
311









312
313
314
315
316
317
318
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344







+
+
+
+
+
+
+
+
+







      db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
      rebuild_step_done(rid);
    }
  }
  db_finalize(&s);
  manifest_crosslink_end();
  rebuild_tag_trunk();
  if (!g.fQuiet) {
    processCnt += incrSize;
    percent_complete((processCnt*1000)/totalSize);
  }
  create_cluster();
  if (!g.fQuiet) {
    processCnt += incrSize;
    percent_complete((processCnt*1000)/totalSize);
  }
  if(!g.fQuiet && ttyOutput ){
    printf("\n");
  }
  return errCnt;
}

/*
369
370
371
372
373
374
375






















376
377
378
379
380
381
382
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







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







    "UPDATE config SET value=lower(hex(randomblob(20)))"
    " WHERE name='project-code';"
    "UPDATE config SET value='detached-' || value"
    " WHERE name='project-name' AND value NOT GLOB 'detached-*';"
  );
  db_end_transaction(0);
}

/*
** COMMAND:  test-create-clusters
**
** Create clusters for all unclustered artifacts if the number of unclustered
** artifacts exceeds the current clustering threshold.
*/
void test_createcluster_cmd(void){
  if( g.argc==3 ){
    db_open_repository(g.argv[2]);
  }else{
    db_find_and_open_repository(1);
    if( g.argc!=2 ){
      usage("?REPOSITORY-FILENAME?");
    }
    db_close();
    db_open_repository(g.zRepositoryName);
  }
  db_begin_transaction();
  create_cluster();
  db_end_transaction(0);
}

/*
** COMMAND: scrub
** %fossil scrub [--verily] [--force] [REPOSITORY]
**
** The command removes sensitive information (such as passwords) from a
** repository so that the respository can be sent to an untrusted reader.

Changes to src/schema.c.

239
240
241
242
243
244
245










246
247
248
249
250
251
252
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262







+
+
+
+
+
+
+
+
+
+







@
@ -- 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
@ );
@
@ -- A record of orphaned delta-manifests.  An orphan is a delta-manifest
@ -- for which we have content, but its baseline-manifest is a phantom.
@ -- We have to track all orphan maniftests so that when the baseline arrives,
@ -- we know to process the orphaned deltas.
@ CREATE TABLE orphan(
@   rid INTEGER PRIMARY KEY,        -- Delta manifest with a phantom baseline
@   baseline INTEGER                -- Phantom baseline of this orphan
@ );
@ CREATE INDEX orphan_baseline ON orphan(baseline);
@
@ -- Unclustered records.  An unclustered record is a record (including
@ -- a cluster records themselves) that is not mentioned by some other
@ -- cluster.
@ --
@ -- Phantoms are usually included in the unclustered table.  A new cluster
@ -- will never be created that contains a phantom.  But another repository

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
23
24
25





26
27
28




29
30
31
32

33
34
35

36
37
38

39
40
41
42
43
44
45
46
47




48
49
50
51
52
53
54
55
56
57


58
59
60





61
62
63
64
65
66
67
68

69
70
71

72
73
74
75

76
77
78
79

80
81
82
83

84
85
86
87
88
89
90
91

92
93
94
95


96
97
98
99
100
101
102
103
104

105
106
107
108
109
110

111
112
113

114
115
116

117







118
119
120


121
122
123
124
125
126

127
128
129
130
131
132
133
134





135
136
137

138
139
140
141



142
143
144
145

146
147

148
149

150
151
152
153


154
155
156
157



158
159
160


161
162
163
164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194




195
196
197
198





199
200
201
202



203
204
205
206




207
208


209
210


211
212
213
214
215









216
217
218
219




220
221
222

223
224
225

226
227
228
229
230

231
232
233


234

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
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
1

2

3











4
5
6
7




8
9
10
11
12
13
14



15
16
17
18
19
20


21



22



23


24






25
26
27
28

29








30
31



32
33
34
35
36








37



38


39

40




41




42








43




44
45









46






47



48



49

50
51
52
53
54
55
56



57
58






59








60
61
62
63
64
65


66
67



68
69
70




71


72


73




74
75




76
77
78



79
80





81





























82
83
84
85




86
87
88
89
90




91
92
93




94
95
96
97


98
99


100
101





102
103
104
105
106
107
108
109
110




111
112
113
114



115



116





117



118
119

120
121
122
































123



124
125
126



127





128











129
130
131
132
133
134
135


136





137


138
139


140






141


142


143
144
145
146


147



148






149





150
151









152
153
154
155
156
157





158
159
160
161




162










163








164





165
166






167
168
169
170
171








172
173
174




175





176
177




178
179
180


181




182
183



184





185

186
187
188
189
190
191
192

-
+
-

-
-
-
-
-
-
-
-
-
-
-




-
-
-
-


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


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

-
-
-
-
-
-
+
+
+
+
-

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

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

-
-
+

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


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

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

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







/*
** This implementation of SHA1 is adapted from the example implementation
** This implementation of SHA1.
** contained in RFC-3174.
*/
/*
 * If you do not have the ISO standard stdint.h header file, then you
 * must typdef the following:
 *    name              meaning
 *  */
#if defined(__DMC__) || defined(_MSC_VER)
  typedef  unsigned long uint32_t; //unsigned 32 bit integer
  typedef  unsigned char  uint8_t; //unsigned  8 bit integer (i.e., unsigned char)
#else
#  include <stdint.h>
#endif
#include <sys/types.h>
#include "config.h"
#include "sha1.h"

#define SHA1HashSize 20
#define shaSuccess 0
#define shaInputTooLong 1
#define shaStateError 2

/*
** The SHA1 implementation below is adapted from:
**
**  $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $
**  $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $
**
 *  This structure will hold context information for the SHA-1
 *  hashing operation
 */
** SHA-1 in C
** By Steve Reid <steve@edmweb.com>
** 100% Public Domain
*/
typedef struct SHA1Context SHA1Context;
struct SHA1Context {
    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */

  unsigned int state[5];
    uint32_t Length_Low;            /* Message length in bits      */
    uint32_t Length_High;           /* Message length in bits      */

  unsigned int count[2];
    int Message_Block_Index;   /* Index into message block array   */
    uint8_t Message_Block[64];      /* 512-bit message blocks      */

  unsigned char buffer[64];
    int Computed;               /* Is the digest computed?         */
    int Corrupted;             /* Is the message digest corrupted? */
};

/*
 *  sha1.c
 *
 *  Description:
 *      This file implements the Secure Hashing Algorithm 1 as

/*
 * blk0() and blk() perform the initial expand.
 * I got the idea of expanding during the round function from SSLeay
 *      defined in FIPS PUB 180-1 published April 17, 1995.
 *
 *      The SHA-1, produces a 160-bit message digest for a given
 *      data stream.  It should take about 2**n steps to find a
 *      message with the same digest as a given message and
 *      2**(n/2) to find any two messages with the same digest,
 *      when n is the digest size in bits.  Therefore, this
 *      algorithm can serve as a means of providing a
 *      "fingerprint" for a message.
 *
 * blk0le() for little-endian and blk0be() for big-endian.
 */
 *  Portability Issues:
 *      SHA-1 is defined in terms of 32-bit "words".  This code
 *      uses <stdint.h> (included via "sha1.h" to define 32 and 8
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
#define blk0le(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
    |(rol(block->l[i],8)&0x00FF00FF))
#define blk0be(i) block->l[i]
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
 *      bit unsigned integer types.  If your C compiler does not
 *      support 32 bit unsigned integers, this code is not
 *      appropriate.
 *
 *  Caveats:
 *      SHA-1 is designed to work with messages less than 2^64 bits
 *      long.  Although SHA-1 allows a message digest to be generated
 *      for messages of any number of bits less than 2^64, this
    ^block->l[(i+2)&15]^block->l[i&15],1))
 *      implementation only works with messages with a length that is
 *      a multiple of the size of an 8-bit character.
 *

 */

/*
 *  Define the SHA1 circular left shift macro
 * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
 */
#define SHA1CircularShift(bits,word) \
                (((word) << (bits)) | ((word) >> (32-(bits))))

 *
/* Local Function Prototyptes */
static void SHA1PadMessage(SHA1Context *);
static void SHA1ProcessMessageBlock(SHA1Context *);

 * Rl0() for little-endian and Rb0() for big-endian.  Endianness is 
/*
 *  SHA1Reset
 *
 *  Description:
 *      This function will initialize the SHA1Context in preparation
 *      for computing a new SHA1 message digest.
 *
 *  Parameters:
 * determined at run-time.
 *      context: [in/out]
 *          The context to reset.
 *
 *  Returns:
 */
#define Rl0(v,w,x,y,z,i) \
 *      sha Error Code.
 *
 */
static int SHA1Reset(SHA1Context *context)
{
    context->Length_Low             = 0;
    context->Length_High            = 0;
    context->Message_Block_Index    = 0;

    z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=rol(w,30);
    context->Intermediate_Hash[0]   = 0x67452301;
    context->Intermediate_Hash[1]   = 0xEFCDAB89;
    context->Intermediate_Hash[2]   = 0x98BADCFE;
    context->Intermediate_Hash[3]   = 0x10325476;
    context->Intermediate_Hash[4]   = 0xC3D2E1F0;

#define Rb0(v,w,x,y,z,i) \
    context->Computed   = 0;
    context->Corrupted  = 0;

    z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=rol(w,30);
    return shaSuccess;
}

#define R1(v,w,x,y,z,i) \
/*
    z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) \
    z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) \
    z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) \
    z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
 *  SHA1Result
 *
 *  Description:

typedef union {
 *      This function will return the 160-bit message digest into the
 *      Message_Digest array  provided by the caller.
 *      NOTE: The first octet of hash is stored in the 0th element,
 *            the last octet of hash in the 19th element.
 *
 *  Parameters:
    unsigned char c[64];
 *      context: [in/out]
 *          The context to use to calculate the SHA-1 hash.
 *      Message_Digest: [out]
 *          Where the digest is returned.
 *
 *  Returns:
 *      sha Error Code.
 *
    unsigned int l[16];
} CHAR64LONG16;

/*
 * Hash a single 512-bit block. This is the core of the algorithm.
 */
static int SHA1Result( SHA1Context *context,
                uint8_t Message_Digest[SHA1HashSize])
void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
{
    int i;

    if (context->Corrupted)
  unsigned int a, b, c, d, e;
  CHAR64LONG16 *block;
  static int one = 1;
    {
        return context->Corrupted;
    }

  CHAR64LONG16 workspace;
    if (!context->Computed)
    {

        SHA1PadMessage(context);
        for(i=0; i<64; ++i)
  block = &workspace;
        {
            /* message may be sensitive, clear it out */
            context->Message_Block[i] = 0;
        }
  (void)memcpy(block, buffer, 64);

        context->Length_Low = 0;    /* and clear length */
        context->Length_High = 0;
        context->Computed = 1;

  /* Copy context->state[] to working vars */
  a = state[0];
  b = state[1];
    }

    for(i = 0; i < SHA1HashSize; ++i)
  c = state[2];
  d = state[3];
    {
        Message_Digest[i] = context->Intermediate_Hash[i>>2]
                            >> 8 * ( 3 - ( i & 0x03 ) );
    }

  e = state[4];
    return shaSuccess;
}

/*
 *  SHA1Input
 *
 *  Description:
 *      This function accepts an array of octets as the next portion
 *      of the message.
 *
 *  Parameters:
 *      context: [in/out]
 *          The SHA context to update
 *      message_array: [in]
 *          An array of characters representing the next portion of
 *          the message.
 *      length: [in]
 *          The length of the message in message_array
 *
 *  Returns:
 *      sha Error Code.
 *
 */
static
int SHA1Input(    SHA1Context    *context,
                  const uint8_t  *message_array,
                  unsigned       length)
{
    if (!length)

  /* 4 rounds of 20 operations each. Loop unrolled. */
  if( 1 == *(unsigned char*)&one ){
    Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3);
    {
        return shaSuccess;
    }

    Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7);
    Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11);
    Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15);
  }else{
    Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3);
    if (context->Computed)
    {
        context->Corrupted = shaStateError;

    Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7);
    Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11);
    Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15);
        return shaStateError;
    }

    if (context->Corrupted)
  }
  R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
  R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
  R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
    {
         return context->Corrupted;
  R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
  R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
    }
    while(length-- && !context->Corrupted)
  R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
  R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
    {
    context->Message_Block[context->Message_Block_Index++] =
                    (*message_array & 0xFF);

    context->Length_Low += 8;
  R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
  R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
  R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
  R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
  R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
  R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
  R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
  R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
  R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
    if (context->Length_Low == 0)
    {
        context->Length_High++;
        if (context->Length_High == 0)

  /* Add the working vars back into context.state[] */
  state[0] += a;
  state[1] += b;
        {
            /* Message is too long */
            context->Corrupted = 1;
  state[2] += c;
        }
    }

  state[3] += d;
    if (context->Message_Block_Index == 64)
    {
        SHA1ProcessMessageBlock(context);
    }

  state[4] += e;
    message_array++;
    }


  /* Wipe variables */
    return shaSuccess;
  a = b = c = d = e = 0;
}

/*
 *  SHA1ProcessMessageBlock
 *
 *  Description:
 *      This function will process the next 512 bits of the message
 *      stored in the Message_Block array.
 *
 *  Parameters:
 *      None.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:
 *      Many of the variable names in this code, especially the
 *      single character names, were used because those were the
 *      names used in the publication.
 *
 *
 */
static void SHA1ProcessMessageBlock(SHA1Context *context)
{
    const uint32_t K[] =    {       /* Constants defined in SHA-1   */
                            0x5A827999,
                            0x6ED9EBA1,
                            0x8F1BBCDC,
                            0xCA62C1D6
                            };
    int           t;                 /* Loop counter                */
    uint32_t      temp;              /* Temporary word value        */
    uint32_t      W[80];             /* Word sequence               */
    uint32_t      A, B, C, D, E;     /* Word buffers                */

    /*
     *  Initialize the first 16 words in the array W
     */
/*
 * SHA1Init - Initialize new context
 */
    for(t = 0; t < 16; t++)
    {
        W[t] = context->Message_Block[t * 4] << 24;
static void SHA1Init(SHA1Context *context){
        W[t] |= context->Message_Block[t * 4 + 1] << 16;
        W[t] |= context->Message_Block[t * 4 + 2] << 8;
        W[t] |= context->Message_Block[t * 4 + 3];
    }

    /* SHA1 initialization constants */
    for(t = 16; t < 80; t++)
    {
       W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
    }

    A = context->Intermediate_Hash[0];
    B = context->Intermediate_Hash[1];
    C = context->Intermediate_Hash[2];
    D = context->Intermediate_Hash[3];
    E = context->Intermediate_Hash[4];

    context->state[0] = 0x67452301;
    context->state[1] = 0xEFCDAB89;
    context->state[2] = 0x98BADCFE;
    context->state[3] = 0x10325476;
    context->state[4] = 0xC3D2E1F0;
    context->count[0] = context->count[1] = 0;
}
    for(t = 0; t < 20; t++)
    {

        temp =  SHA1CircularShift(5,A) +
                ((B & C) | ((~B) & D)) + E + W[t] + K[0];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);

        B = A;
        A = temp;
/*
 * Run your data through this.
    }

 */
    for(t = 20; t < 40; t++)
    {
        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
static void SHA1Update(
        B = A;
        A = temp;
  SHA1Context *context,
    }

  const unsigned char *data,
  unsigned int len
){
    unsigned int i, j;
    for(t = 40; t < 60; t++)
    {

        temp = SHA1CircularShift(5,A) +
               ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
        E = D;
    j = context->count[0];
        D = C;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }

    if ((context->count[0] += len << 3) < j)
    for(t = 60; t < 80; t++)
    {
        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
        E = D;
        D = C;
	context->count[1] += (len>>29)+1;
    j = (j >> 3) & 63;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }

    context->Intermediate_Hash[0] += A;
    context->Intermediate_Hash[1] += B;
    context->Intermediate_Hash[2] += C;
    context->Intermediate_Hash[3] += D;
    if ((j + len) > 63) {
	(void)memcpy(&context->buffer[j], data, (i = 64-j));
	SHA1Transform(context->state, context->buffer);
	for ( ; i + 63 < len; i += 64)
	    SHA1Transform(context->state, &data[i]);
	j = 0;
    context->Intermediate_Hash[4] += E;

    context->Message_Block_Index = 0;
}

    } else {
	i = 0;
    }
    (void)memcpy(&context->buffer[j], &data[i], len - i);
/*
 *  SHA1PadMessage
 *

}
 *  Description:
 *      According to the standard, the message must be padded to an even
 *      512 bits.  The first padding bit must be a '1'.  The last 64
 *      bits represent the length of the original message.  All bits in
 *      between should be 0.  This function will pad the message
 *      according to those rules by filling the Message_Block array
 *      accordingly.  It will also call the ProcessMessageBlock function
 *      provided appropriately.  When it returns, it can be assumed that
 *      the message digest has been computed.
 *

 *  Parameters:
 *      context: [in/out]
 *          The context to pad
 *      ProcessMessageBlock: [in]
 *          The appropriate SHA*ProcessMessageBlock function
 *  Returns:
 *      Nothing.
 *

 */
static void SHA1PadMessage(SHA1Context *context)
{
    /*
     *  Check to see if the current message block is too small to hold
/*
 * Add padding and return the message digest.
     *  the initial padding bits and length.  If so, we will pad the
     *  block, process it, and then continue padding into a second
     *  block.
     */
    if (context->Message_Block_Index > 55)
    {
 */
static void SHA1Final(SHA1Context *context, unsigned char digest[20]){
    unsigned int i;
    unsigned char finalcount[8];

        context->Message_Block[context->Message_Block_Index++] = 0x80;
        while(context->Message_Block_Index < 64)
        {
            context->Message_Block[context->Message_Block_Index++] = 0;
        }

        SHA1ProcessMessageBlock(context);

    for (i = 0; i < 8; i++) {
	finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
	 >> ((3-(i & 3)) * 8) ) & 255);	 /* Endian independent */
        while(context->Message_Block_Index < 56)
        {
            context->Message_Block[context->Message_Block_Index++] = 0;
        }
    }
    }
    else
    {
        context->Message_Block[context->Message_Block_Index++] = 0x80;
        while(context->Message_Block_Index < 56)
    SHA1Update(context, (const unsigned char *)"\200", 1);
    while ((context->count[0] & 504) != 448)
        {

            context->Message_Block[context->Message_Block_Index++] = 0;
        }
	SHA1Update(context, (const unsigned char *)"\0", 1);
    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */

    }

    if (digest) {
    /*
     *  Store the message length as the last 8 octets
     */
    context->Message_Block[56] = context->Length_High >> 24;
	for (i = 0; i < 20; i++)
	    digest[i] = (unsigned char)
    context->Message_Block[57] = context->Length_High >> 16;
    context->Message_Block[58] = context->Length_High >> 8;
    context->Message_Block[59] = context->Length_High;
		((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
    context->Message_Block[60] = context->Length_Low >> 24;
    context->Message_Block[61] = context->Length_Low >> 16;
    context->Message_Block[62] = context->Length_Low >> 8;
    context->Message_Block[63] = context->Length_Low;

    }
    SHA1ProcessMessageBlock(context);
}


/*
** Convert a digest into base-16.  digest should be declared as
** "unsigned char digest[20]" in the calling function.  The SHA1
** digest is stored in the first 20 bytes.  zBuf should
439
440
441
442
443
444
445
446

447
448
449
450
451
452
453

454
455
456
457
458
459
460
212
213
214
215
216
217
218

219
220
221
222
223
224
225

226
227
228
229
230
231
232
233







-
+






-
+







static int incrInit = 0;

/*
** Add more text to the incremental SHA1 checksum.
*/
void sha1sum_step_text(const char *zText, int nBytes){
  if( !incrInit ){
    SHA1Reset(&incrCtx);
    SHA1Init(&incrCtx);
    incrInit = 1;
  }
  if( nBytes<=0 ){
    if( nBytes==0 ) return;
    nBytes = strlen(zText);
  }
  SHA1Input(&incrCtx, (unsigned char*)zText, nBytes);
  SHA1Update(&incrCtx, (unsigned char*)zText, nBytes);
}

/*
** Add the content of a blob to the incremental SHA1 checksum.
*/
void sha1sum_step_blob(Blob *p){
  sha1sum_step_text(blob_buffer(p), blob_size(p));
468
469
470
471
472
473
474
475

476
477
478
479
480
481
482
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255







-
+







** of computation.  The return pointer points to a static buffer that
** is overwritten by subsequent calls to this function.
*/
char *sha1sum_finish(Blob *pOut){
  unsigned char zResult[20];
  static char zOut[41];
  sha1sum_step_text(0,0);
  SHA1Result(&incrCtx, zResult);
  SHA1Final(&incrCtx, zResult);
  incrInit = 0;
  DigestToBase16(zResult, zOut);
  if( pOut ){
    blob_zero(pOut);
    blob_append(pOut, zOut, 40);
  }
  return zOut;
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
268
269
270
271
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







-
+




-
+




-
+















-
-
+
+






-
+













-
-
-
+
+
+







  unsigned char zResult[20];
  char zBuf[10240];

  in = fopen(zFilename,"rb");
  if( in==0 ){
    return 1;
  }
  SHA1Reset(&ctx);
  SHA1Init(&ctx);
  for(;;){
    int n;
    n = fread(zBuf, 1, sizeof(zBuf), in);
    if( n<=0 ) break;
    SHA1Input(&ctx, (unsigned char*)zBuf, (unsigned)n);
    SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
  }
  fclose(in);
  blob_zero(pCksum);
  blob_resize(pCksum, 40);
  SHA1Result(&ctx, zResult);
  SHA1Final(&ctx, zResult);
  DigestToBase16(zResult, blob_buffer(pCksum));
  return 0;
}

/*
** Compute the SHA1 checksum of a blob in memory.  Store the resulting
** checksum in the blob pCksum.  pCksum is assumed to be either
** uninitialized or the same blob as pIn.
**
** Return the number of errors.
*/
int sha1sum_blob(const Blob *pIn, Blob *pCksum){
  SHA1Context ctx;
  unsigned char zResult[20];

  SHA1Reset(&ctx);
  SHA1Input(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn));
  SHA1Init(&ctx);
  SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn));
  if( pIn==pCksum ){
    blob_reset(pCksum);
  }else{
    blob_zero(pCksum);
  }
  blob_resize(pCksum, 40);
  SHA1Result(&ctx, zResult);
  SHA1Final(&ctx, zResult);
  DigestToBase16(zResult, blob_buffer(pCksum));
  return 0;
}

/*
** Compute the SHA1 checksum of a zero-terminated string.  The
** result is held in memory obtained from mprintf().
*/
char *sha1sum(const char *zIn){
  SHA1Context ctx;
  unsigned char zResult[20];
  char zDigest[41];

  SHA1Reset(&ctx);
  SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn));
  SHA1Result(&ctx, zResult);
  SHA1Init(&ctx);
  SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
  SHA1Final(&ctx, zResult);
  DigestToBase16(zResult, zDigest);
  return mprintf("%s", zDigest);
}

/*
** Convert a cleartext password for a specific user into a SHA1 hash.
** 
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
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







-
+










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







*/
char *sha1_shared_secret(const char *zPw, const char *zLogin){
  static char *zProjectId = 0;
  SHA1Context ctx;
  unsigned char zResult[20];
  char zDigest[41];

  SHA1Reset(&ctx);
  SHA1Init(&ctx);
  if( zProjectId==0 ){
    zProjectId = db_get("project-code", 0);

    /* On the first xfer request of a clone, the project-code is not yet
    ** known.  Use the cleartext password, since that is all we have.
    */
    if( zProjectId==0 ){
      return mprintf("%s", zPw);
    }
  }
  SHA1Input(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
  SHA1Input(&ctx, (unsigned char*)"/", 1);
  SHA1Input(&ctx, (unsigned char*)zLogin, strlen(zLogin));
  SHA1Input(&ctx, (unsigned char*)"/", 1);
  SHA1Input(&ctx, (unsigned const char*)zPw, strlen(zPw));
  SHA1Result(&ctx, zResult);
  SHA1Update(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
  SHA1Update(&ctx, (unsigned char*)"/", 1);
  SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
  SHA1Update(&ctx, (unsigned char*)"/", 1);
  SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
  SHA1Final(&ctx, zResult);
  DigestToBase16(zResult, zDigest);
  return mprintf("%s", zDigest);
}

/*
** COMMAND: sha1sum
** %fossil sha1sum FILE...

Changes to src/sync.c.

163
164
165
166
167
168
169



170

171
172
173
174
175
176
177
163
164
165
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180







+
+
+
-
+







** is used.
**
** The URL specified normally becomes the new "remote-url" used for
** subsequent <a>push</a>, <a>pull</a>, and <a>sync</a> operations.  However,
** the "--once" command-line option makes the URL a one-time-use URL
** that is not saved.
**
** If configured (<a>setting</a> push-hook-..), the push hook command will be
** executed on the server after pushing files.
**
** See also: <a>clone</a>, <a>pull</a>, <a>sync</a>, <a>remote-url</a>
** See also: <a>callhook</a>, <a>clone</a>, <a>pull</a>, <a>sync</a>, <a>remote-url</a>
*/
void push_cmd(void){
  process_sync_args();
  client_sync(1,0,0,0,0);
}


194
195
196
197
198
199
200



201

202
203
204
205
206
207
208
197
198
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214







+
+
+
-
+







** command is used.
**
** The URL specified normally becomes the new "remote-url" used for
** subsequent <a>push</a>, <a>pull</a>, and <a>sync</a> operations.  However,
** the "--once" command-line option makes the URL a one-time-use URL
** that is not saved.
**
** If configured (<a>setting</a> push-hook-..), the push hook command will be
** executed on the server after pushing files.
**
** See also:  <a>clone</a>, <a>push</a>, <a>pull</a>, <a>remote-url</a>
** See also: <a>callhook</a>, <a>clone</a>, <a>push</a>, <a>pull</a>, <a>remote-url</a>
*/
void sync_cmd(void){
  int syncFlags = process_sync_args();
  client_sync(1,1,0,syncFlags,0);
}

/*

Changes to src/tkt.c.

212
213
214
215
216
217
218
219

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






234
235
236
237
238
239
240
212
213
214
215
216
217
218

219

220
221
222
223
224
225
226
227





228
229
230
231
232
233
234
235
236
237
238
239
240







-
+
-








-
-
-
-
-
+
+
+
+
+
+







/*
** 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;
  Manifest *pTicket;
  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, rid);
    manifest_ticket_event(rid, &manifest, createFlag, tagid);
    manifest_clear(&manifest);
    pTicket = manifest_get(rid, CFTYPE_TICKET);
    if( pTicket ){
      ticket_insert(pTicket, createFlag, rid);
      manifest_ticket_event(rid, pTicket, createFlag, tagid);
      manifest_destroy(pTicket);
    }
    createFlag = 0;
  }
  db_finalize(&q);
}

/*
** Create the subscript interpreter and load the "common" code.
751
752
753
754
755
756
757
758
759

760
761
762
763
764
765
766
751
752
753
754
755
756
757


758
759
760
761
762
763
764
765







-
-
+







    "  FROM attachment, blob"
    " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
    "   AND blob.rid=attachid"
    " ORDER BY 1 DESC",
    tagid, tagid
  );
  while( db_step(&q)==SQLITE_ROW ){
    Blob content;
    Manifest m;
    Manifest *pTicket;
    char zShort[12];
    const char *zDate = db_column_text(&q, 0);
    int rid = db_column_int(&q, 1);
    const char *zChngUuid = db_column_text(&q, 2);
    const char *zFile = db_column_text(&q, 4);
    memcpy(zShort, zChngUuid, 10);
    zShort[10] = 0;
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
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







-
-
+
+




-
+


-
+

-
+







        @ <p>Add attachment "%h(zFile)"
      }
      @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
      @ (rid %d(rid)) by
      hyperlink_to_user(zUser,zDate," on");
      hyperlink_to_date(zDate, ".</p>");
    }else{
      content_get(rid, &content);
      if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
      pTicket = manifest_get(rid, CFTYPE_TICKET);
      if( pTicket ){
        @
        @ <p>Ticket change
        @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
        @ (rid %d(rid)) by
        hyperlink_to_user(m.zUser,zDate," on");
        hyperlink_to_user(pTicket->zUser,zDate," on");
        hyperlink_to_date(zDate, ":");
        @ </p>
        ticket_output_change_artifact(&m);
        ticket_output_change_artifact(pTicket);
      }
      manifest_clear(&m);
      manifest_destroy(pTicket);
    }
  }
  db_finalize(&q);
  style_footer_cmdref("timeline",0);
}

/*

Changes to src/update.c.

277
278
279
280
281
282
283

284
285
286
287
288
289
290
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291







+







  */
  if( nochangeFlag ){
    db_end_transaction(1);  /* With --nochange, rollback changes */
  }else{
    if( g.argc<=3 ){
      /* All files updated.  Shift the current checkout to the target. */
      db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
      checkout_set_all_exe(vid);
      manifest_to_disk(tid);
      db_lset_int("checkout", tid);
    }else{
      /* A subset of files have been checked out.  Keep the current
      ** checkout unchanged. */
      db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
    }
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
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







-
-
-
+
+
+










-
+

-
-
-
-
-
+
+
+
+
+
+



-
+







*/
int historical_version_of_file(
  const char *revision,    /* The checkin containing the file */
  const char *file,        /* Full treename of the file */
  Blob *content,           /* Put the content here */
  int errCode              /* Error code if file not found.  Panic if 0. */
){
  Blob mfile;
  Manifest m;
  int i, rid=0;
  Manifest *pManifest;
  ManifestFile *pFile;
  int rid=0;
  
  if( revision ){
    rid = name_to_rid(revision);
  }else{
    rid = db_lget_int("checkout", 0);
  }
  if( !is_a_version(rid) ){
    if( errCode>0 ) return errCode;
    fossil_fatal("no such checkin: %s", revision);
  }
  content_get(rid, &mfile);
  pManifest = manifest_get(rid, CFTYPE_MANIFEST);
  
  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);
        manifest_clear(&m);
  if( pManifest ){
    manifest_file_rewind(pManifest);
    while( (pFile = manifest_file_next(pManifest,0))!=0 ){
      if( strcmp(pFile->zName, file)==0 ){
        rid = uuid_to_rid(pFile->zUuid, 0);
        manifest_destroy(pManifest);
        return content_get(rid, content);
      }
    }
    manifest_clear(&m);
    manifest_destroy(pManifest);
    if( errCode<=0 ){
      fossil_fatal("file %s does not exist in checkin: %s", file, revision);
    }
  }else if( errCode<=0 ){
    fossil_panic("could not parse manifest for checkin: %s", revision);
  }
  return errCode;
384
385
386
387
388
389
390

391
392
393
394

395
396
397
398
399
400
401
386
387
388
389
390
391
392
393
394
395
396

397
398
399
400
401
402
403
404







+



-
+







      blob_reset(&fname);
    }
  }else{
    int vid;
    vid = db_lget_int("checkout", 0);
    vfile_check_signature(vid, 0);
    db_multi_exec(
      "DELETE FROM vmerge;"
      "INSERT INTO torevert "
      "SELECT pathname"
      "  FROM vfile "
      " WHERE chnged OR deleted OR rid=0 OR pathname!=origname"
      " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
    );
  }
  blob_zero(&record);
  db_prepare(&q, "SELECT name FROM torevert");
  while( db_step(&q)==SQLITE_ROW ){
    zFile = db_column_text(&q, 0);
    if( zRevision!=0 ){

Changes to src/vfile.c.

83
84
85
86
87
88
89
90

91
92
93
94
95
96
97

98
99
100


101

102
103
104


105
106
107
108
109
110

111
112
113
114

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


127
128
129
130
131
132





133
134
135
136

137
138
139
140
141
142
143
83
84
85
86
87
88
89

90





91

92
93

94
95
96

97

98
99
100
101
102
103
104
105
106

107




108












109
110






111
112
113
114
115



116
117
118
119
120
121
122
123
124







-
+
-
-
-
-
-

-
+

-

+
+
-
+
-


+
+





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

+







        fossil_panic("bad object id: %d", rid);
      }
    }
  }
}

/*
** Build a catalog of all files in a baseline.
** Build a catalog of all files in a checkin.
** We scan the baseline file for lines of the form:
**
**     F NAME UUID
**
** Each such line makes an entry in the VFILE table.
*/
void vfile_build(int vid, Blob *p){
void vfile_build(int vid){
  int rid;
  char *zName, *zUuid;
  Stmt ins;
  Manifest *p;
  ManifestFile *pFile;
  Blob line, token, name, uuid;

  int seenHeader = 0;
  db_begin_transaction();
  vfile_verify_not_phantom(vid, 0, 0);
  p = manifest_get(vid, CFTYPE_MANIFEST);
  if( p==0 ) return;
  db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
  db_prepare(&ins,
    "INSERT INTO vfile(vid,rid,mrid,pathname) "
    " VALUES(:vid,:id,:id,:name)");
  db_bind_int(&ins, ":vid", vid);
  while( blob_line(p, &line) ){
  manifest_file_rewind(p);
    char *z = blob_buffer(&line);
    if( z[0]=='-' ){
      if( seenHeader ) break;
      while( blob_line(p, &line)>2 ){}
  while( (pFile = manifest_file_next(p,0))!=0 ){
      if( blob_line(p, &line)==0 ) break;
    }
    seenHeader = 1;
    if( z[0]!='F' || z[1]!=' ' ) continue;
    blob_token(&line, &token);  /* Skip the "F" token */
    if( blob_token(&line, &name)==0 ) break;
    if( blob_token(&line, &uuid)==0 ) break;
    zName = blob_str(&name);
    defossilize(zName);
    zUuid = blob_str(&uuid);
    rid = uuid_to_rid(zUuid, 0);
    vfile_verify_not_phantom(rid, zName, zUuid);
    rid = uuid_to_rid(pFile->zUuid, 0);
    vfile_verify_not_phantom(rid, pFile->zName, pFile->zUuid);
    if( rid>0 && file_is_simple_pathname(zName) ){
      db_bind_int(&ins, ":id", rid);
      db_bind_text(&ins, ":name", zName);
      db_step(&ins);
      db_reset(&ins);
    }
    db_bind_int(&ins, ":id", rid);
    db_bind_text(&ins, ":name", pFile->zName);
    db_step(&ins);
    db_reset(&ins);
  }
    blob_reset(&name);
    blob_reset(&uuid);
  }
  db_finalize(&ins);
  manifest_destroy(p);
  db_end_transaction(0);
}

/*
** Check the file signature of the disk image for every VFILE of vid.
**
** Set the VFILE.CHNGED field on every file that has changed.  Also 
367
368
369
370
371
372
373

374
375
376
377
378
379
380
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362







+







        md5sum_step_text(" 0\n", -1);
        continue;
      }
      fseek(in, 0L, SEEK_END);
      sprintf(zBuf, " %ld\n", ftell(in));
      fseek(in, 0L, SEEK_SET);
      md5sum_step_text(zBuf, -1);
      /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
      for(;;){
        int n;
        n = fread(zBuf, 1, sizeof(zBuf), in);
        if( n<=0 ) break;
        md5sum_step_text(zBuf, n);
      }
      fclose(in);
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
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







+
















-
-
-
+
+
+
+







-
-
+
+


-
-
-
+
+
+
+







-
-
+
+




-
+







  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    int rid = db_column_int(&q, 1);
    md5sum_step_text(zName, -1);
    content_get(rid, &file);
    sprintf(zBuf, " %d\n", blob_size(&file));
    md5sum_step_text(zBuf, -1);
    /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
    md5sum_step_blob(&file);
    blob_reset(&file);
  }
  db_finalize(&q);
  md5sum_finish(pOut);
}

/*
** Compute an aggregate MD5 checksum over the repository image of every
** file in manifest vid.  The file names are part of the checksum.
** Return the resulting checksum in blob pOut.
**
** If pManOut is not NULL then fill it with the checksum found in the
** "R" card near the end of the manifest.  
*/
void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){
  int i, fid;
  Blob file, mfile;
  Manifest m;
  int fid;
  Blob file;
  Manifest *pManifest;
  ManifestFile *pFile;
  char zBuf[100];

  blob_zero(pOut);
  if( pManOut ){
    blob_zero(pManOut);
  }
  db_must_be_within_tree();
  content_get(vid, &mfile);
  if( manifest_parse(&m, &mfile)==0 ){
  pManifest = manifest_get(vid, CFTYPE_MANIFEST);
  if( pManifest==0 ){
    fossil_panic("manifest file (%d) is malformed", vid);
  }
  for(i=0; i<m.nFile; i++){
    fid = uuid_to_rid(m.aFile[i].zUuid, 0);
    md5sum_step_text(m.aFile[i].zName, -1);
  manifest_file_rewind(pManifest);
  while( (pFile = manifest_file_next(pManifest,0))!=0 ){
    fid = uuid_to_rid(pFile->zUuid, 0);
    md5sum_step_text(pFile->zName, -1);
    content_get(fid, &file);
    sprintf(zBuf, " %d\n", blob_size(&file));
    md5sum_step_text(zBuf, -1);
    md5sum_step_blob(&file);
    blob_reset(&file);
  }
  if( pManOut ){
    if( m.zRepoCksum ){
      blob_append(pManOut, m.zRepoCksum, -1);
    if( pManifest->zRepoCksum ){
      blob_append(pManOut, pManifest->zRepoCksum, -1);
    }else{
      blob_zero(pManOut);
    }
  }
  manifest_clear(&m);
  manifest_destroy(pManifest);
  md5sum_finish(pOut);
}

/*
** COMMAND: test-agg-cksum
*/
void test_agg_cksum_cmd(void){

Changes to src/wiki.c.

125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
125
126
127
128
129
130
131

132
133
134
135
136
137
138
139







-
+







** URL: /wiki?name=PAGENAME
*/
void wiki_page(void){
  char *zTag;
  int rid = 0;
  int isSandbox;
  Blob wiki;
  Manifest m;
  Manifest *pWiki = 0;
  const char *zPageName;
  char *zBody = mprintf("%s","<i>Empty Page</i>");
  Stmt q;
  int cnt = 0;

  login_check_credentials();
  if( !g.okRdWiki ){ login_needed(); return; }
177
178
179
180
181
182
183
184
185
186
187
188

189
190
191
192



193
194
195
196
197
198
199
200
177
178
179
180
181
182
183





184




185
186
187

188
189
190
191
192
193
194







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







    zTag = mprintf("wiki-%s", zPageName);
    rid = db_int(0, 
      "SELECT rid FROM tagxref"
      " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
      " ORDER BY mtime DESC", zTag
    );
    free(zTag);
    memset(&m, 0, sizeof(m));
    blob_zero(&m.content);
    if( rid ){
      Blob content;
      content_get(rid, &content);
    pWiki = manifest_get(rid, CFTYPE_WIKI);
      manifest_parse(&m, &content);
      if( m.type==CFTYPE_WIKI && m.zWiki ){
        while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
        if( m.zWiki[0] ) zBody = m.zWiki;
    if( pWiki ){
      while( fossil_isspace(pWiki->zWiki[0]) ) pWiki->zWiki++;
      if( pWiki->zWiki[0] ) zBody = pWiki->zWiki;
      }
    }
  }
  if( !g.isHome ){
    if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
      style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
           g.zTop, zPageName);
    }
247
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

274
275
276
277
278
279
280
241
242
243
244
245
246
247


248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

265
266
267
268
269
270
271
272







-
-
+
-
















-
+







    @ </li>
  }
  if( cnt ){
    @ </ul>
  }
  db_finalize(&q);
 
  if( !isSandbox ){
    manifest_clear(&m);
  manifest_destroy(pWiki);
  }
  /* don'tshow the help cross reference, because we are rendering
  ** the wiki conten!
  ** style_footer_cmdref("wiki","export");
  */
  style_footer();
}

/*
** WEBPAGE: wikiedit
** URL: /wikiedit?name=PAGENAME
*/
void wikiedit_page(void){
  char *zTag;
  int rid = 0;
  int isSandbox;
  Blob wiki;
  Manifest m;
  Manifest *pWiki = 0;
  const char *zPageName;
  char *zHtmlPageName;
  int n;
  const char *z;
  char *zBody = (char*)P("w");

  if( zBody ){
300
301
302
303
304
305
306
307
308
309
310
311

312
313
314

315
316
317
318
319
320
321
322
292
293
294
295
296
297
298





299



300

301
302
303
304
305
306
307







-
-
-
-
-
+
-
-
-
+
-







      " ORDER BY mtime DESC", zTag
    );
    free(zTag);
    if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
      login_needed();
      return;
    }
    memset(&m, 0, sizeof(m));
    blob_zero(&m.content);
    if( rid && zBody==0 ){
      Blob content;
      content_get(rid, &content);
    if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
      manifest_parse(&m, &content);
      if( m.type==CFTYPE_WIKI ){
        zBody = m.zWiki;
      zBody = pWiki->zWiki;
      }
    }
  }
  if( P("submit")!=0 && zBody!=0 ){
    char *zDate;
    Blob cksum;
    int nrid;
    blob_zero(&wiki);
379
380
381
382
383
384
385
386
387

388
389
390
391
392
393
394
395
364
365
366
367
368
369
370


371

372
373
374
375
376
377
378







-
-
+
-







  @ <textarea name="w" class="wikiedit" cols="80" 
  @  rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
  @ <br />
  @ <input type="submit" name="preview" value="Preview Your Changes" />
  @ <input type="submit" name="submit" value="Apply These Changes" />
  @ <input type="submit" name="cancel" value="Cancel" />
  @ </div></form>
  if( !isSandbox ){
    manifest_clear(&m);
  manifest_destroy(pWiki);
  }
  style_footer_cmdref("wiki"," / <a href=\"wiki_rules\">wiki format</a>");
}

/*
** WEBPAGE: wikinew
** URL /wikinew
**
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
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







-

-
+








-
+
-
-
-
+
+
+

-







    return;
  }
  if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
    char *zDate;
    Blob cksum;
    int nrid;
    Blob body;
    Blob content;
    Blob wiki;
    Manifest m;
    Manifest *pWiki = 0;

    blob_zero(&body);
    if( isSandbox ){
      blob_appendf(&body, db_get("sandbox",""));
      appendRemark(&body);
      db_set("sandbox", blob_str(&body), 0);
    }else{
      login_verify_csrf_secret();
      content_get(rid, &content);
      pWiki = manifest_get(rid, CFTYPE_WIKI);
      manifest_parse(&m, &content);
      if( m.type==CFTYPE_WIKI ){
        blob_append(&body, m.zWiki, -1);
      if( pWiki ){
        blob_append(&body, pWiki->zWiki, -1);
        manifest_destroy(pWiki);
      }
      manifest_clear(&m);
      blob_zero(&wiki);
      db_begin_transaction();
      zDate = db_text(0, "SELECT datetime('now')");
      zDate[10] = 'T';
      blob_appendf(&wiki, "D %s\n", zDate);
      blob_appendf(&wiki, "L %F\n", zPageName);
      if( rid ){
614
615
616
617
618
619
620
621
622

623
624
625
626
627
628
629
595
596
597
598
599
600
601


602
603
604
605
606
607
608
609







-
-
+







**
** Show the difference between two wiki pages.
*/
void wdiff_page(void){
  char *zTitle;
  int rid1, rid2;
  const char *zPageName;
  Blob content1, content2;
  Manifest m1, m2;
  Manifest *pW1, *pW2 = 0;
  Blob w1, w2, d;

  login_check_credentials();
  rid1 = atoi(PD("a","0"));
  if( !g.okHistory ){ login_needed(); return; }
  if( rid1==0 ) fossil_redirect_home();
  rid2 = atoi(PD("b","0"));
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
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







-
+
-
-
-
+
+

-
+
-
-
-
-
+
-






+
+







      "SELECT objid FROM event JOIN tagxref ON objid=rid AND tagxref.tagid="
                        "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
      " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
      " ORDER BY event.mtime DESC LIMIT 1",
      zPageName, rid1
    );
  }
  content_get(rid1, &content1);
  pW1 = manifest_get(rid1, CFTYPE_WIKI);
  manifest_parse(&m1, &content1);
  if( m1.type!=CFTYPE_WIKI ) fossil_redirect_home();
  blob_init(&w1, m1.zWiki, -1);
  if( pW1==0 ) fossil_redirect_home();
  blob_init(&w1, pW1->zWiki, -1);
  blob_zero(&w2);
  if( rid2 ){
  if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
    content_get(rid2, &content2);
    manifest_parse(&m2, &content2);
    if( m2.type==CFTYPE_WIKI ){
      blob_init(&w2, m2.zWiki, -1);
    blob_init(&w2, pW2->zWiki, -1);
    }
  }
  blob_zero(&d);
  text_diff(&w2, &w1, &d, 5, 1);
  @ <pre>
  @ %h(blob_str(&d))
  @ </pre>
  manifest_destroy(pW1);
  manifest_destroy(pW2);
  style_footer();
}

/*
** WEBPAGE: wcontent
**
**     all=1         Show deleted pages
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
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







-
-
-
-
+
+
+
+
+









-
-
-
+
-
-
-
+
-







  if( n==0 ){
    goto wiki_cmd_usage;
  }

  if( strncmp(g.argv[2],"export",n)==0 ){
    char const *zPageName;        /* Name of the wiki page to export */
    char const *zFile;            /* Name of the output file (0=stdout) */
    int rid;                /* Artifact ID of the wiki page */
    int i;                  /* Loop counter */
    char *zBody = 0;        /* Wiki page content */
    Manifest m;             /* Parsed wiki page content */
    int rid;                      /* Artifact ID of the wiki page */
    int i;                        /* Loop counter */
    char *zBody = 0;              /* Wiki page content */
    Manifest *pWiki = 0;          /* Parsed wiki page content */

    if( (g.argc!=4) && (g.argc!=5) ){
      usage("export PAGENAME ?FILE?");
    }
    zPageName = g.argv[3];
    rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
      " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
      " ORDER BY x.mtime DESC LIMIT 1",
      zPageName 
    );
    if( rid ){
      Blob content;
      content_get(rid, &content);
    if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
      manifest_parse(&m, &content);
      if( m.type==CFTYPE_WIKI ){
        zBody = m.zWiki;
      zBody = pWiki->zWiki;
      }
    }
    if( zBody==0 ){
      fossil_fatal("wiki page [%s] not found",zPageName);
    }
    for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){}
    zFile  = (g.argc==4) ? 0 : g.argv[4];
    if( zFile ){
960
961
962
963
964
965
966
967

968

969
970
971
972
973
974
975
933
934
935
936
937
938
939

940
941
942
943
944
945
946
947
948
949







-
+

+







      }
      if( ! zF ){
        fossil_fatal("wiki export could not open output file for writing.");
      }
      fprintf(zF,"%.*s\n", i, zBody);
      if( doClose ) fclose(zF);
    }else{
	printf("%.*s\n", i, zBody);
      printf("%.*s\n", i, zBody);
    }
    manifest_destroy(pWiki);
    return;
  }else
  if( strncmp(g.argv[2],"commit",n)==0
      || strncmp(g.argv[2],"create",n)==0 ){
    char *zPageName;
    Blob content;
    if( g.argc!=4 && g.argc!=5 ){

Changes to src/xfer.c.

38
39
40
41
42
43
44
























































































































45
46
47
48
49
50
51
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171







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







  int nDeltaSent;     /* Number of deltas sent */
  int nFileRcvd;      /* Number of files received */
  int nDeltaRcvd;     /* Number of deltas received */
  int nDanglingFile;  /* Number of dangling deltas received */
  int mxSend;         /* Stop sending "file" with pOut reaches this size */
};

/*
** COMMAND: callhook
** %fossil callhook PUSHHOOKPATTERN ?--force|-f?
**
** Call the push hook command on a server, which will normally be called
** after a client push (<a>setting</a> push-hook-cmd).
**
** If --force is used, the given pattern is not checked against the
** configuration (<a>setting</a> push-hook-pattern-server).
**
** This command only works on the server side, it does not send a message
** from a client, but executes the hook directly on the server.
**
** See also <a>push</a>, <a>sync</a>, <a>setting</a>
*/
void callhook_cmd(void){
  int forceFlag = find_option("force","f",0)!=0;

  db_open_config(1);
  db_find_and_open_repository(0);
  if( (g.argc!=3) || (!g.argv[2]) || (!g.argv[2][0]) ){
    usage("PUSHHOOKPATTERN ?--force?");
  }
  if( !forceFlag ){
    const char *zPushHookPattern = db_get("push-hook-pattern-server", "");
    int lenPushHookPattern = (zPushHookPattern && zPushHookPattern[0])
                              ? strlen(zPushHookPattern) : 0;
    if(    (!lenPushHookPattern)
        || memcmp(g.argv[2], zPushHookPattern, lenPushHookPattern)
    ){
      fossil_fatal("push hook pattern '%s' doesn't match configuration '%s'\n",
                     g.argv[2],zPushHookPattern);
    }
  }
  post_push_hook(g.argv[2],'C');
}

/*
** Let a server-side external agent know that a push has completed. /fatman
** The second argument controls, how the command is called:
**   P - client request with pushed files
**   F - client request without pushed files(FORCE!)
**   C - server side command line activation
*/
void post_push_hook(char const * const zPushHookLine, const char requestType){
  /*
  ** TO DO: get the string cmd from a config file? Or the database local
  ** settings, as someone suggested? Ditto output and error logs. /fatman
  */
  const char *zCmd = db_get("push-hook-cmd", "");
  int allowForced = db_get_boolean("push-hook-force", 0);
  const char *zHookPriv = db_get("push-hook-privilege","");
  int privOk = 0;

  if( zHookPriv && *zHookPriv ){
    switch( *zHookPriv ){
      
      case 's':
        if( g.okSetup ) privOk = 1;
        break;
      case 'a':
        if( g.okAdmin ) privOk = 1;
        break;
      case 'i':
        if( g.okWrite ) privOk = 1;
        break;
      case 'o':
        if( g.okRead ) privOk = 1;
        break;
      default:
        fossil_print("Push hook wrong privilege type '%s'\n", zHookPriv);
    }
  }else{
    privOk = 1;
  }
  if( !privOk ){
    fossil_print("No privilege to activate hook!\n");
  }else if( requestType!='P' &&  requestType!='C' && requestType!='F' ){
    fossil_print("Push hook wrong request type '%c'\n", requestType);
  }else if( requestType=='F' && !allowForced ){
    fossil_print("Forced push call from client not allowed,"
                 " skipping call for '%s'\n", zPushHookLine);
  }else if( zCmd && zCmd[0] ){
    int rc;
    char * zCalledCmd;
    char * zDate;
    const char *zRnd;


    zDate = db_text(0, "SELECT strftime('%%Y%%m%%d%%H%%M%%f','now')");
    zRnd = db_text(0, "SELECT lower(hex(randomblob(6)))");

    zCalledCmd = mprintf("%s %s-%s %s >hook-log-%s-%s 2>&1",zCmd,zDate,zRnd,zPushHookLine,zDate,zRnd);
    { /* remove newlines from command */
      char *zSrc, *zDest;

      for (zSrc=zDest=zCalledCmd;;zSrc++){
        switch( *zSrc ){
          case '\0':
            *zDest=0;
            break;
          default:
            *zDest++ = *zSrc;
            /* fall through is intended! */
          case '\n':
            continue;
        }
        break;
      }
    }
    rc = system(zCalledCmd);
    if (rc != 0) {
      fossil_print("The post-push-hook command '%s' failed.", zCalledCmd);
    }
    free(zCalledCmd);
    free(zDate);
  }else{
    fossil_print("No push hook configured, skipping call for '%s'\n", zPushHookLine);
  }
}

/*
** The input blob contains a UUID.  Convert it into a record ID.
** Create a phantom record if no prior record exists and
** phantomize is true.
**
** Compare to uuid_to_rid().  This routine takes a blob argument
70
71
72
73
74
75
76


77





78
79
80
81
82
83
84
190
191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
206
207
208
209
210







+
+
-
+
+
+
+
+







}

/*
** Remember that the other side of the connection already has a copy
** of the file rid.
*/
static void remote_has(int rid){
  if( rid ){
    static Stmt q;
  if( rid ) db_multi_exec("INSERT OR IGNORE INTO onremote VALUES(%d)", rid);
    db_static_prepare(&q, "INSERT OR IGNORE INTO onremote VALUES(:r)");
    db_bind_int(&q, ":r", rid);
    db_step(&q);
    db_reset(&q);
  }
}

/*
** The aToken[0..nToken-1] blob array is a parse of a "file" line 
** message.  This routine finishes parsing that message and does
** a record insert of the file.
**
93
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

120
121













122
123
124
125
126
127
128
219
220
221
222
223
224
225

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

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267







-
+


















-
+


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







**
** If any error occurs, write a message into pErr which has already
** be initialized to an empty string.
**
** Any artifact successfully received by this routine is considered to
** be public and is therefore removed from the "private" table.
*/
static void xfer_accept_file(Xfer *pXfer){
static void xfer_accept_file(Xfer *pXfer, int cloneFlag){
  int n;
  int rid;
  int srcid = 0;
  Blob content, hash;
  
  if( pXfer->nToken<3 
   || pXfer->nToken>4
   || !blob_is_uuid(&pXfer->aToken[1])
   || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n)
   || n<0
   || (pXfer->nToken==4 && !blob_is_uuid(&pXfer->aToken[2]))
  ){
    blob_appendf(&pXfer->err, "malformed file line");
    return;
  }
  blob_zero(&content);
  blob_zero(&hash);
  blob_extract(pXfer->pIn, n, &content);
  if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
  if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
    /* Ignore files that have been shunned */
    return;
  }
  if( cloneFlag ){
    if( pXfer->nToken==4 ){
      srcid = rid_from_uuid(&pXfer->aToken[2], 1);
      pXfer->nDeltaRcvd++;
    }else{
      srcid = 0;
      pXfer->nFileRcvd++;
    }
    rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid);
    remote_has(rid);
    blob_reset(&content);
    return;
  }
  if( pXfer->nToken==4 ){
    Blob src, next;
    srcid = rid_from_uuid(&pXfer->aToken[2], 1);
    if( content_get(srcid, &src)==0 ){
      rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid);
      pXfer->nDanglingFile++;
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
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







-
+



+










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








/*
** Check to see if the number of unclustered entries is greater than
** 100 and if it is, form a new cluster.  Unclustered phantoms do not
** count toward the 100 total.  And phantoms are never added to a new
** cluster.
*/
static void create_cluster(void){
void create_cluster(void){
  Blob cluster, cksum;
  Stmt q;
  int nUncl;
  int nRow = 0;

  /* We should not ever get any private artifacts in the unclustered table.
  ** But if we do (because of a bug) now is a good time to delete them. */
  db_multi_exec(
    "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)"
  );

  nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/"
                    " WHERE NOT EXISTS(SELECT 1 FROM phantom"
                                      " WHERE rid=unclustered.rid)");
  if( nUncl<100 ){
  if( nUncl>=100 ){
    return;
  }
  blob_zero(&cluster);
  db_prepare(&q, "SELECT uuid FROM unclustered, blob"
                 " WHERE NOT EXISTS(SELECT 1 FROM phantom"
                 "                   WHERE rid=unclustered.rid)"
                 "   AND unclustered.rid=blob.rid"
                 "   AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
                 " ORDER BY 1");
  while( db_step(&q)==SQLITE_ROW ){
    blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0));
  }
  db_finalize(&q);
  md5sum_blob(&cluster, &cksum);
  blob_appendf(&cluster, "Z %b\n", &cksum);
  blob_reset(&cksum);
    blob_zero(&cluster);
    db_prepare(&q, "SELECT uuid FROM unclustered, blob"
                   " WHERE NOT EXISTS(SELECT 1 FROM phantom"
                   "                   WHERE rid=unclustered.rid)"
                   "   AND unclustered.rid=blob.rid"
                   "   AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
                   " ORDER BY 1");
    while( db_step(&q)==SQLITE_ROW ){
      blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0));
      nRow++;
      if( nRow>=800 && nUncl>nRow+100 ){
        md5sum_blob(&cluster, &cksum);
        blob_appendf(&cluster, "Z %b\n", &cksum);
        blob_reset(&cksum);
        content_put(&cluster, 0, 0);
        blob_reset(&cluster);
        nUncl -= nRow;
        nRow = 0;
      }
    }
    db_finalize(&q);
    db_multi_exec("DELETE FROM unclustered");
    if( nRow>0 ){
      md5sum_blob(&cluster, &cksum);
      blob_appendf(&cluster, "Z %b\n", &cksum);
      blob_reset(&cksum);
  db_multi_exec("DELETE FROM unclustered");
  content_put(&cluster, 0, 0);
  blob_reset(&cluster);
      content_put(&cluster, 0, 0);
      blob_reset(&cluster);
    }
  }
}

/*
** Send an igot message for every entry in unclustered table.
** Return the number of cards sent.
*/
static int send_unclustered(Xfer *pXfer){
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
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







+
+
+










-
+






-
-


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














-
+







  Xfer xfer;
  int deltaFlag = 0;
  int isClone = 0;
  int nGimme = 0;
  int size;
  int recvConfig = 0;
  char *zNow;
  const char *zPushHookPattern = db_get("push-hook-pattern-server", "");
  int lenPushHookPattern = (zPushHookPattern && zPushHookPattern[0])
                            ? strlen(zPushHookPattern) : 0;

  if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
     fossil_redirect_home();
  }
  memset(&xfer, 0, sizeof(xfer));
  blobarray_zero(xfer.aToken, count(xfer.aToken));
  cgi_set_content_type(g.zContentType);
  blob_zero(&xfer.err);
  xfer.pIn = &g.cgiIn;
  xfer.pOut = cgi_output_blob();
  xfer.mxSend = db_get_int("max-download", 5000000);
  xfer.mxSend = db_get_int("max-download", 20000000);
  g.xferPanic = 1;

  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
  );
  zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
  @ # timestamp %s(zNow)
  manifest_crosslink_begin();
  while( blob_line(xfer.pIn, &xfer.line) ){
    if( blob_buffer(&xfer.line)[0]=='#' ) continue;
    if( blob_buffer(&xfer.line)[0]=='#' ){
      if(    lenPushHookPattern
          && blob_buffer(&xfer.line)[1]
          && blob_buffer(&xfer.line)[2]
          && (0 == memcmp(blob_buffer(&xfer.line)+2,
                          zPushHookPattern, lenPushHookPattern))
      ){
        post_push_hook(blob_buffer(&xfer.line)+2,blob_buffer(&xfer.line)[1]);
      }
      continue;
    }
    xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));

    /*   file UUID SIZE \n CONTENT
    **   file UUID DELTASRC SIZE \n CONTENT
    **
    ** Accept a file from the client.
    */
    if( blob_eq(&xfer.aToken[0], "file") ){
      if( !isPush ){
        cgi_reset_content();
        @ error not\sauthorized\sto\swrite
        nErr++;
        break;
      }
      xfer_accept_file(&xfer);
      xfer_accept_file(&xfer, 0);
      if( blob_size(&xfer.err) ){
        cgi_reset_content();
        @ error %T(blob_str(&xfer.err))
        nErr++;
        break;
      }
    }else
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
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







-
+




+








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







          }
        }else{
          isPush = 1;
        }
      }
    }else

    /*    clone
    /*    clone   ?PROTOCOL-VERSION?  ?SEQUENCE-NUMBER?
    **
    ** The client knows nothing.  Tell all.
    */
    if( blob_eq(&xfer.aToken[0], "clone") ){
      int iVers;
      login_check_credentials();
      if( !g.okClone ){
        cgi_reset_content();
        @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
        @ error not\sauthorized\sto\sclone
        nErr++;
        break;
      }
      if( xfer.nToken==3
       && blob_is_int(&xfer.aToken[1], &iVers)
       && iVers>=2
      ){
        int seqno, max;
        blob_is_int(&xfer.aToken[2], &seqno);
        max = db_int(0, "SELECT max(rid) FROM blob");
        while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){
          send_file(&xfer, seqno, 0, 1);
          seqno++;
        }
        if( seqno>=max ) seqno = 0;
        @ clone_seqno %d(seqno)
      }else{
      isClone = 1;
      isPull = 1;
      deltaFlag = 1;
        isClone = 1;
        isPull = 1;
        deltaFlag = 1;
      }
      @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
    }else

    /*    login  USER  NONCE  SIGNATURE
    **
    ** Check for a valid login.  This has to happen before anything else.
    ** The client can send multiple logins.  Permissions are cumulative.
872
873
874
875
876
877
878








879
880
881
882
883
884
885
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071







+
+
+
+
+
+
+
+







    create_cluster();
    send_unclustered(&xfer);
  }
  if( recvConfig ){
    configure_finalize_receive();
  }
  manifest_crosslink_end();

  /* Send the server timestamp last, in case prior processing happened
  ** to use up a significant fraction of our time window.
  */
  zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
  @ # timestamp %s(zNow)
  free(zNow);

  db_end_transaction(0);
}

/*
** COMMAND: test-xfer
**
** This command is used for debugging the server.  There is a single
941
942
943
944
945
946
947

948
949
950



951
952



953
954
955
956
957
958
959
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







+



+
+
+


+
+
+







  int size;               /* Size of a config value */
  int nFileSend = 0;
  int origConfigRcvMask;  /* Original value of configRcvMask */
  int nFileRecv;          /* Number of files received */
  int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
  const char *zCookie;    /* Server cookie */
  int nSent, nRcvd;       /* Bytes sent and received (after compression) */
  int cloneSeqno = 1;     /* Sequence number for clones */
  Blob send;              /* Text we are sending to the server */
  Blob recv;              /* Reply we got back from the server */
  Xfer xfer;              /* Transfer data */
  int pctDone;            /* Percentage done with a message */
  int lastPctDone = -1;   /* Last displayed pctDone */
  double rArrivalTime;    /* Time at which a message arrived */
  const char *zSCode = db_get("server-code", "x");
  const char *zPCode = db_get("project-code", 0);
  const char *zPushHookPattern = db_get("push-hook-pattern-client", "");
  int allowForced = db_get_boolean("push-hook-force", 0);


  if( db_get_boolean("dont-push", 0) ) pushFlag = 0;
  if( pushFlag + pullFlag + cloneFlag == 0 
     && configRcvMask==0 && configSendMask==0 ) return;

  transport_stats(0, 0, 1);
  socket_global_init();
975
976
977
978
979
980
981
982

983
984
985
986

987
988
989
990
991
992
993
1168
1169
1170
1171
1172
1173
1174

1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187







-
+




+







  blob_zero(&xfer.line);
  origConfigRcvMask = 0;

  /*
  ** Always begin with a clone, pull, or push message
  */
  if( cloneFlag ){
    blob_appendf(&send, "clone\n");
    blob_appendf(&send, "clone 2 %d\n", cloneSeqno);
    pushFlag = 0;
    pullFlag = 0;
    nCardSent++;
    /* TBD: Request all transferable configuration values */
    content_enable_dephantomize(0);
  }else if( pullFlag ){
    blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
    nCardSent++;
  }
  if( pushFlag ){
    blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
    nCardSent++;
1007
1008
1009
1010
1011
1012
1013
1014

1015
1016
1017
1018
1019
1020
1021
1201
1202
1203
1204
1205
1206
1207

1208
1209
1210
1211
1212
1213
1214
1215







-
+







    if( zCookie ){
      blob_appendf(&send, "cookie %s\n", zCookie);
    }
    
    /* Generate gimme cards for phantoms and leaf cards
    ** for all leaves.
    */
    if( pullFlag || cloneFlag ){
    if( pullFlag || (cloneFlag && cloneSeqno==1) ){
      request_phantoms(&xfer, mxPhantomReq);
    }
    if( pushFlag ){
      send_unsent(&xfer);
      nCardSent += send_unclustered(&xfer);
    }

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
1250
1251
1252
1253
1254
1255
1256

1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299


1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316


1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328

1329
1330
1331
1332
1333
1334
1335
1336







-
+








+
+
+


+

+
















+
+
+
+






-
-
+
+













+
+
-
-
+
+
+
+








-
+







    */
    zRandomness = db_text(0, "SELECT hex(randomblob(20))");
    blob_appendf(&send, "# %s\n", zRandomness);
    free(zRandomness);

    /* Exchange messages with the server */
    nFileSend = xfer.nFileSent + xfer.nDeltaSent;
    fossil_print(zValueFormat, "Send:",
    fossil_print(zValueFormat, "Sent:",
                 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
                 xfer.nFileSent, xfer.nDeltaSent);
    nCardSent = 0;
    nCardRcvd = 0;
    xfer.nFileSent = 0;
    xfer.nDeltaSent = 0;
    xfer.nGimmeSent = 0;
    xfer.nIGotSent = 0;
    if( !g.cgiOutput && !g.fQuiet ){
      printf("waiting for server...");
    }
    fflush(stdout);
    http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
    lastPctDone = -1;
    blob_reset(&send);
    rArrivalTime = db_double(0.0, "SELECT julianday('now')");

    /* Begin constructing the next message (which might never be
    ** sent) by beginning with the pull or push cards
    */
    if( pullFlag ){
      blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
      nCardSent++;
    }
    if( pushFlag ){
      blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
      nCardSent++;
    }
    go = 0;

    /* Process the reply that came back from the server */
    while( blob_line(&recv, &xfer.line) ){
      if( g.fHttpTrace ){
        printf("\rGOT: %.*s", (int)blob_size(&xfer.line),
                              blob_buffer(&xfer.line));
      }
      if( blob_buffer(&xfer.line)[0]=='#' ){
        const char *zLine = blob_buffer(&xfer.line);
        if( memcmp(zLine, "# timestamp ", 12)==0 ){
          char zTime[20];
          double rDiff;
          sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]);
          rDiff = db_double(9e99, "SELECT julianday('%q') - julianday('now')",
                            zTime);
          rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g",
                            zTime, rArrivalTime);
          if( rDiff<0.0 ) rDiff = -rDiff;
          if( rDiff>9e98 ) rDiff = 0.0;
          if( (rDiff*24.0*3600.0)>=60.0 ){
            fossil_warning("*** time skew *** server time differs by %s",
                           db_timespan_name(rDiff));
            g.clockSkewSeen = 1;
          }
        }
        continue;
      }
      xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
      nCardRcvd++;
      if( !g.cgiOutput && !g.fQuiet ){
        pctDone = (recv.iCursor*100)/recv.nUsed;
        if( pctDone!=lastPctDone ){
        printf("\r%d", nCardRcvd);
        fflush(stdout);
          printf("\rprocessed: %d%%         ", pctDone);
          lastPctDone = pctDone;
          fflush(stdout);
        }
      }

      /*   file UUID SIZE \n CONTENT
      **   file UUID DELTASRC SIZE \n CONTENT
      **
      ** Receive a file transmitted from the server.
      */
      if( blob_eq(&xfer.aToken[0],"file") ){
        xfer_accept_file(&xfer);
        xfer_accept_file(&xfer, cloneFlag);
      }else

      /*   gimme UUID
      **
      ** Server is requesting a file.  If the file is a manifest, assume
      ** that the server will also want to know all of the content files
      ** associated with the manifest and send those too.
1175
1176
1177
1178
1179
1180
1181
1182

1183
1184
1185
1186
1187
1188
1189
1382
1383
1384
1385
1386
1387
1388

1389
1390
1391
1392
1393
1394
1395
1396







-
+







        if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
          fossil_fatal("server loop");
        }
        if( zPCode==0 ){
          zPCode = mprintf("%b", &xfer.aToken[2]);
          db_set("project-code", zPCode, 0);
        }
        blob_appendf(&send, "clone\n");
        blob_appendf(&send, "clone 2 %d\n", cloneSeqno);
        nCardSent++;
      }else
      
      /*   config NAME SIZE \n CONTENT
      **
      ** Receive a configuration value from the server.
      */
1234
1235
1236
1237
1238
1239
1240











1241
1242
1243
1244
1245
1246
1247
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465







+
+
+
+
+
+
+
+
+
+
+







      **
      ** Each cookie received overwrites the prior cookie from the
      ** same server.
      */
      if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
        db_set("cookie", blob_str(&xfer.aToken[1]), 0);
      }else

      /*    clone_seqno N
      **
      ** When doing a clone, the server tries to send all of its artifacts
      ** in sequence.  This card indicates the sequence number of the next
      ** blob that needs to be sent.  If N<=0 that indicates that all blobs
      ** have been sent.
      */
      if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){
        blob_is_int(&xfer.aToken[1], &cloneSeqno);
      }else

      /*   message MESSAGE
      **
      ** Print a message.  Similar to "error" but does not stop processing.
      **
      ** If the "login failed" message is seen, clear the sync password prior
      ** to the next cycle.
1312
1313
1314
1315
1316
1317
1318


1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333



1334











1335
1336
1337
1338
1339
1340
1341

1342
1343
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578







+
+















+
+
+

+
+
+
+
+
+
+
+
+
+
+







+


    ** there are still phantoms, then go another round.
    */
    nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
    if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
      go = 1;
      mxPhantomReq = nFileRecv*2;
      if( mxPhantomReq<200 ) mxPhantomReq = 200;
    }else if( cloneFlag && nFileRecv>0 ){
      go = 1;
    }
    nCardRcvd = 0;
    xfer.nFileRcvd = 0;
    xfer.nDeltaRcvd = 0;
    xfer.nDanglingFile = 0;

    /* If we have one or more files queued to send, then go
    ** another round 
    */
    if( xfer.nFileSent+xfer.nDeltaSent>0 ){
      go = 1;
    }

    /* If this is a clone, the go at least two rounds */
    if( cloneFlag && nCycle==1 ) go = 1;

    /* Stop the cycle if the server sends a "clone_seqno 0" card */
    if( cloneSeqno<=0 ) go = 0;   
  };
  if( pushFlag && ( (nFileSend > 0) || allowForced ) ){
    if( zPushHookPattern && zPushHookPattern[0] ){
      blob_appendf(&send, "#%s%s\n",
                   ((nFileSend > 0)?"P":"F"), zPushHookPattern);
      fossil_print("Triggering push hook %s '%s'\n",((nFileSend > 0)?"P":"F"),zPushHookPattern);
      http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
      blob_reset(&send);
      nCardSent++;
    }
  int allowForced = db_get_boolean("push-hook-force", 0);
  }
  transport_stats(&nSent, &nRcvd, 1);
  fossil_print("Total network traffic: %d bytes sent, %d bytes received\n",
               nSent, nRcvd);
  transport_close();
  transport_global_shutdown();
  db_multi_exec("DROP TABLE onremote");
  manifest_crosslink_end();
  content_enable_dephantomize(1);
  db_end_transaction(0);
}

Changes to src/zip.c.

311
312
313
314
315
316
317
318
319
320



321
322
323
324
325
326
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
311
312
313
314
315
316
317



318
319
320
321
322
323
324
325
326
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







-
-
-
+
+
+








-

-








+
-
+

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



-
+






-


-

+







** added to as part of the zip file. It may be 0 or an empty string,
** in which case it is ignored. The intention is to create a zip which
** politely expands into a subdir instead of filling your current dir
** with source files. For example, pass a UUID or "ProjectName".
**
*/
void zip_of_baseline(int rid, Blob *pZip, const char *zDir){
  int i;
  Blob mfile, file, hash;
  Manifest m;
  Blob mfile, hash, file;
  Manifest *pManifest;
  ManifestFile *pFile;
  Blob filename;
  int nPrefix;
  
  content_get(rid, &mfile);
  if( blob_size(&mfile)==0 ){
    blob_zero(pZip);
    return;
  }
  blob_zero(&file);
  blob_zero(&hash);
  blob_copy(&file, &mfile);
  blob_zero(&filename);
  zip_open();

  if( zDir && zDir[0] ){
    blob_appendf(&filename, "%s/", zDir);
  }
  nPrefix = blob_size(&filename);

  pManifest = manifest_get(rid, CFTYPE_MANIFEST);
  if( manifest_parse(&m, &mfile) ){
  if( pManifest ){
    char *zName;
    zip_set_timedate(m.rDate);
    blob_append(&filename, "manifest", -1);
    zName = blob_str(&filename);
    zip_add_folders(zName);
    zip_add_file(zName, &file);
    sha1sum_blob(&file, &hash);
    blob_reset(&file);
    blob_append(&hash, "\n", 1);
    blob_resize(&filename, nPrefix);
    blob_append(&filename, "manifest.uuid", -1);
    zName = blob_str(&filename);
    zip_add_file(zName, &hash);
    blob_reset(&hash);
    for(i=0; i<m.nFile; i++){
      int fid = uuid_to_rid(m.aFile[i].zUuid, 0);
    zip_set_timedate(pManifest->rDate);
    if( db_get_boolean("manifest", 0) ){
      blob_append(&filename, "manifest", -1);
      zName = blob_str(&filename);
      zip_add_folders(zName);
      zip_add_file(zName, &mfile);
      sha1sum_blob(&mfile, &hash);
      blob_reset(&mfile);
      blob_append(&hash, "\n", 1);
      blob_resize(&filename, nPrefix);
      blob_append(&filename, "manifest.uuid", -1);
      zName = blob_str(&filename);
      zip_add_file(zName, &hash);
      blob_reset(&hash);
    }
    manifest_file_rewind(pManifest);
    while( (pFile = manifest_file_next(pManifest,0))!=0 ){
      int fid = uuid_to_rid(pFile->zUuid, 0);
      if( fid ){
        content_get(fid, &file);
        blob_resize(&filename, nPrefix);
        blob_append(&filename, m.aFile[i].zName, -1);
        blob_append(&filename, pFile->zName, -1);
        zName = blob_str(&filename);
        zip_add_folders(zName);
        zip_add_file(zName, &file);
        blob_reset(&file);
      }
    }
    manifest_clear(&m);
  }else{
    blob_reset(&mfile);
    blob_reset(&file);
  }
  manifest_destroy(pManifest);
  blob_reset(&filename);
  zip_close(pZip);
}

/*
** COMMAND: zip
**

Changes to win/Makefile.dmc.

37
38
39
40
41
42
43
44
45


46
47
48
49
50
51
52
37
38
39
40
41
42
43


44
45
46
47
48
49
50
51
52







-
-
+
+








all: $(APPNAME)

$(APPNAME) : translate$E mkindex$E headers fossil.res $(OBJ) $(OBJDIR)\link
	cd $(OBJDIR) 
	$(DMDIR)\bin\link @link

fossil.res:	$B\win\fossil.rc
	$(RC) $(RCFLAGS) -o$@ $**
fossil.res:	$B\win\fossil.rc VERSION.h
	$(RC) $(RCFLAGS) -o$@ $B\win\fossil.rc

$(OBJDIR)\link: $B\win\Makefile.dmc
	+echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 th th_lang > $@
	+echo fossil >> $@
	+echo fossil >> $@
	+echo $(LIBS) >> $@
	+echo. >> $@

Changes to www/fileformat.wiki.

93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108








109
110
111
112
113
114
115
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124







+









+
+
+
+
+
+
+
+







No card may be duplicated.
The entire manifest may be PGP clear-signed, but otherwise it
may contain no additional text or data beyond what is described here.

Allowed cards in the manifest are as follows:

<blockquote>
<b>B</b> <i>baseline-manifest</i><br>
<b>C</b> <i>checkin-comment</i><br>
<b>D</b> <i>time-and-date-stamp</i><br>
<b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
<b>P</b> <i>SHA1-hash</i>+<br>
<b>R</b> <i>repository-checksum</i><br>
<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name  <b>*</b> ?value?</i><br>
<b>U</b> <i>user-login</i><br>
<b>Z</b> <i>manifest-checksum</i>
</blockquote>

A manifest may optionally have a single B-card.  The B-card specifies
another manifest that serves as the "baseline" for this manifest.  A
manifest that has a B-card is called a delta-manifest and a manifest
that omits the B-card is a baseline-manifest.  The other manifest
identified by the argument of the B-card must be a baseline-manifest.
A baseline-manifest records the complete contents of a checkin.
A delta-manifest records only changes from its baseline.  

A manifest must have exactly one C-card.  The sole argument to
the C-card is a check-in comment that describes the check-in that
the manifest defines.  The check-in comment is text.  The following
escape sequences are applied to the text:
A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73).  A
newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E).  A backslash 
123
124
125
126
127
128
129
130

131
132

133
134
135
136
137
138
139
140



141

142
143
144
145


146
147
148
149
150
151
152
132
133
134
135
136
137
138

139


140
141
142
143
144
145
146
147
148
149
150
151

152
153
154
155

156
157
158
159
160
161
162
163
164







-
+
-
-
+








+
+
+
-
+



-
+
+







date and time should be in coordinated universal time (UTC).
The format is:

<blockquote>
<i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i>
</blockquote>

A manifest has zero or more F-cards.  Each F-card defines a file
A manifest has zero or more F-cards.  Each F-card identifies a file
(other than the manifest itself) which is part of the check-in that
the manifest defines.  There are two, three, or four arguments.
that is part of the check-in.  There are one, two, three, or four arguments.
The first argument
is the pathname of the file in the check-in relative to the root
of the project file hierarchy.  No ".." or "." directories are allowed
within the filename.  Space characters are escaped as in C-card
comment text.  Backslash characters and newlines are not allowed
within filenames.  The directory separator character is a forward
slash (ASCII 0x2F).  The second argument to the F-card is the
full 40-character lower-case hexadecimal SHA1 hash of the content
artifact.  The second argument is required for baseline manifests
but is optional for delta manifests.  When the second argument to the
F-card is omitted, it means that the file has been deleted relative
artifact.  The optional 3rd argument defines any special access 
to the baseline.  The optional 3rd argument defines any special access 
permissions associated with the file.  The only special code currently
defined is "x" which means that the file is executable.  All files are
always readable and writable.  This can be expressed by "w" permission
if desired but is optional.
if desired but is optional.  The file format might be extended with
new permission letters in the future.
The optional 4th argument is the name of the same file as it existed in
the parent check-in.  If the name of the file is unchanged from its
parent, then the 4th argument is omitted.

A manifest has zero or one P-cards.  Most manifests have one P-card.
The P-card has a varying number of arguments that
defines other manifests from which the current manifest
502
503
504
505
506
507
508










509
510
511
512
513
514
515
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537







+
+
+
+
+
+
+
+
+
+







<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td align=center><b>X</b></td>
<td>&nbsp;</td>
</tr>
<tr>
<td><b>B</b> <i>baseline</i></td>
<td align=center><b>X</b></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td><b>C</b> <i>comment-text</i></td>
<td align=center><b>X</b></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>