Fossil SCM

merge from trunk

wolfgang 2010-10-26 19:01 StvPrivateHook2 merge
Commit 4260bc6a221d7c1cf56dbcfca40cd81179932641
+20 -20
--- src/branch.c
+++ src/branch.c
@@ -35,12 +35,11 @@
3535
const char *zBranch; /* Name of the new branch */
3636
char *zDate; /* Date that branch was created */
3737
char *zComment; /* Check-in comment for the new branch */
3838
const char *zColor; /* Color of the new branch */
3939
Blob branch; /* manifest for the new branch */
40
- Blob parent; /* root check-in manifest */
41
- Manifest mParent; /* Parsed parent manifest */
40
+ Manifest *pParent; /* Parsed parent manifest */
4241
Blob mcksum; /* Self-checksum on the manifest */
4342
const char *zDateOvrd; /* Override date string */
4443
const char *zUserOvrd; /* Override user name */
4544
4645
noSign = find_option("nosign","",0)!=0;
@@ -71,41 +70,42 @@
7170
db_begin_transaction();
7271
rootid = name_to_rid(g.argv[4]);
7372
if( rootid==0 ){
7473
fossil_fatal("unable to locate check-in off of which to branch");
7574
}
75
+
76
+ pParent = manifest_get(rootid, CFTYPE_MANIFEST);
77
+ if( pParent==0 ){
78
+ fossil_fatal("%s is not a valid check-in", g.argv[4]);
79
+ }
7680
7781
/* Create a manifest for the new branch */
7882
blob_zero(&branch);
83
+ if( pParent->zBaseline ){
84
+ blob_appendf(&branch, "B %s\n", pParent->zBaseline);
85
+ }
7986
zComment = mprintf("Create new branch named \"%h\"", zBranch);
8087
blob_appendf(&branch, "C %F\n", zComment);
8188
zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
8289
zDate[10] = 'T';
8390
blob_appendf(&branch, "D %s\n", zDate);
8491
8592
/* Copy all of the content from the parent into the branch */
86
- content_get(rootid, &parent);
87
- manifest_parse(&mParent, &parent);
88
- if( mParent.type!=CFTYPE_MANIFEST ){
89
- fossil_fatal("%s is not a valid check-in", g.argv[4]);
90
- }
91
- for(i=0; i<mParent.nFile; ++i){
92
- if( mParent.aFile[i].zPerm[0] ){
93
- blob_appendf(&branch, "F %F %s %s\n",
94
- mParent.aFile[i].zName,
95
- mParent.aFile[i].zUuid,
96
- mParent.aFile[i].zPerm);
97
- }else{
98
- blob_appendf(&branch, "F %F %s\n",
99
- mParent.aFile[i].zName,
100
- mParent.aFile[i].zUuid);
101
- }
93
+ for(i=0; i<pParent->nFile; ++i){
94
+ blob_appendf(&branch, "F %F", pParent->aFile[i].zName);
95
+ if( pParent->aFile[i].zUuid ){
96
+ blob_appendf(&branch, " %s", pParent->aFile[i].zUuid);
97
+ if( pParent->aFile[i].zPerm[0] ){
98
+ blob_appendf(&branch, " %s", pParent->aFile[i].zPerm);
99
+ }
100
+ }
101
+ blob_append(&branch, "\n", 1);
102102
}
103103
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
104104
blob_appendf(&branch, "P %s\n", zUuid);
105
- blob_appendf(&branch, "R %s\n", mParent.zRepoCksum);
106
- manifest_clear(&mParent);
105
+ blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
106
+ manifest_destroy(pParent);
107107
108108
/* Add the symbolic branch name and the "branch" tag to identify
109109
** this as a new branch */
110110
if( zColor!=0 ){
111111
blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
112112
--- src/branch.c
+++ src/branch.c
@@ -35,12 +35,11 @@
35 const char *zBranch; /* Name of the new branch */
36 char *zDate; /* Date that branch was created */
37 char *zComment; /* Check-in comment for the new branch */
38 const char *zColor; /* Color of the new branch */
39 Blob branch; /* manifest for the new branch */
40 Blob parent; /* root check-in manifest */
41 Manifest mParent; /* Parsed parent manifest */
42 Blob mcksum; /* Self-checksum on the manifest */
43 const char *zDateOvrd; /* Override date string */
44 const char *zUserOvrd; /* Override user name */
45
46 noSign = find_option("nosign","",0)!=0;
@@ -71,41 +70,42 @@
71 db_begin_transaction();
72 rootid = name_to_rid(g.argv[4]);
73 if( rootid==0 ){
74 fossil_fatal("unable to locate check-in off of which to branch");
75 }
 
 
 
 
 
76
77 /* Create a manifest for the new branch */
78 blob_zero(&branch);
 
 
 
79 zComment = mprintf("Create new branch named \"%h\"", zBranch);
80 blob_appendf(&branch, "C %F\n", zComment);
81 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
82 zDate[10] = 'T';
83 blob_appendf(&branch, "D %s\n", zDate);
84
85 /* Copy all of the content from the parent into the branch */
86 content_get(rootid, &parent);
87 manifest_parse(&mParent, &parent);
88 if( mParent.type!=CFTYPE_MANIFEST ){
89 fossil_fatal("%s is not a valid check-in", g.argv[4]);
90 }
91 for(i=0; i<mParent.nFile; ++i){
92 if( mParent.aFile[i].zPerm[0] ){
93 blob_appendf(&branch, "F %F %s %s\n",
94 mParent.aFile[i].zName,
95 mParent.aFile[i].zUuid,
96 mParent.aFile[i].zPerm);
97 }else{
98 blob_appendf(&branch, "F %F %s\n",
99 mParent.aFile[i].zName,
100 mParent.aFile[i].zUuid);
101 }
102 }
103 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
104 blob_appendf(&branch, "P %s\n", zUuid);
105 blob_appendf(&branch, "R %s\n", mParent.zRepoCksum);
106 manifest_clear(&mParent);
107
108 /* Add the symbolic branch name and the "branch" tag to identify
109 ** this as a new branch */
110 if( zColor!=0 ){
111 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
112
--- src/branch.c
+++ src/branch.c
@@ -35,12 +35,11 @@
35 const char *zBranch; /* Name of the new branch */
36 char *zDate; /* Date that branch was created */
37 char *zComment; /* Check-in comment for the new branch */
38 const char *zColor; /* Color of the new branch */
39 Blob branch; /* manifest for the new branch */
40 Manifest *pParent; /* Parsed parent manifest */
 
41 Blob mcksum; /* Self-checksum on the manifest */
42 const char *zDateOvrd; /* Override date string */
43 const char *zUserOvrd; /* Override user name */
44
45 noSign = find_option("nosign","",0)!=0;
@@ -71,41 +70,42 @@
70 db_begin_transaction();
71 rootid = name_to_rid(g.argv[4]);
72 if( rootid==0 ){
73 fossil_fatal("unable to locate check-in off of which to branch");
74 }
75
76 pParent = manifest_get(rootid, CFTYPE_MANIFEST);
77 if( pParent==0 ){
78 fossil_fatal("%s is not a valid check-in", g.argv[4]);
79 }
80
81 /* Create a manifest for the new branch */
82 blob_zero(&branch);
83 if( pParent->zBaseline ){
84 blob_appendf(&branch, "B %s\n", pParent->zBaseline);
85 }
86 zComment = mprintf("Create new branch named \"%h\"", zBranch);
87 blob_appendf(&branch, "C %F\n", zComment);
88 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
89 zDate[10] = 'T';
90 blob_appendf(&branch, "D %s\n", zDate);
91
92 /* Copy all of the content from the parent into the branch */
93 for(i=0; i<pParent->nFile; ++i){
94 blob_appendf(&branch, "F %F", pParent->aFile[i].zName);
95 if( pParent->aFile[i].zUuid ){
96 blob_appendf(&branch, " %s", pParent->aFile[i].zUuid);
97 if( pParent->aFile[i].zPerm[0] ){
98 blob_appendf(&branch, " %s", pParent->aFile[i].zPerm);
99 }
100 }
101 blob_append(&branch, "\n", 1);
 
 
 
 
 
 
 
102 }
103 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
104 blob_appendf(&branch, "P %s\n", zUuid);
105 blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
106 manifest_destroy(pParent);
107
108 /* Add the symbolic branch name and the "branch" tag to identify
109 ** this as a new branch */
110 if( zColor!=0 ){
111 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
112
+61 -40
--- src/browse.c
+++ src/browse.c
@@ -71,19 +71,24 @@
7171
** There is no hyperlink on the file element of the path.
7272
**
7373
** The computed string is appended to the pOut blob. pOut should
7474
** have already been initialized.
7575
*/
76
-void hyperlinked_path(const char *zPath, Blob *pOut){
76
+void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
7777
int i, j;
7878
char *zSep = "";
7979
8080
for(i=0; zPath[i]; i=j){
8181
for(j=i; zPath[j] && zPath[j]!='/'; j++){}
8282
if( zPath[j] && g.okHistory ){
83
- blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>",
84
- zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
83
+ if( zCI ){
84
+ blob_appendf(pOut, "%s<a href=\"%s/dir?ci=%S&amp;name=%#T\">%#h</a>",
85
+ zSep, g.zBaseURL, zCI, j, zPath, j-i, &zPath[i]);
86
+ }else{
87
+ blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>",
88
+ zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
89
+ }
8590
}else{
8691
blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
8792
}
8893
zSep = "/";
8994
while( zPath[j]=='/' ){ j++; }
@@ -99,21 +104,21 @@
99104
** name=PATH Directory to display. Required.
100105
** ci=LABEL Show only files in this check-in. Optional.
101106
*/
102107
void page_dir(void){
103108
const char *zD = P("name");
109
+ int nD = zD ? strlen(zD)+1 : 0;
104110
int mxLen;
105111
int nCol, nRow;
106112
int cnt, i;
107113
char *zPrefix;
108114
Stmt q;
109115
const char *zCI = P("ci");
110116
int rid = 0;
111117
char *zUuid = 0;
112
- Blob content;
113118
Blob dirname;
114
- Manifest m;
119
+ Manifest *pM = 0;
115120
const char *zSubdirLink;
116121
117122
login_check_credentials();
118123
if( !g.okHistory ){ login_needed(); return; }
119124
style_header("File List");
@@ -126,25 +131,24 @@
126131
/* If a specific check-in is requested, fetch and parse it. If the
127132
** specific check-in does not exist, clear zCI. zCI==0 will cause all
128133
** files from all check-ins to be displayed.
129134
*/
130135
if( zCI ){
131
- if( (rid = name_to_rid(zCI))==0 || content_get(rid, &content)==0 ){
132
- zCI = 0; /* No artifact named zCI exists */
133
- }else if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){
134
- zCI = 0; /* The artifact exists but is not a manifest */
136
+ pM = manifest_get_by_name(zCI, &rid);
137
+ if( pM ){
138
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
135139
}else{
136
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
140
+ zCI = 0;
137141
}
138142
}
139143
140144
141145
/* Compute the title of the page */
142146
blob_zero(&dirname);
143147
if( zD ){
144148
blob_append(&dirname, "in directory ", -1);
145
- hyperlinked_path(zD, &dirname);
149
+ hyperlinked_path(zD, &dirname, zCI);
146150
zPrefix = mprintf("%h/", zD);
147151
}else{
148152
blob_append(&dirname, "in the top-level directory", -1);
149153
zPrefix = "";
150154
}
@@ -160,22 +164,30 @@
160164
style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
161165
}else{
162166
style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
163167
}
164168
}else{
169
+ int hasTrunk;
165170
@ <h2>The union of all files from all check-ins
166171
@ %s(blob_str(&dirname))</h2>
172
+ hasTrunk = db_exists(
173
+ "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'",
174
+ TAG_BRANCH);
167175
zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
168176
if( zD ){
169177
style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
170178
style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
171179
g.zBaseURL, zD);
172
- style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
173
- g.zBaseURL,zD);
180
+ if( hasTrunk ){
181
+ style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
182
+ g.zBaseURL,zD);
183
+ }
174184
}else{
175185
style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
176
- style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
186
+ if( hasTrunk ){
187
+ style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
188
+ }
177189
}
178190
}
179191
180192
/* Compute the temporary table "localfiles" containing the names
181193
** of all files and subdirectories in the zD[] directory.
@@ -184,39 +196,47 @@
184196
** first and it also gives us an easy way to distinguish files
185197
** from directories in the loop that follows.
186198
*/
187199
db_multi_exec(
188200
"CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
189
- "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);"
190201
);
191202
if( zCI ){
192203
Stmt ins;
193
- int i;
194
- db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)");
195
- for(i=0; i<m.nFile; i++){
196
- db_bind_text(&ins, ":x", m.aFile[i].zName);
197
- db_bind_text(&ins, ":u", m.aFile[i].zUuid);
198
- db_step(&ins);
199
- db_reset(&ins);
200
- }
201
- db_finalize(&ins);
202
- }else{
203
- db_multi_exec(
204
- "INSERT INTO allfiles SELECT name, NULL FROM filename"
205
- );
206
- }
207
- if( zD ){
208
- db_multi_exec(
209
- "INSERT OR IGNORE INTO localfiles "
210
- " SELECT pathelement(x,%d), u FROM allfiles"
211
- " WHERE x GLOB '%q/*'",
212
- strlen(zD)+1, zD
213
- );
214
- }else{
215
- db_multi_exec(
216
- "INSERT OR IGNORE INTO localfiles "
217
- " SELECT pathelement(x,0), u FROM allfiles"
204
+ ManifestFile *pFile;
205
+ ManifestFile *pPrev = 0;
206
+ int nPrev = 0;
207
+ int c;
208
+
209
+ db_prepare(&ins,
210
+ "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)"
211
+ );
212
+ manifest_file_rewind(pM);
213
+ while( (pFile = manifest_file_next(pM,0))!=0 ){
214
+ if( nD>0 && memcmp(pFile->zName, zD, nD-1)!=0 ) continue;
215
+ if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 ){
216
+ continue;
217
+ }
218
+ db_bind_text(&ins, ":x", &pFile->zName[nD]);
219
+ db_bind_text(&ins, ":u", pFile->zUuid);
220
+ db_step(&ins);
221
+ db_reset(&ins);
222
+ pPrev = pFile;
223
+ for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
224
+ if( c=='/' ) nPrev++;
225
+ }
226
+ db_finalize(&ins);
227
+ }else if( zD ){
228
+ db_multi_exec(
229
+ "INSERT OR IGNORE INTO localfiles"
230
+ " SELECT pathelement(name,%d), NULL FROM filename"
231
+ " WHERE name GLOB '%q/*'",
232
+ nD, zD
233
+ );
234
+ }else{
235
+ db_multi_exec(
236
+ "INSERT OR IGNORE INTO localfiles"
237
+ " SELECT pathelement(name,0), NULL FROM filename"
218238
);
219239
}
220240
221241
/* Generate a multi-column table listing the contents of zD[]
222242
** directory.
@@ -246,8 +266,9 @@
246266
@ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
247267
@ </a></li>
248268
}
249269
}
250270
db_finalize(&q);
271
+ manifest_destroy(pM);
251272
@ </ul></td></tr></table>
252273
style_footer_cmdref("ls",0);
253274
}
254275
--- src/browse.c
+++ src/browse.c
@@ -71,19 +71,24 @@
71 ** There is no hyperlink on the file element of the path.
72 **
73 ** The computed string is appended to the pOut blob. pOut should
74 ** have already been initialized.
75 */
76 void hyperlinked_path(const char *zPath, Blob *pOut){
77 int i, j;
78 char *zSep = "";
79
80 for(i=0; zPath[i]; i=j){
81 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
82 if( zPath[j] && g.okHistory ){
83 blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>",
84 zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
 
 
 
 
 
85 }else{
86 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
87 }
88 zSep = "/";
89 while( zPath[j]=='/' ){ j++; }
@@ -99,21 +104,21 @@
99 ** name=PATH Directory to display. Required.
100 ** ci=LABEL Show only files in this check-in. Optional.
101 */
102 void page_dir(void){
103 const char *zD = P("name");
 
104 int mxLen;
105 int nCol, nRow;
106 int cnt, i;
107 char *zPrefix;
108 Stmt q;
109 const char *zCI = P("ci");
110 int rid = 0;
111 char *zUuid = 0;
112 Blob content;
113 Blob dirname;
114 Manifest m;
115 const char *zSubdirLink;
116
117 login_check_credentials();
118 if( !g.okHistory ){ login_needed(); return; }
119 style_header("File List");
@@ -126,25 +131,24 @@
126 /* If a specific check-in is requested, fetch and parse it. If the
127 ** specific check-in does not exist, clear zCI. zCI==0 will cause all
128 ** files from all check-ins to be displayed.
129 */
130 if( zCI ){
131 if( (rid = name_to_rid(zCI))==0 || content_get(rid, &content)==0 ){
132 zCI = 0; /* No artifact named zCI exists */
133 }else if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){
134 zCI = 0; /* The artifact exists but is not a manifest */
135 }else{
136 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
137 }
138 }
139
140
141 /* Compute the title of the page */
142 blob_zero(&dirname);
143 if( zD ){
144 blob_append(&dirname, "in directory ", -1);
145 hyperlinked_path(zD, &dirname);
146 zPrefix = mprintf("%h/", zD);
147 }else{
148 blob_append(&dirname, "in the top-level directory", -1);
149 zPrefix = "";
150 }
@@ -160,22 +164,30 @@
160 style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
161 }else{
162 style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
163 }
164 }else{
 
165 @ <h2>The union of all files from all check-ins
166 @ %s(blob_str(&dirname))</h2>
 
 
 
167 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
168 if( zD ){
169 style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
170 style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
171 g.zBaseURL, zD);
172 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
173 g.zBaseURL,zD);
 
 
174 }else{
175 style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
176 style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
 
 
177 }
178 }
179
180 /* Compute the temporary table "localfiles" containing the names
181 ** of all files and subdirectories in the zD[] directory.
@@ -184,39 +196,47 @@
184 ** first and it also gives us an easy way to distinguish files
185 ** from directories in the loop that follows.
186 */
187 db_multi_exec(
188 "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
189 "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);"
190 );
191 if( zCI ){
192 Stmt ins;
193 int i;
194 db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)");
195 for(i=0; i<m.nFile; i++){
196 db_bind_text(&ins, ":x", m.aFile[i].zName);
197 db_bind_text(&ins, ":u", m.aFile[i].zUuid);
198 db_step(&ins);
199 db_reset(&ins);
200 }
201 db_finalize(&ins);
202 }else{
203 db_multi_exec(
204 "INSERT INTO allfiles SELECT name, NULL FROM filename"
205 );
206 }
207 if( zD ){
208 db_multi_exec(
209 "INSERT OR IGNORE INTO localfiles "
210 " SELECT pathelement(x,%d), u FROM allfiles"
211 " WHERE x GLOB '%q/*'",
212 strlen(zD)+1, zD
213 );
214 }else{
215 db_multi_exec(
216 "INSERT OR IGNORE INTO localfiles "
217 " SELECT pathelement(x,0), u FROM allfiles"
 
 
 
 
 
 
 
 
 
218 );
219 }
220
221 /* Generate a multi-column table listing the contents of zD[]
222 ** directory.
@@ -246,8 +266,9 @@
246 @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
247 @ </a></li>
248 }
249 }
250 db_finalize(&q);
 
251 @ </ul></td></tr></table>
252 style_footer_cmdref("ls",0);
253 }
254
--- src/browse.c
+++ src/browse.c
@@ -71,19 +71,24 @@
71 ** There is no hyperlink on the file element of the path.
72 **
73 ** The computed string is appended to the pOut blob. pOut should
74 ** have already been initialized.
75 */
76 void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
77 int i, j;
78 char *zSep = "";
79
80 for(i=0; zPath[i]; i=j){
81 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
82 if( zPath[j] && g.okHistory ){
83 if( zCI ){
84 blob_appendf(pOut, "%s<a href=\"%s/dir?ci=%S&amp;name=%#T\">%#h</a>",
85 zSep, g.zBaseURL, zCI, j, zPath, j-i, &zPath[i]);
86 }else{
87 blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>",
88 zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
89 }
90 }else{
91 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
92 }
93 zSep = "/";
94 while( zPath[j]=='/' ){ j++; }
@@ -99,21 +104,21 @@
104 ** name=PATH Directory to display. Required.
105 ** ci=LABEL Show only files in this check-in. Optional.
106 */
107 void page_dir(void){
108 const char *zD = P("name");
109 int nD = zD ? strlen(zD)+1 : 0;
110 int mxLen;
111 int nCol, nRow;
112 int cnt, i;
113 char *zPrefix;
114 Stmt q;
115 const char *zCI = P("ci");
116 int rid = 0;
117 char *zUuid = 0;
 
118 Blob dirname;
119 Manifest *pM = 0;
120 const char *zSubdirLink;
121
122 login_check_credentials();
123 if( !g.okHistory ){ login_needed(); return; }
124 style_header("File List");
@@ -126,25 +131,24 @@
131 /* If a specific check-in is requested, fetch and parse it. If the
132 ** specific check-in does not exist, clear zCI. zCI==0 will cause all
133 ** files from all check-ins to be displayed.
134 */
135 if( zCI ){
136 pM = manifest_get_by_name(zCI, &rid);
137 if( pM ){
138 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
 
139 }else{
140 zCI = 0;
141 }
142 }
143
144
145 /* Compute the title of the page */
146 blob_zero(&dirname);
147 if( zD ){
148 blob_append(&dirname, "in directory ", -1);
149 hyperlinked_path(zD, &dirname, zCI);
150 zPrefix = mprintf("%h/", zD);
151 }else{
152 blob_append(&dirname, "in the top-level directory", -1);
153 zPrefix = "";
154 }
@@ -160,22 +164,30 @@
164 style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
165 }else{
166 style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
167 }
168 }else{
169 int hasTrunk;
170 @ <h2>The union of all files from all check-ins
171 @ %s(blob_str(&dirname))</h2>
172 hasTrunk = db_exists(
173 "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'",
174 TAG_BRANCH);
175 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
176 if( zD ){
177 style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
178 style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
179 g.zBaseURL, zD);
180 if( hasTrunk ){
181 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
182 g.zBaseURL,zD);
183 }
184 }else{
185 style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
186 if( hasTrunk ){
187 style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
188 }
189 }
190 }
191
192 /* Compute the temporary table "localfiles" containing the names
193 ** of all files and subdirectories in the zD[] directory.
@@ -184,39 +196,47 @@
196 ** first and it also gives us an easy way to distinguish files
197 ** from directories in the loop that follows.
198 */
199 db_multi_exec(
200 "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
 
201 );
202 if( zCI ){
203 Stmt ins;
204 ManifestFile *pFile;
205 ManifestFile *pPrev = 0;
206 int nPrev = 0;
207 int c;
208
209 db_prepare(&ins,
210 "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)"
211 );
212 manifest_file_rewind(pM);
213 while( (pFile = manifest_file_next(pM,0))!=0 ){
214 if( nD>0 && memcmp(pFile->zName, zD, nD-1)!=0 ) continue;
215 if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 ){
216 continue;
217 }
218 db_bind_text(&ins, ":x", &pFile->zName[nD]);
219 db_bind_text(&ins, ":u", pFile->zUuid);
220 db_step(&ins);
221 db_reset(&ins);
222 pPrev = pFile;
223 for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
224 if( c=='/' ) nPrev++;
225 }
226 db_finalize(&ins);
227 }else if( zD ){
228 db_multi_exec(
229 "INSERT OR IGNORE INTO localfiles"
230 " SELECT pathelement(name,%d), NULL FROM filename"
231 " WHERE name GLOB '%q/*'",
232 nD, zD
233 );
234 }else{
235 db_multi_exec(
236 "INSERT OR IGNORE INTO localfiles"
237 " SELECT pathelement(name,0), NULL FROM filename"
238 );
239 }
240
241 /* Generate a multi-column table listing the contents of zD[]
242 ** directory.
@@ -246,8 +266,9 @@
266 @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
267 @ </a></li>
268 }
269 }
270 db_finalize(&q);
271 manifest_destroy(pM);
272 @ </ul></td></tr></table>
273 style_footer_cmdref("ls",0);
274 }
275
+61 -40
--- src/browse.c
+++ src/browse.c
@@ -71,19 +71,24 @@
7171
** There is no hyperlink on the file element of the path.
7272
**
7373
** The computed string is appended to the pOut blob. pOut should
7474
** have already been initialized.
7575
*/
76
-void hyperlinked_path(const char *zPath, Blob *pOut){
76
+void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
7777
int i, j;
7878
char *zSep = "";
7979
8080
for(i=0; zPath[i]; i=j){
8181
for(j=i; zPath[j] && zPath[j]!='/'; j++){}
8282
if( zPath[j] && g.okHistory ){
83
- blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>",
84
- zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
83
+ if( zCI ){
84
+ blob_appendf(pOut, "%s<a href=\"%s/dir?ci=%S&amp;name=%#T\">%#h</a>",
85
+ zSep, g.zBaseURL, zCI, j, zPath, j-i, &zPath[i]);
86
+ }else{
87
+ blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>",
88
+ zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
89
+ }
8590
}else{
8691
blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
8792
}
8893
zSep = "/";
8994
while( zPath[j]=='/' ){ j++; }
@@ -99,21 +104,21 @@
99104
** name=PATH Directory to display. Required.
100105
** ci=LABEL Show only files in this check-in. Optional.
101106
*/
102107
void page_dir(void){
103108
const char *zD = P("name");
109
+ int nD = zD ? strlen(zD)+1 : 0;
104110
int mxLen;
105111
int nCol, nRow;
106112
int cnt, i;
107113
char *zPrefix;
108114
Stmt q;
109115
const char *zCI = P("ci");
110116
int rid = 0;
111117
char *zUuid = 0;
112
- Blob content;
113118
Blob dirname;
114
- Manifest m;
119
+ Manifest *pM = 0;
115120
const char *zSubdirLink;
116121
117122
login_check_credentials();
118123
if( !g.okHistory ){ login_needed(); return; }
119124
style_header("File List");
@@ -126,25 +131,24 @@
126131
/* If a specific check-in is requested, fetch and parse it. If the
127132
** specific check-in does not exist, clear zCI. zCI==0 will cause all
128133
** files from all check-ins to be displayed.
129134
*/
130135
if( zCI ){
131
- if( (rid = name_to_rid(zCI))==0 || content_get(rid, &content)==0 ){
132
- zCI = 0; /* No artifact named zCI exists */
133
- }else if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){
134
- zCI = 0; /* The artifact exists but is not a manifest */
136
+ pM = manifest_get_by_name(zCI, &rid);
137
+ if( pM ){
138
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
135139
}else{
136
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
140
+ zCI = 0;
137141
}
138142
}
139143
140144
141145
/* Compute the title of the page */
142146
blob_zero(&dirname);
143147
if( zD ){
144148
blob_append(&dirname, "in directory ", -1);
145
- hyperlinked_path(zD, &dirname);
149
+ hyperlinked_path(zD, &dirname, zCI);
146150
zPrefix = mprintf("%h/", zD);
147151
}else{
148152
blob_append(&dirname, "in the top-level directory", -1);
149153
zPrefix = "";
150154
}
@@ -160,22 +164,30 @@
160164
style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
161165
}else{
162166
style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
163167
}
164168
}else{
169
+ int hasTrunk;
165170
@ <h2>The union of all files from all check-ins
166171
@ %s(blob_str(&dirname))</h2>
172
+ hasTrunk = db_exists(
173
+ "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'",
174
+ TAG_BRANCH);
167175
zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
168176
if( zD ){
169177
style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
170178
style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
171179
g.zBaseURL, zD);
172
- style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
173
- g.zBaseURL,zD);
180
+ if( hasTrunk ){
181
+ style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
182
+ g.zBaseURL,zD);
183
+ }
174184
}else{
175185
style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
176
- style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
186
+ if( hasTrunk ){
187
+ style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
188
+ }
177189
}
178190
}
179191
180192
/* Compute the temporary table "localfiles" containing the names
181193
** of all files and subdirectories in the zD[] directory.
@@ -184,39 +196,47 @@
184196
** first and it also gives us an easy way to distinguish files
185197
** from directories in the loop that follows.
186198
*/
187199
db_multi_exec(
188200
"CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
189
- "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);"
190201
);
191202
if( zCI ){
192203
Stmt ins;
193
- int i;
194
- db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)");
195
- for(i=0; i<m.nFile; i++){
196
- db_bind_text(&ins, ":x", m.aFile[i].zName);
197
- db_bind_text(&ins, ":u", m.aFile[i].zUuid);
198
- db_step(&ins);
199
- db_reset(&ins);
200
- }
201
- db_finalize(&ins);
202
- }else{
203
- db_multi_exec(
204
- "INSERT INTO allfiles SELECT name, NULL FROM filename"
205
- );
206
- }
207
- if( zD ){
208
- db_multi_exec(
209
- "INSERT OR IGNORE INTO localfiles "
210
- " SELECT pathelement(x,%d), u FROM allfiles"
211
- " WHERE x GLOB '%q/*'",
212
- strlen(zD)+1, zD
213
- );
214
- }else{
215
- db_multi_exec(
216
- "INSERT OR IGNORE INTO localfiles "
217
- " SELECT pathelement(x,0), u FROM allfiles"
204
+ ManifestFile *pFile;
205
+ ManifestFile *pPrev = 0;
206
+ int nPrev = 0;
207
+ int c;
208
+
209
+ db_prepare(&ins,
210
+ "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)"
211
+ );
212
+ manifest_file_rewind(pM);
213
+ while( (pFile = manifest_file_next(pM,0))!=0 ){
214
+ if( nD>0 && memcmp(pFile->zName, zD, nD-1)!=0 ) continue;
215
+ if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 ){
216
+ continue;
217
+ }
218
+ db_bind_text(&ins, ":x", &pFile->zName[nD]);
219
+ db_bind_text(&ins, ":u", pFile->zUuid);
220
+ db_step(&ins);
221
+ db_reset(&ins);
222
+ pPrev = pFile;
223
+ for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
224
+ if( c=='/' ) nPrev++;
225
+ }
226
+ db_finalize(&ins);
227
+ }else if( zD ){
228
+ db_multi_exec(
229
+ "INSERT OR IGNORE INTO localfiles"
230
+ " SELECT pathelement(name,%d), NULL FROM filename"
231
+ " WHERE name GLOB '%q/*'",
232
+ nD, zD
233
+ );
234
+ }else{
235
+ db_multi_exec(
236
+ "INSERT OR IGNORE INTO localfiles"
237
+ " SELECT pathelement(name,0), NULL FROM filename"
218238
);
219239
}
220240
221241
/* Generate a multi-column table listing the contents of zD[]
222242
** directory.
@@ -246,8 +266,9 @@
246266
@ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
247267
@ </a></li>
248268
}
249269
}
250270
db_finalize(&q);
271
+ manifest_destroy(pM);
251272
@ </ul></td></tr></table>
252273
style_footer_cmdref("ls",0);
253274
}
254275
--- src/browse.c
+++ src/browse.c
@@ -71,19 +71,24 @@
71 ** There is no hyperlink on the file element of the path.
72 **
73 ** The computed string is appended to the pOut blob. pOut should
74 ** have already been initialized.
75 */
76 void hyperlinked_path(const char *zPath, Blob *pOut){
77 int i, j;
78 char *zSep = "";
79
80 for(i=0; zPath[i]; i=j){
81 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
82 if( zPath[j] && g.okHistory ){
83 blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>",
84 zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
 
 
 
 
 
85 }else{
86 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
87 }
88 zSep = "/";
89 while( zPath[j]=='/' ){ j++; }
@@ -99,21 +104,21 @@
99 ** name=PATH Directory to display. Required.
100 ** ci=LABEL Show only files in this check-in. Optional.
101 */
102 void page_dir(void){
103 const char *zD = P("name");
 
104 int mxLen;
105 int nCol, nRow;
106 int cnt, i;
107 char *zPrefix;
108 Stmt q;
109 const char *zCI = P("ci");
110 int rid = 0;
111 char *zUuid = 0;
112 Blob content;
113 Blob dirname;
114 Manifest m;
115 const char *zSubdirLink;
116
117 login_check_credentials();
118 if( !g.okHistory ){ login_needed(); return; }
119 style_header("File List");
@@ -126,25 +131,24 @@
126 /* If a specific check-in is requested, fetch and parse it. If the
127 ** specific check-in does not exist, clear zCI. zCI==0 will cause all
128 ** files from all check-ins to be displayed.
129 */
130 if( zCI ){
131 if( (rid = name_to_rid(zCI))==0 || content_get(rid, &content)==0 ){
132 zCI = 0; /* No artifact named zCI exists */
133 }else if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){
134 zCI = 0; /* The artifact exists but is not a manifest */
135 }else{
136 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
137 }
138 }
139
140
141 /* Compute the title of the page */
142 blob_zero(&dirname);
143 if( zD ){
144 blob_append(&dirname, "in directory ", -1);
145 hyperlinked_path(zD, &dirname);
146 zPrefix = mprintf("%h/", zD);
147 }else{
148 blob_append(&dirname, "in the top-level directory", -1);
149 zPrefix = "";
150 }
@@ -160,22 +164,30 @@
160 style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
161 }else{
162 style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
163 }
164 }else{
 
165 @ <h2>The union of all files from all check-ins
166 @ %s(blob_str(&dirname))</h2>
 
 
 
167 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
168 if( zD ){
169 style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
170 style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
171 g.zBaseURL, zD);
172 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
173 g.zBaseURL,zD);
 
 
174 }else{
175 style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
176 style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
 
 
177 }
178 }
179
180 /* Compute the temporary table "localfiles" containing the names
181 ** of all files and subdirectories in the zD[] directory.
@@ -184,39 +196,47 @@
184 ** first and it also gives us an easy way to distinguish files
185 ** from directories in the loop that follows.
186 */
187 db_multi_exec(
188 "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
189 "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);"
190 );
191 if( zCI ){
192 Stmt ins;
193 int i;
194 db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)");
195 for(i=0; i<m.nFile; i++){
196 db_bind_text(&ins, ":x", m.aFile[i].zName);
197 db_bind_text(&ins, ":u", m.aFile[i].zUuid);
198 db_step(&ins);
199 db_reset(&ins);
200 }
201 db_finalize(&ins);
202 }else{
203 db_multi_exec(
204 "INSERT INTO allfiles SELECT name, NULL FROM filename"
205 );
206 }
207 if( zD ){
208 db_multi_exec(
209 "INSERT OR IGNORE INTO localfiles "
210 " SELECT pathelement(x,%d), u FROM allfiles"
211 " WHERE x GLOB '%q/*'",
212 strlen(zD)+1, zD
213 );
214 }else{
215 db_multi_exec(
216 "INSERT OR IGNORE INTO localfiles "
217 " SELECT pathelement(x,0), u FROM allfiles"
 
 
 
 
 
 
 
 
 
218 );
219 }
220
221 /* Generate a multi-column table listing the contents of zD[]
222 ** directory.
@@ -246,8 +266,9 @@
246 @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
247 @ </a></li>
248 }
249 }
250 db_finalize(&q);
 
251 @ </ul></td></tr></table>
252 style_footer_cmdref("ls",0);
253 }
254
--- src/browse.c
+++ src/browse.c
@@ -71,19 +71,24 @@
71 ** There is no hyperlink on the file element of the path.
72 **
73 ** The computed string is appended to the pOut blob. pOut should
74 ** have already been initialized.
75 */
76 void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
77 int i, j;
78 char *zSep = "";
79
80 for(i=0; zPath[i]; i=j){
81 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
82 if( zPath[j] && g.okHistory ){
83 if( zCI ){
84 blob_appendf(pOut, "%s<a href=\"%s/dir?ci=%S&amp;name=%#T\">%#h</a>",
85 zSep, g.zBaseURL, zCI, j, zPath, j-i, &zPath[i]);
86 }else{
87 blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>",
88 zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]);
89 }
90 }else{
91 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
92 }
93 zSep = "/";
94 while( zPath[j]=='/' ){ j++; }
@@ -99,21 +104,21 @@
104 ** name=PATH Directory to display. Required.
105 ** ci=LABEL Show only files in this check-in. Optional.
106 */
107 void page_dir(void){
108 const char *zD = P("name");
109 int nD = zD ? strlen(zD)+1 : 0;
110 int mxLen;
111 int nCol, nRow;
112 int cnt, i;
113 char *zPrefix;
114 Stmt q;
115 const char *zCI = P("ci");
116 int rid = 0;
117 char *zUuid = 0;
 
118 Blob dirname;
119 Manifest *pM = 0;
120 const char *zSubdirLink;
121
122 login_check_credentials();
123 if( !g.okHistory ){ login_needed(); return; }
124 style_header("File List");
@@ -126,25 +131,24 @@
131 /* If a specific check-in is requested, fetch and parse it. If the
132 ** specific check-in does not exist, clear zCI. zCI==0 will cause all
133 ** files from all check-ins to be displayed.
134 */
135 if( zCI ){
136 pM = manifest_get_by_name(zCI, &rid);
137 if( pM ){
138 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
 
139 }else{
140 zCI = 0;
141 }
142 }
143
144
145 /* Compute the title of the page */
146 blob_zero(&dirname);
147 if( zD ){
148 blob_append(&dirname, "in directory ", -1);
149 hyperlinked_path(zD, &dirname, zCI);
150 zPrefix = mprintf("%h/", zD);
151 }else{
152 blob_append(&dirname, "in the top-level directory", -1);
153 zPrefix = "";
154 }
@@ -160,22 +164,30 @@
164 style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
165 }else{
166 style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
167 }
168 }else{
169 int hasTrunk;
170 @ <h2>The union of all files from all check-ins
171 @ %s(blob_str(&dirname))</h2>
172 hasTrunk = db_exists(
173 "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'",
174 TAG_BRANCH);
175 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
176 if( zD ){
177 style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
178 style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
179 g.zBaseURL, zD);
180 if( hasTrunk ){
181 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
182 g.zBaseURL,zD);
183 }
184 }else{
185 style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
186 if( hasTrunk ){
187 style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
188 }
189 }
190 }
191
192 /* Compute the temporary table "localfiles" containing the names
193 ** of all files and subdirectories in the zD[] directory.
@@ -184,39 +196,47 @@
196 ** first and it also gives us an easy way to distinguish files
197 ** from directories in the loop that follows.
198 */
199 db_multi_exec(
200 "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
 
201 );
202 if( zCI ){
203 Stmt ins;
204 ManifestFile *pFile;
205 ManifestFile *pPrev = 0;
206 int nPrev = 0;
207 int c;
208
209 db_prepare(&ins,
210 "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)"
211 );
212 manifest_file_rewind(pM);
213 while( (pFile = manifest_file_next(pM,0))!=0 ){
214 if( nD>0 && memcmp(pFile->zName, zD, nD-1)!=0 ) continue;
215 if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 ){
216 continue;
217 }
218 db_bind_text(&ins, ":x", &pFile->zName[nD]);
219 db_bind_text(&ins, ":u", pFile->zUuid);
220 db_step(&ins);
221 db_reset(&ins);
222 pPrev = pFile;
223 for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
224 if( c=='/' ) nPrev++;
225 }
226 db_finalize(&ins);
227 }else if( zD ){
228 db_multi_exec(
229 "INSERT OR IGNORE INTO localfiles"
230 " SELECT pathelement(name,%d), NULL FROM filename"
231 " WHERE name GLOB '%q/*'",
232 nD, zD
233 );
234 }else{
235 db_multi_exec(
236 "INSERT OR IGNORE INTO localfiles"
237 " SELECT pathelement(name,0), NULL FROM filename"
238 );
239 }
240
241 /* Generate a multi-column table listing the contents of zD[]
242 ** directory.
@@ -246,8 +266,9 @@
266 @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
267 @ </a></li>
268 }
269 }
270 db_finalize(&q);
271 manifest_destroy(pM);
272 @ </ul></td></tr></table>
273 style_footer_cmdref("ls",0);
274 }
275
+285 -141
--- src/checkin.c
+++ src/checkin.c
@@ -264,10 +264,11 @@
264264
Blob repo;
265265
Stmt q;
266266
int n;
267267
const char *zIgnoreFlag = find_option("ignore",0,1);
268268
int allFlag = find_option("dotfiles",0,0)!=0;
269
+ int outputManifest = db_get_boolean("manifest",0);
269270
270271
db_must_be_within_tree();
271272
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
272273
n = strlen(g.zLocalRoot);
273274
blob_init(&path, g.zLocalRoot, n-1);
@@ -275,16 +276,18 @@
275276
zIgnoreFlag = db_get("ignore-glob", 0);
276277
}
277278
vfile_scan(0, &path, blob_size(&path), allFlag);
278279
db_prepare(&q,
279280
"SELECT x FROM sfile"
280
- " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_',"
281
+ " WHERE x NOT IN ('%s','%s','_FOSSIL_',"
281282
"'_FOSSIL_-journal','.fos','.fos-journal',"
282283
"'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal',"
283284
"'.fos-shm')"
284285
" AND NOT %s"
285286
" ORDER BY 1",
287
+ outputManifest ? "manifest" : "_FOSSIL_",
288
+ outputManifest ? "manifest.uuid" : "_FOSSIL_",
286289
glob_expr("x", zIgnoreFlag)
287290
);
288291
if( file_tree_name(g.zRepositoryName, &repo, 0) ){
289292
db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
290293
}
@@ -541,21 +544,158 @@
541544
zDate[10] = 'T';
542545
return zDate;
543546
}
544547
545548
/*
546
-** Return TRUE (non-zero) if a file named "zFilename" exists in
547
-** the checkout identified by vid.
548
-**
549
-** The original purpose of this routine was to check for the presence of
550
-** a "checked-in" file named "manifest" or "manifest.uuid" so as to avoid
551
-** overwriting that file with automatically generated files.
549
+** Create a manifest.
552550
*/
553
-int file_exists_in_checkout(int vid, const char *zFilename){
554
- return db_exists("SELECT 1 FROM vfile WHERE vid=%d AND pathname=%Q",
555
- vid, zFilename);
551
+static void create_manifest(
552
+ Blob *pOut, /* Write the manifest here */
553
+ const char *zBaselineUuid, /* UUID of baseline, or zero */
554
+ Manifest *pBaseline, /* Make it a delta manifest if not zero */
555
+ Blob *pComment, /* Check-in comment text */
556
+ int vid, /* blob-id of the parent manifest */
557
+ int verifyDate, /* Verify that child is younger */
558
+ Blob *pCksum, /* Repository checksum. May be 0 */
559
+ const char *zDateOvrd, /* Date override. If 0 then use 'now' */
560
+ const char *zUserOvrd, /* User override. If 0 then use g.zLogin */
561
+ const char *zBranch, /* Branch name. May be 0 */
562
+ const char *zBgColor, /* Background color. May be 0 */
563
+ int *pnFBcard /* Number of generated B- and F-cards */
564
+){
565
+ char *zDate; /* Date of the check-in */
566
+ char *zParentUuid; /* UUID of parent check-in */
567
+ Blob filename; /* A single filename */
568
+ int nBasename; /* Size of base filename */
569
+ Stmt q; /* Query of files changed */
570
+ Stmt q2; /* Query of merge parents */
571
+ Blob mcksum; /* Manifest checksum */
572
+ ManifestFile *pFile; /* File from the baseline */
573
+ int nFBcard = 0; /* Number of B-cards and F-cards */
574
+
575
+ assert( pBaseline==0 || pBaseline->zBaseline==0 );
576
+ assert( pBaseline==0 || zBaselineUuid!=0 );
577
+ blob_zero(pOut);
578
+ zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
579
+ if( pBaseline ){
580
+ blob_appendf(pOut, "B %s\n", zBaselineUuid);
581
+ manifest_file_rewind(pBaseline);
582
+ pFile = manifest_file_next(pBaseline, 0);
583
+ nFBcard++;
584
+ }else{
585
+ pFile = 0;
586
+ }
587
+ blob_appendf(pOut, "C %F\n", blob_str(pComment));
588
+ zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
589
+ blob_appendf(pOut, "D %s\n", zDate);
590
+ zDate[10] = ' ';
591
+ db_prepare(&q,
592
+ "SELECT pathname, uuid, origname, blob.rid, isexe"
593
+ " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
594
+ " WHERE NOT deleted AND vfile.vid=%d"
595
+ " ORDER BY 1", vid);
596
+ blob_zero(&filename);
597
+ blob_appendf(&filename, "%s", g.zLocalRoot);
598
+ nBasename = blob_size(&filename);
599
+ while( db_step(&q)==SQLITE_ROW ){
600
+ const char *zName = db_column_text(&q, 0);
601
+ const char *zUuid = db_column_text(&q, 1);
602
+ const char *zOrig = db_column_text(&q, 2);
603
+ int frid = db_column_int(&q, 3);
604
+ int isexe = db_column_int(&q, 4);
605
+ const char *zPerm;
606
+ int cmp;
607
+ blob_append(&filename, zName, -1);
608
+#if !defined(_WIN32)
609
+ /* For unix, extract the "executable" permission bit directly from
610
+ ** the filesystem. On windows, the "executable" bit is retained
611
+ ** unchanged from the original. */
612
+ isexe = file_isexe(blob_str(&filename));
613
+#endif
614
+ if( isexe ){
615
+ zPerm = " x";
616
+ }else{
617
+ zPerm = "";
618
+ }
619
+ if( !g.markPrivate ) content_make_public(frid);
620
+ while( pFile && strcmp(pFile->zName,zName)<0 ){
621
+ blob_appendf(pOut, "F %F\n", pFile->zName);
622
+ pFile = manifest_file_next(pBaseline, 0);
623
+ nFBcard++;
624
+ }
625
+ cmp = 1;
626
+ if( pFile==0
627
+ || (cmp = strcmp(pFile->zName,zName))!=0
628
+ || strcmp(pFile->zUuid, zUuid)!=0
629
+ ){
630
+ blob_resize(&filename, nBasename);
631
+ if( zOrig==0 || strcmp(zOrig,zName)==0 ){
632
+ blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm);
633
+ }else{
634
+ if( zPerm[0]==0 ){ zPerm = " w"; }
635
+ blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
636
+ }
637
+ nFBcard++;
638
+ }
639
+ if( cmp==0 ) pFile = manifest_file_next(pBaseline,0);
640
+ }
641
+ blob_reset(&filename);
642
+ db_finalize(&q);
643
+ while( pFile ){
644
+ blob_appendf(pOut, "F %F\n", pFile->zName);
645
+ pFile = manifest_file_next(pBaseline, 0);
646
+ nFBcard++;
647
+ }
648
+ blob_appendf(pOut, "P %s", zParentUuid);
649
+ if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate);
650
+ free(zParentUuid);
651
+ db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
652
+ db_bind_int(&q2, ":id", 0);
653
+ while( db_step(&q2)==SQLITE_ROW ){
654
+ char *zMergeUuid;
655
+ int mid = db_column_int(&q2, 0);
656
+ if( !g.markPrivate && content_is_private(mid) ) continue;
657
+ zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
658
+ if( zMergeUuid ){
659
+ blob_appendf(pOut, " %s", zMergeUuid);
660
+ if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
661
+ free(zMergeUuid);
662
+ }
663
+ }
664
+ db_finalize(&q2);
665
+ free(zDate);
666
+
667
+ blob_appendf(pOut, "\n");
668
+ if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum);
669
+ if( zBranch && zBranch[0] ){
670
+ Stmt q;
671
+ if( zBgColor && zBgColor[0] ){
672
+ blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor);
673
+ }
674
+ blob_appendf(pOut, "T *branch * %F\n", zBranch);
675
+ blob_appendf(pOut, "T *sym-%F *\n", zBranch);
676
+
677
+ /* Cancel all other symbolic tags */
678
+ db_prepare(&q,
679
+ "SELECT tagname FROM tagxref, tag"
680
+ " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
681
+ " AND tagtype>0 AND tagname GLOB 'sym-*'"
682
+ " AND tagname!='sym-'||%Q"
683
+ " ORDER BY tagname",
684
+ vid, zBranch);
685
+ while( db_step(&q)==SQLITE_ROW ){
686
+ const char *zTag = db_column_text(&q, 0);
687
+ blob_appendf(pOut, "T -%F *\n", zTag);
688
+ }
689
+ db_finalize(&q);
690
+ }
691
+ blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
692
+ md5sum_blob(pOut, &mcksum);
693
+ blob_appendf(pOut, "Z %b\n", &mcksum);
694
+ if( pnFBcard ) *pnFBcard = nFBcard;
556695
}
696
+
557697
558698
/*
559699
** COMMAND: ci
560700
** COMMAND: commit
561701
**
@@ -590,39 +730,52 @@
590730
** --branch NEW-BRANCH-NAME
591731
** --bgcolor COLOR
592732
** --nosign
593733
** --force|-f
594734
** --private
735
+** --baseline
736
+** --delta
595737
**
596738
*/
597739
void commit_cmd(void){
598
- int rc;
599
- int vid, nrid, nvid;
600
- Blob comment;
601
- const char *zComment;
602
- Stmt q;
603
- Stmt q2;
604
- char *zUuid, *zDate;
740
+ int hasChanges; /* True if unsaved changes exist */
741
+ int vid; /* blob-id of parent version */
742
+ int nrid; /* blob-id of a modified file */
743
+ int nvid; /* Blob-id of the new check-in */
744
+ Blob comment; /* Check-in comment */
745
+ const char *zComment; /* Check-in comment */
746
+ Stmt q; /* Query to find files that have been modified */
747
+ char *zUuid; /* UUID of the new check-in */
605748
int noSign = 0; /* True to omit signing the manifest using GPG */
606749
int isAMerge = 0; /* True if checking in a merge */
607750
int forceFlag = 0; /* Force a fork */
751
+ int forceDelta = 0; /* Force a delta-manifest */
752
+ int forceBaseline = 0; /* Force a baseline-manifest */
608753
char *zManifestFile; /* Name of the manifest file */
609
- int nBasename; /* Length of "g.zLocalRoot/" */
754
+ int useCksum; /* True if checksums should be computed and verified */
755
+ int outputManifest; /* True to output "manifest" and "manifest.uuid" */
756
+ int testRun; /* True for a test run. Debugging only */
610757
const char *zBranch; /* Create a new branch with this name */
611758
const char *zBgColor; /* Set background color when branching */
612759
const char *zDateOvrd; /* Override date string */
613760
const char *zUserOvrd; /* Override user name */
614761
const char *zComFile; /* Read commit message from this file */
615
- Blob filename; /* complete filename */
616
- Blob manifest;
762
+ Blob manifest; /* Manifest in baseline form */
617763
Blob muuid; /* Manifest uuid */
618
- Blob mcksum; /* Self-checksum on the manifest */
619764
Blob cksum1, cksum2; /* Before and after commit checksums */
620765
Blob cksum1b; /* Checksum recorded in the manifest */
766
+ int szD; /* Size of the delta manifest */
767
+ int szB; /* Size of the baseline manifest */
621768
622769
url_proxy_options();
623770
noSign = find_option("nosign",0,0)!=0;
771
+ forceDelta = find_option("delta",0,0)!=0;
772
+ forceBaseline = find_option("baseline",0,0)!=0;
773
+ if( forceDelta && forceBaseline ){
774
+ fossil_fatal("cannot use --delta and --baseline together");
775
+ }
776
+ testRun = find_option("test",0,0)!=0;
624777
zComment = find_option("comment","m",1);
625778
forceFlag = find_option("force", "f", 0)!=0;
626779
zBranch = find_option("branch","b",1);
627780
zBgColor = find_option("bgcolor",0,1);
628781
zComFile = find_option("message-file", "M", 1);
@@ -634,11 +787,23 @@
634787
zDateOvrd = find_option("date-override",0,1);
635788
zUserOvrd = find_option("user-override",0,1);
636789
db_must_be_within_tree();
637790
noSign = db_get_boolean("omitsign", 0)|noSign;
638791
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
792
+ useCksum = db_get_boolean("repo-cksum", 1);
793
+ outputManifest = db_get_boolean("manifest", 0);
639794
verify_all_options();
795
+
796
+ /* So that older versions of Fossil (that do not understand delta-
797
+ ** manifest) can continue to use this repository, do not create a new
798
+ ** delta-manifest unless this repository already contains one or more
799
+ ** delta-manifets, or unless the delta-manifest is explicitly requested
800
+ ** by the --delta option.
801
+ */
802
+ if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
803
+ forceBaseline = 1;
804
+ }
640805
641806
/* Get the ID of the parent manifest artifact */
642807
vid = db_lget_int("checkout", 0);
643808
if( content_is_private(vid) ){
644809
g.markPrivate = 1;
@@ -685,14 +850,14 @@
685850
*/
686851
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
687852
fossil_fatal("no such user: %s", g.zLogin);
688853
}
689854
690
- rc = unsaved_changes();
855
+ hasChanges = unsaved_changes();
691856
db_begin_transaction();
692857
db_record_repository_filename(0);
693
- if( rc==0 && !isAMerge && !forceFlag ){
858
+ if( hasChanges==0 && !isAMerge && !forceFlag ){
694859
fossil_fatal("nothing has changed");
695860
}
696861
697862
/* If one or more files that were named on the command line have not
698863
** been modified, bail out now.
@@ -724,11 +889,11 @@
724889
" WHERE tagid=%d AND rid=%d AND tagtype>0",
725890
TAG_CLOSED, vid) ){
726891
fossil_fatal("cannot commit against a closed leaf");
727892
}
728893
729
- vfile_aggregate_checksum_disk(vid, &cksum1);
894
+ if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1);
730895
if( zComment ){
731896
blob_zero(&comment);
732897
blob_append(&comment, zComment, -1);
733898
}else if( zComFile ){
734899
blob_zero(&comment);
@@ -779,113 +944,86 @@
779944
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
780945
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
781946
}
782947
db_finalize(&q);
783948
784
- /* Create the manifest */
785
- blob_zero(&manifest);
949
+ /* Create the new manifest */
786950
if( blob_size(&comment)==0 ){
787951
blob_append(&comment, "(no comment)", -1);
788952
}
789
- blob_appendf(&manifest, "C %F\n", blob_str(&comment));
790
- zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
791
- blob_appendf(&manifest, "D %s\n", zDate);
792
- zDate[10] = ' ';
793
- db_prepare(&q,
794
- "SELECT pathname, uuid, origname, blob.rid, isexe"
795
- " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
796
- " WHERE NOT deleted AND vfile.vid=%d"
797
- " ORDER BY 1", vid);
798
- blob_zero(&filename);
799
- blob_appendf(&filename, "%s", g.zLocalRoot);
800
- nBasename = blob_size(&filename);
801
- while( db_step(&q)==SQLITE_ROW ){
802
- const char *zName = db_column_text(&q, 0);
803
- const char *zUuid = db_column_text(&q, 1);
804
- const char *zOrig = db_column_text(&q, 2);
805
- int frid = db_column_int(&q, 3);
806
- int isexe = db_column_int(&q, 4);
807
- const char *zPerm;
808
- blob_append(&filename, zName, -1);
809
-#if !defined(_WIN32)
810
- /* For unix, extract the "executable" permission bit directly from
811
- ** the filesystem. On windows, the "executable" bit is retained
812
- ** unchanged from the original. */
813
- isexe = file_isexe(blob_str(&filename));
814
-#endif
815
- if( isexe ){
816
- zPerm = " x";
817
- }else{
818
- zPerm = "";
819
- }
820
- blob_resize(&filename, nBasename);
821
- if( zOrig==0 || strcmp(zOrig,zName)==0 ){
822
- blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm);
823
- }else{
824
- if( zPerm[0]==0 ){ zPerm = " w"; }
825
- blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
826
- }
827
- if( !g.markPrivate ) content_make_public(frid);
828
- }
829
- blob_reset(&filename);
830
- db_finalize(&q);
831
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
832
- blob_appendf(&manifest, "P %s", zUuid);
833
-
834
- if( !forceFlag ){
835
- checkin_verify_younger(vid, zUuid, zDate);
836
- db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
837
- db_bind_int(&q2, ":id", 0);
838
- while( db_step(&q2)==SQLITE_ROW ){
839
- int mid = db_column_int(&q2, 0);
840
- if( !g.markPrivate && content_is_private(mid) ) continue;
841
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
842
- if( zUuid ){
843
- blob_appendf(&manifest, " %s", zUuid);
844
- checkin_verify_younger(mid, zUuid, zDate);
845
- free(zUuid);
846
- }
847
- }
848
- db_finalize(&q2);
849
- }
850
-
851
- blob_appendf(&manifest, "\n");
852
- blob_appendf(&manifest, "R %b\n", &cksum1);
853
- if( zBranch && zBranch[0] ){
854
- Stmt q;
855
- if( zBgColor && zBgColor[0] ){
856
- blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor);
857
- }
858
- blob_appendf(&manifest, "T *branch * %F\n", zBranch);
859
- blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
860
-
861
- /* Cancel all other symbolic tags */
862
- db_prepare(&q,
863
- "SELECT tagname FROM tagxref, tag"
864
- " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
865
- " AND tagtype>0 AND tagname GLOB 'sym-*'"
866
- " AND tagname!='sym-'||%Q"
867
- " ORDER BY tagname",
868
- vid, zBranch);
869
- while( db_step(&q)==SQLITE_ROW ){
870
- const char *zTag = db_column_text(&q, 0);
871
- blob_appendf(&manifest, "T -%F *\n", zTag);
872
- }
873
- db_finalize(&q);
874
- }
875
- blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
876
- md5sum_blob(&manifest, &mcksum);
877
- blob_appendf(&manifest, "Z %b\n", &mcksum);
953
+ if( forceDelta ){
954
+ blob_zero(&manifest);
955
+ }else{
956
+ create_manifest(&manifest, 0, 0, &comment, vid,
957
+ !forceFlag, useCksum ? &cksum1 : 0,
958
+ zDateOvrd, zUserOvrd, zBranch, zBgColor, &szB);
959
+ }
960
+
961
+ /* See if a delta-manifest would be more appropriate */
962
+ if( !forceBaseline ){
963
+ const char *zBaselineUuid;
964
+ Manifest *pParent;
965
+ Manifest *pBaseline;
966
+ pParent = manifest_get(vid, CFTYPE_MANIFEST);
967
+ if( pParent && pParent->zBaseline ){
968
+ zBaselineUuid = pParent->zBaseline;
969
+ pBaseline = manifest_get_by_name(zBaselineUuid, 0);
970
+ }else{
971
+ zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
972
+ pBaseline = pParent;
973
+ }
974
+ if( pBaseline ){
975
+ Blob delta;
976
+ create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
977
+ !forceFlag, useCksum ? &cksum1 : 0,
978
+ zDateOvrd, zUserOvrd, zBranch, zBgColor, &szD);
979
+ /*
980
+ ** At this point, two manifests have been constructed, either of
981
+ ** which would work for this checkin. The first manifest (held
982
+ ** in the "manifest" variable) is a baseline manifest and the second
983
+ ** (held in variable named "delta") is a delta manifest. The
984
+ ** question now is: which manifest should we use?
985
+ **
986
+ ** Let B be the number of F-cards in the baseline manifest and
987
+ ** let D be the number of F-cards in the delta manifest, plus one for
988
+ ** the B-card. (B is held in the szB variable and D is held in the
989
+ ** szD variable.) Assume that all delta manifests adds X new F-cards.
990
+ ** Then to minimize the total number of F- and B-cards in the repository,
991
+ ** we should use the delta manifest if and only if:
992
+ **
993
+ ** D*D < B*X - X*X
994
+ **
995
+ ** X is an unknown here, but for most repositories, we will not be
996
+ ** far wrong if we assume X=3.
997
+ */
998
+ if( forceDelta || (szD*szD)<(szB*3-9) ){
999
+ blob_reset(&manifest);
1000
+ manifest = delta;
1001
+ }else{
1002
+ blob_reset(&delta);
1003
+ }
1004
+ }else if( forceDelta ){
1005
+ fossil_panic("unable to find a baseline-manifest for the delta");
1006
+ }
1007
+ }
8781008
if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
8791009
Blob ans;
8801010
blob_zero(&ans);
8811011
prompt_user("unable to sign manifest. continue (y/N)? ", &ans);
8821012
if( blob_str(&ans)[0]!='y' ){
8831013
fossil_exit(1);
8841014
}
8851015
}
886
- if( !file_exists_in_checkout(vid, "manifest") ){
1016
+
1017
+ /* If the --test option is specified, output the manifest file
1018
+ ** and rollback the transaction.
1019
+ */
1020
+ if( testRun ){
1021
+ blob_write_to_file(&manifest, "");
1022
+ }
1023
+
1024
+ if( outputManifest ){
8871025
zManifestFile = mprintf("%smanifest", g.zLocalRoot);
8881026
blob_write_to_file(&manifest, zManifestFile);
8891027
blob_reset(&manifest);
8901028
blob_read_from_file(&manifest, zManifestFile);
8911029
free(zManifestFile);
@@ -897,11 +1035,11 @@
8971035
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
8981036
manifest_crosslink(nvid, &manifest);
8991037
content_deltify(vid, nvid, 0);
9001038
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
9011039
printf("New_Version: %s\n", zUuid);
902
- if( !file_exists_in_checkout(vid, "manifest.uuid") ){
1040
+ if( outputManifest ){
9031041
zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
9041042
blob_zero(&muuid);
9051043
blob_appendf(&muuid, "%s\n", zUuid);
9061044
blob_write_to_file(&muuid, zManifestFile);
9071045
free(zManifestFile);
@@ -918,45 +1056,51 @@
9181056
" WHERE file_is_selected(id);"
9191057
, vid, nvid
9201058
);
9211059
db_lset_int("checkout", nvid);
9221060
923
- /* Verify that the repository checksum matches the expected checksum
924
- ** calculated before the checkin started (and stored as the R record
925
- ** of the manifest file).
926
- */
927
- vfile_aggregate_checksum_repository(nvid, &cksum2);
928
- if( blob_compare(&cksum1, &cksum2) ){
929
- fossil_panic("tree checksum does not match repository after commit");
930
- }
931
-
932
- /* Verify that the manifest checksum matches the expected checksum */
933
- vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
934
- if( blob_compare(&cksum1, &cksum1b) ){
935
- fossil_panic("manifest checksum does not agree with manifest: "
936
- "%b versus %b", &cksum1, &cksum1b);
937
- }
938
- if( blob_compare(&cksum1, &cksum2) ){
939
- fossil_panic("tree checksum does not match manifest after commit: "
940
- "%b versus %b", &cksum1, &cksum2);
941
- }
942
-
943
- /* Verify that the commit did not modify any disk images. */
944
- vfile_aggregate_checksum_disk(nvid, &cksum2);
945
- if( blob_compare(&cksum1, &cksum2) ){
946
- fossil_panic("tree checksums before and after commit do not match");
1061
+ if( useCksum ){
1062
+ /* Verify that the repository checksum matches the expected checksum
1063
+ ** calculated before the checkin started (and stored as the R record
1064
+ ** of the manifest file).
1065
+ */
1066
+ vfile_aggregate_checksum_repository(nvid, &cksum2);
1067
+ if( blob_compare(&cksum1, &cksum2) ){
1068
+ fossil_panic("tree checksum does not match repository after commit");
1069
+ }
1070
+
1071
+ /* Verify that the manifest checksum matches the expected checksum */
1072
+ vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
1073
+ if( blob_compare(&cksum1, &cksum1b) ){
1074
+ fossil_panic("manifest checksum does not agree with manifest: "
1075
+ "%b versus %b", &cksum1, &cksum1b);
1076
+ }
1077
+ if( blob_compare(&cksum1, &cksum2) ){
1078
+ fossil_panic("tree checksum does not match manifest after commit: "
1079
+ "%b versus %b", &cksum1, &cksum2);
1080
+ }
1081
+
1082
+ /* Verify that the commit did not modify any disk images. */
1083
+ vfile_aggregate_checksum_disk(nvid, &cksum2);
1084
+ if( blob_compare(&cksum1, &cksum2) ){
1085
+ fossil_panic("tree checksums before and after commit do not match");
1086
+ }
9471087
}
9481088
9491089
/* Clear the undo/redo stack */
9501090
undo_reset();
9511091
9521092
/* Commit */
9531093
db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
1094
+ if( testRun ){
1095
+ db_end_transaction(1);
1096
+ exit(1);
1097
+ }
9541098
db_end_transaction(0);
9551099
9561100
if( !g.markPrivate ){
9571101
autosync(AUTOSYNC_PUSH);
9581102
}
9591103
if( count_nonbranch_children(vid)>1 ){
9601104
printf("**** warning: a fork has occurred *****\n");
9611105
}
9621106
}
9631107
--- src/checkin.c
+++ src/checkin.c
@@ -264,10 +264,11 @@
264 Blob repo;
265 Stmt q;
266 int n;
267 const char *zIgnoreFlag = find_option("ignore",0,1);
268 int allFlag = find_option("dotfiles",0,0)!=0;
 
269
270 db_must_be_within_tree();
271 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
272 n = strlen(g.zLocalRoot);
273 blob_init(&path, g.zLocalRoot, n-1);
@@ -275,16 +276,18 @@
275 zIgnoreFlag = db_get("ignore-glob", 0);
276 }
277 vfile_scan(0, &path, blob_size(&path), allFlag);
278 db_prepare(&q,
279 "SELECT x FROM sfile"
280 " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_',"
281 "'_FOSSIL_-journal','.fos','.fos-journal',"
282 "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal',"
283 "'.fos-shm')"
284 " AND NOT %s"
285 " ORDER BY 1",
 
 
286 glob_expr("x", zIgnoreFlag)
287 );
288 if( file_tree_name(g.zRepositoryName, &repo, 0) ){
289 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
290 }
@@ -541,21 +544,158 @@
541 zDate[10] = 'T';
542 return zDate;
543 }
544
545 /*
546 ** Return TRUE (non-zero) if a file named "zFilename" exists in
547 ** the checkout identified by vid.
548 **
549 ** The original purpose of this routine was to check for the presence of
550 ** a "checked-in" file named "manifest" or "manifest.uuid" so as to avoid
551 ** overwriting that file with automatically generated files.
552 */
553 int file_exists_in_checkout(int vid, const char *zFilename){
554 return db_exists("SELECT 1 FROM vfile WHERE vid=%d AND pathname=%Q",
555 vid, zFilename);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556 }
 
557
558 /*
559 ** COMMAND: ci
560 ** COMMAND: commit
561 **
@@ -590,39 +730,52 @@
590 ** --branch NEW-BRANCH-NAME
591 ** --bgcolor COLOR
592 ** --nosign
593 ** --force|-f
594 ** --private
 
 
595 **
596 */
597 void commit_cmd(void){
598 int rc;
599 int vid, nrid, nvid;
600 Blob comment;
601 const char *zComment;
602 Stmt q;
603 Stmt q2;
604 char *zUuid, *zDate;
 
605 int noSign = 0; /* True to omit signing the manifest using GPG */
606 int isAMerge = 0; /* True if checking in a merge */
607 int forceFlag = 0; /* Force a fork */
 
 
608 char *zManifestFile; /* Name of the manifest file */
609 int nBasename; /* Length of "g.zLocalRoot/" */
 
 
610 const char *zBranch; /* Create a new branch with this name */
611 const char *zBgColor; /* Set background color when branching */
612 const char *zDateOvrd; /* Override date string */
613 const char *zUserOvrd; /* Override user name */
614 const char *zComFile; /* Read commit message from this file */
615 Blob filename; /* complete filename */
616 Blob manifest;
617 Blob muuid; /* Manifest uuid */
618 Blob mcksum; /* Self-checksum on the manifest */
619 Blob cksum1, cksum2; /* Before and after commit checksums */
620 Blob cksum1b; /* Checksum recorded in the manifest */
 
 
621
622 url_proxy_options();
623 noSign = find_option("nosign",0,0)!=0;
 
 
 
 
 
 
624 zComment = find_option("comment","m",1);
625 forceFlag = find_option("force", "f", 0)!=0;
626 zBranch = find_option("branch","b",1);
627 zBgColor = find_option("bgcolor",0,1);
628 zComFile = find_option("message-file", "M", 1);
@@ -634,11 +787,23 @@
634 zDateOvrd = find_option("date-override",0,1);
635 zUserOvrd = find_option("user-override",0,1);
636 db_must_be_within_tree();
637 noSign = db_get_boolean("omitsign", 0)|noSign;
638 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
 
 
639 verify_all_options();
 
 
 
 
 
 
 
 
 
 
640
641 /* Get the ID of the parent manifest artifact */
642 vid = db_lget_int("checkout", 0);
643 if( content_is_private(vid) ){
644 g.markPrivate = 1;
@@ -685,14 +850,14 @@
685 */
686 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
687 fossil_fatal("no such user: %s", g.zLogin);
688 }
689
690 rc = unsaved_changes();
691 db_begin_transaction();
692 db_record_repository_filename(0);
693 if( rc==0 && !isAMerge && !forceFlag ){
694 fossil_fatal("nothing has changed");
695 }
696
697 /* If one or more files that were named on the command line have not
698 ** been modified, bail out now.
@@ -724,11 +889,11 @@
724 " WHERE tagid=%d AND rid=%d AND tagtype>0",
725 TAG_CLOSED, vid) ){
726 fossil_fatal("cannot commit against a closed leaf");
727 }
728
729 vfile_aggregate_checksum_disk(vid, &cksum1);
730 if( zComment ){
731 blob_zero(&comment);
732 blob_append(&comment, zComment, -1);
733 }else if( zComFile ){
734 blob_zero(&comment);
@@ -779,113 +944,86 @@
779 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
780 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
781 }
782 db_finalize(&q);
783
784 /* Create the manifest */
785 blob_zero(&manifest);
786 if( blob_size(&comment)==0 ){
787 blob_append(&comment, "(no comment)", -1);
788 }
789 blob_appendf(&manifest, "C %F\n", blob_str(&comment));
790 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
791 blob_appendf(&manifest, "D %s\n", zDate);
792 zDate[10] = ' ';
793 db_prepare(&q,
794 "SELECT pathname, uuid, origname, blob.rid, isexe"
795 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
796 " WHERE NOT deleted AND vfile.vid=%d"
797 " ORDER BY 1", vid);
798 blob_zero(&filename);
799 blob_appendf(&filename, "%s", g.zLocalRoot);
800 nBasename = blob_size(&filename);
801 while( db_step(&q)==SQLITE_ROW ){
802 const char *zName = db_column_text(&q, 0);
803 const char *zUuid = db_column_text(&q, 1);
804 const char *zOrig = db_column_text(&q, 2);
805 int frid = db_column_int(&q, 3);
806 int isexe = db_column_int(&q, 4);
807 const char *zPerm;
808 blob_append(&filename, zName, -1);
809 #if !defined(_WIN32)
810 /* For unix, extract the "executable" permission bit directly from
811 ** the filesystem. On windows, the "executable" bit is retained
812 ** unchanged from the original. */
813 isexe = file_isexe(blob_str(&filename));
814 #endif
815 if( isexe ){
816 zPerm = " x";
817 }else{
818 zPerm = "";
819 }
820 blob_resize(&filename, nBasename);
821 if( zOrig==0 || strcmp(zOrig,zName)==0 ){
822 blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm);
823 }else{
824 if( zPerm[0]==0 ){ zPerm = " w"; }
825 blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
826 }
827 if( !g.markPrivate ) content_make_public(frid);
828 }
829 blob_reset(&filename);
830 db_finalize(&q);
831 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
832 blob_appendf(&manifest, "P %s", zUuid);
833
834 if( !forceFlag ){
835 checkin_verify_younger(vid, zUuid, zDate);
836 db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
837 db_bind_int(&q2, ":id", 0);
838 while( db_step(&q2)==SQLITE_ROW ){
839 int mid = db_column_int(&q2, 0);
840 if( !g.markPrivate && content_is_private(mid) ) continue;
841 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
842 if( zUuid ){
843 blob_appendf(&manifest, " %s", zUuid);
844 checkin_verify_younger(mid, zUuid, zDate);
845 free(zUuid);
846 }
847 }
848 db_finalize(&q2);
849 }
850
851 blob_appendf(&manifest, "\n");
852 blob_appendf(&manifest, "R %b\n", &cksum1);
853 if( zBranch && zBranch[0] ){
854 Stmt q;
855 if( zBgColor && zBgColor[0] ){
856 blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor);
857 }
858 blob_appendf(&manifest, "T *branch * %F\n", zBranch);
859 blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
860
861 /* Cancel all other symbolic tags */
862 db_prepare(&q,
863 "SELECT tagname FROM tagxref, tag"
864 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
865 " AND tagtype>0 AND tagname GLOB 'sym-*'"
866 " AND tagname!='sym-'||%Q"
867 " ORDER BY tagname",
868 vid, zBranch);
869 while( db_step(&q)==SQLITE_ROW ){
870 const char *zTag = db_column_text(&q, 0);
871 blob_appendf(&manifest, "T -%F *\n", zTag);
872 }
873 db_finalize(&q);
874 }
875 blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
876 md5sum_blob(&manifest, &mcksum);
877 blob_appendf(&manifest, "Z %b\n", &mcksum);
878 if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
879 Blob ans;
880 blob_zero(&ans);
881 prompt_user("unable to sign manifest. continue (y/N)? ", &ans);
882 if( blob_str(&ans)[0]!='y' ){
883 fossil_exit(1);
884 }
885 }
886 if( !file_exists_in_checkout(vid, "manifest") ){
 
 
 
 
 
 
 
 
887 zManifestFile = mprintf("%smanifest", g.zLocalRoot);
888 blob_write_to_file(&manifest, zManifestFile);
889 blob_reset(&manifest);
890 blob_read_from_file(&manifest, zManifestFile);
891 free(zManifestFile);
@@ -897,11 +1035,11 @@
897 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
898 manifest_crosslink(nvid, &manifest);
899 content_deltify(vid, nvid, 0);
900 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
901 printf("New_Version: %s\n", zUuid);
902 if( !file_exists_in_checkout(vid, "manifest.uuid") ){
903 zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
904 blob_zero(&muuid);
905 blob_appendf(&muuid, "%s\n", zUuid);
906 blob_write_to_file(&muuid, zManifestFile);
907 free(zManifestFile);
@@ -918,45 +1056,51 @@
918 " WHERE file_is_selected(id);"
919 , vid, nvid
920 );
921 db_lset_int("checkout", nvid);
922
923 /* Verify that the repository checksum matches the expected checksum
924 ** calculated before the checkin started (and stored as the R record
925 ** of the manifest file).
926 */
927 vfile_aggregate_checksum_repository(nvid, &cksum2);
928 if( blob_compare(&cksum1, &cksum2) ){
929 fossil_panic("tree checksum does not match repository after commit");
930 }
931
932 /* Verify that the manifest checksum matches the expected checksum */
933 vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
934 if( blob_compare(&cksum1, &cksum1b) ){
935 fossil_panic("manifest checksum does not agree with manifest: "
936 "%b versus %b", &cksum1, &cksum1b);
937 }
938 if( blob_compare(&cksum1, &cksum2) ){
939 fossil_panic("tree checksum does not match manifest after commit: "
940 "%b versus %b", &cksum1, &cksum2);
941 }
942
943 /* Verify that the commit did not modify any disk images. */
944 vfile_aggregate_checksum_disk(nvid, &cksum2);
945 if( blob_compare(&cksum1, &cksum2) ){
946 fossil_panic("tree checksums before and after commit do not match");
 
 
947 }
948
949 /* Clear the undo/redo stack */
950 undo_reset();
951
952 /* Commit */
953 db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
 
 
 
 
954 db_end_transaction(0);
955
956 if( !g.markPrivate ){
957 autosync(AUTOSYNC_PUSH);
958 }
959 if( count_nonbranch_children(vid)>1 ){
960 printf("**** warning: a fork has occurred *****\n");
961 }
962 }
963
--- src/checkin.c
+++ src/checkin.c
@@ -264,10 +264,11 @@
264 Blob repo;
265 Stmt q;
266 int n;
267 const char *zIgnoreFlag = find_option("ignore",0,1);
268 int allFlag = find_option("dotfiles",0,0)!=0;
269 int outputManifest = db_get_boolean("manifest",0);
270
271 db_must_be_within_tree();
272 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
273 n = strlen(g.zLocalRoot);
274 blob_init(&path, g.zLocalRoot, n-1);
@@ -275,16 +276,18 @@
276 zIgnoreFlag = db_get("ignore-glob", 0);
277 }
278 vfile_scan(0, &path, blob_size(&path), allFlag);
279 db_prepare(&q,
280 "SELECT x FROM sfile"
281 " WHERE x NOT IN ('%s','%s','_FOSSIL_',"
282 "'_FOSSIL_-journal','.fos','.fos-journal',"
283 "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal',"
284 "'.fos-shm')"
285 " AND NOT %s"
286 " ORDER BY 1",
287 outputManifest ? "manifest" : "_FOSSIL_",
288 outputManifest ? "manifest.uuid" : "_FOSSIL_",
289 glob_expr("x", zIgnoreFlag)
290 );
291 if( file_tree_name(g.zRepositoryName, &repo, 0) ){
292 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
293 }
@@ -541,21 +544,158 @@
544 zDate[10] = 'T';
545 return zDate;
546 }
547
548 /*
549 ** Create a manifest.
 
 
 
 
 
550 */
551 static void create_manifest(
552 Blob *pOut, /* Write the manifest here */
553 const char *zBaselineUuid, /* UUID of baseline, or zero */
554 Manifest *pBaseline, /* Make it a delta manifest if not zero */
555 Blob *pComment, /* Check-in comment text */
556 int vid, /* blob-id of the parent manifest */
557 int verifyDate, /* Verify that child is younger */
558 Blob *pCksum, /* Repository checksum. May be 0 */
559 const char *zDateOvrd, /* Date override. If 0 then use 'now' */
560 const char *zUserOvrd, /* User override. If 0 then use g.zLogin */
561 const char *zBranch, /* Branch name. May be 0 */
562 const char *zBgColor, /* Background color. May be 0 */
563 int *pnFBcard /* Number of generated B- and F-cards */
564 ){
565 char *zDate; /* Date of the check-in */
566 char *zParentUuid; /* UUID of parent check-in */
567 Blob filename; /* A single filename */
568 int nBasename; /* Size of base filename */
569 Stmt q; /* Query of files changed */
570 Stmt q2; /* Query of merge parents */
571 Blob mcksum; /* Manifest checksum */
572 ManifestFile *pFile; /* File from the baseline */
573 int nFBcard = 0; /* Number of B-cards and F-cards */
574
575 assert( pBaseline==0 || pBaseline->zBaseline==0 );
576 assert( pBaseline==0 || zBaselineUuid!=0 );
577 blob_zero(pOut);
578 zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
579 if( pBaseline ){
580 blob_appendf(pOut, "B %s\n", zBaselineUuid);
581 manifest_file_rewind(pBaseline);
582 pFile = manifest_file_next(pBaseline, 0);
583 nFBcard++;
584 }else{
585 pFile = 0;
586 }
587 blob_appendf(pOut, "C %F\n", blob_str(pComment));
588 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
589 blob_appendf(pOut, "D %s\n", zDate);
590 zDate[10] = ' ';
591 db_prepare(&q,
592 "SELECT pathname, uuid, origname, blob.rid, isexe"
593 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
594 " WHERE NOT deleted AND vfile.vid=%d"
595 " ORDER BY 1", vid);
596 blob_zero(&filename);
597 blob_appendf(&filename, "%s", g.zLocalRoot);
598 nBasename = blob_size(&filename);
599 while( db_step(&q)==SQLITE_ROW ){
600 const char *zName = db_column_text(&q, 0);
601 const char *zUuid = db_column_text(&q, 1);
602 const char *zOrig = db_column_text(&q, 2);
603 int frid = db_column_int(&q, 3);
604 int isexe = db_column_int(&q, 4);
605 const char *zPerm;
606 int cmp;
607 blob_append(&filename, zName, -1);
608 #if !defined(_WIN32)
609 /* For unix, extract the "executable" permission bit directly from
610 ** the filesystem. On windows, the "executable" bit is retained
611 ** unchanged from the original. */
612 isexe = file_isexe(blob_str(&filename));
613 #endif
614 if( isexe ){
615 zPerm = " x";
616 }else{
617 zPerm = "";
618 }
619 if( !g.markPrivate ) content_make_public(frid);
620 while( pFile && strcmp(pFile->zName,zName)<0 ){
621 blob_appendf(pOut, "F %F\n", pFile->zName);
622 pFile = manifest_file_next(pBaseline, 0);
623 nFBcard++;
624 }
625 cmp = 1;
626 if( pFile==0
627 || (cmp = strcmp(pFile->zName,zName))!=0
628 || strcmp(pFile->zUuid, zUuid)!=0
629 ){
630 blob_resize(&filename, nBasename);
631 if( zOrig==0 || strcmp(zOrig,zName)==0 ){
632 blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm);
633 }else{
634 if( zPerm[0]==0 ){ zPerm = " w"; }
635 blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
636 }
637 nFBcard++;
638 }
639 if( cmp==0 ) pFile = manifest_file_next(pBaseline,0);
640 }
641 blob_reset(&filename);
642 db_finalize(&q);
643 while( pFile ){
644 blob_appendf(pOut, "F %F\n", pFile->zName);
645 pFile = manifest_file_next(pBaseline, 0);
646 nFBcard++;
647 }
648 blob_appendf(pOut, "P %s", zParentUuid);
649 if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate);
650 free(zParentUuid);
651 db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
652 db_bind_int(&q2, ":id", 0);
653 while( db_step(&q2)==SQLITE_ROW ){
654 char *zMergeUuid;
655 int mid = db_column_int(&q2, 0);
656 if( !g.markPrivate && content_is_private(mid) ) continue;
657 zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
658 if( zMergeUuid ){
659 blob_appendf(pOut, " %s", zMergeUuid);
660 if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
661 free(zMergeUuid);
662 }
663 }
664 db_finalize(&q2);
665 free(zDate);
666
667 blob_appendf(pOut, "\n");
668 if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum);
669 if( zBranch && zBranch[0] ){
670 Stmt q;
671 if( zBgColor && zBgColor[0] ){
672 blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor);
673 }
674 blob_appendf(pOut, "T *branch * %F\n", zBranch);
675 blob_appendf(pOut, "T *sym-%F *\n", zBranch);
676
677 /* Cancel all other symbolic tags */
678 db_prepare(&q,
679 "SELECT tagname FROM tagxref, tag"
680 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
681 " AND tagtype>0 AND tagname GLOB 'sym-*'"
682 " AND tagname!='sym-'||%Q"
683 " ORDER BY tagname",
684 vid, zBranch);
685 while( db_step(&q)==SQLITE_ROW ){
686 const char *zTag = db_column_text(&q, 0);
687 blob_appendf(pOut, "T -%F *\n", zTag);
688 }
689 db_finalize(&q);
690 }
691 blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
692 md5sum_blob(pOut, &mcksum);
693 blob_appendf(pOut, "Z %b\n", &mcksum);
694 if( pnFBcard ) *pnFBcard = nFBcard;
695 }
696
697
698 /*
699 ** COMMAND: ci
700 ** COMMAND: commit
701 **
@@ -590,39 +730,52 @@
730 ** --branch NEW-BRANCH-NAME
731 ** --bgcolor COLOR
732 ** --nosign
733 ** --force|-f
734 ** --private
735 ** --baseline
736 ** --delta
737 **
738 */
739 void commit_cmd(void){
740 int hasChanges; /* True if unsaved changes exist */
741 int vid; /* blob-id of parent version */
742 int nrid; /* blob-id of a modified file */
743 int nvid; /* Blob-id of the new check-in */
744 Blob comment; /* Check-in comment */
745 const char *zComment; /* Check-in comment */
746 Stmt q; /* Query to find files that have been modified */
747 char *zUuid; /* UUID of the new check-in */
748 int noSign = 0; /* True to omit signing the manifest using GPG */
749 int isAMerge = 0; /* True if checking in a merge */
750 int forceFlag = 0; /* Force a fork */
751 int forceDelta = 0; /* Force a delta-manifest */
752 int forceBaseline = 0; /* Force a baseline-manifest */
753 char *zManifestFile; /* Name of the manifest file */
754 int useCksum; /* True if checksums should be computed and verified */
755 int outputManifest; /* True to output "manifest" and "manifest.uuid" */
756 int testRun; /* True for a test run. Debugging only */
757 const char *zBranch; /* Create a new branch with this name */
758 const char *zBgColor; /* Set background color when branching */
759 const char *zDateOvrd; /* Override date string */
760 const char *zUserOvrd; /* Override user name */
761 const char *zComFile; /* Read commit message from this file */
762 Blob manifest; /* Manifest in baseline form */
 
763 Blob muuid; /* Manifest uuid */
 
764 Blob cksum1, cksum2; /* Before and after commit checksums */
765 Blob cksum1b; /* Checksum recorded in the manifest */
766 int szD; /* Size of the delta manifest */
767 int szB; /* Size of the baseline manifest */
768
769 url_proxy_options();
770 noSign = find_option("nosign",0,0)!=0;
771 forceDelta = find_option("delta",0,0)!=0;
772 forceBaseline = find_option("baseline",0,0)!=0;
773 if( forceDelta && forceBaseline ){
774 fossil_fatal("cannot use --delta and --baseline together");
775 }
776 testRun = find_option("test",0,0)!=0;
777 zComment = find_option("comment","m",1);
778 forceFlag = find_option("force", "f", 0)!=0;
779 zBranch = find_option("branch","b",1);
780 zBgColor = find_option("bgcolor",0,1);
781 zComFile = find_option("message-file", "M", 1);
@@ -634,11 +787,23 @@
787 zDateOvrd = find_option("date-override",0,1);
788 zUserOvrd = find_option("user-override",0,1);
789 db_must_be_within_tree();
790 noSign = db_get_boolean("omitsign", 0)|noSign;
791 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
792 useCksum = db_get_boolean("repo-cksum", 1);
793 outputManifest = db_get_boolean("manifest", 0);
794 verify_all_options();
795
796 /* So that older versions of Fossil (that do not understand delta-
797 ** manifest) can continue to use this repository, do not create a new
798 ** delta-manifest unless this repository already contains one or more
799 ** delta-manifets, or unless the delta-manifest is explicitly requested
800 ** by the --delta option.
801 */
802 if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
803 forceBaseline = 1;
804 }
805
806 /* Get the ID of the parent manifest artifact */
807 vid = db_lget_int("checkout", 0);
808 if( content_is_private(vid) ){
809 g.markPrivate = 1;
@@ -685,14 +850,14 @@
850 */
851 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
852 fossil_fatal("no such user: %s", g.zLogin);
853 }
854
855 hasChanges = unsaved_changes();
856 db_begin_transaction();
857 db_record_repository_filename(0);
858 if( hasChanges==0 && !isAMerge && !forceFlag ){
859 fossil_fatal("nothing has changed");
860 }
861
862 /* If one or more files that were named on the command line have not
863 ** been modified, bail out now.
@@ -724,11 +889,11 @@
889 " WHERE tagid=%d AND rid=%d AND tagtype>0",
890 TAG_CLOSED, vid) ){
891 fossil_fatal("cannot commit against a closed leaf");
892 }
893
894 if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1);
895 if( zComment ){
896 blob_zero(&comment);
897 blob_append(&comment, zComment, -1);
898 }else if( zComFile ){
899 blob_zero(&comment);
@@ -779,113 +944,86 @@
944 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
945 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
946 }
947 db_finalize(&q);
948
949 /* Create the new manifest */
 
950 if( blob_size(&comment)==0 ){
951 blob_append(&comment, "(no comment)", -1);
952 }
953 if( forceDelta ){
954 blob_zero(&manifest);
955 }else{
956 create_manifest(&manifest, 0, 0, &comment, vid,
957 !forceFlag, useCksum ? &cksum1 : 0,
958 zDateOvrd, zUserOvrd, zBranch, zBgColor, &szB);
959 }
960
961 /* See if a delta-manifest would be more appropriate */
962 if( !forceBaseline ){
963 const char *zBaselineUuid;
964 Manifest *pParent;
965 Manifest *pBaseline;
966 pParent = manifest_get(vid, CFTYPE_MANIFEST);
967 if( pParent && pParent->zBaseline ){
968 zBaselineUuid = pParent->zBaseline;
969 pBaseline = manifest_get_by_name(zBaselineUuid, 0);
970 }else{
971 zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
972 pBaseline = pParent;
973 }
974 if( pBaseline ){
975 Blob delta;
976 create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
977 !forceFlag, useCksum ? &cksum1 : 0,
978 zDateOvrd, zUserOvrd, zBranch, zBgColor, &szD);
979 /*
980 ** At this point, two manifests have been constructed, either of
981 ** which would work for this checkin. The first manifest (held
982 ** in the "manifest" variable) is a baseline manifest and the second
983 ** (held in variable named "delta") is a delta manifest. The
984 ** question now is: which manifest should we use?
985 **
986 ** Let B be the number of F-cards in the baseline manifest and
987 ** let D be the number of F-cards in the delta manifest, plus one for
988 ** the B-card. (B is held in the szB variable and D is held in the
989 ** szD variable.) Assume that all delta manifests adds X new F-cards.
990 ** Then to minimize the total number of F- and B-cards in the repository,
991 ** we should use the delta manifest if and only if:
992 **
993 ** D*D < B*X - X*X
994 **
995 ** X is an unknown here, but for most repositories, we will not be
996 ** far wrong if we assume X=3.
997 */
998 if( forceDelta || (szD*szD)<(szB*3-9) ){
999 blob_reset(&manifest);
1000 manifest = delta;
1001 }else{
1002 blob_reset(&delta);
1003 }
1004 }else if( forceDelta ){
1005 fossil_panic("unable to find a baseline-manifest for the delta");
1006 }
1007 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1008 if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
1009 Blob ans;
1010 blob_zero(&ans);
1011 prompt_user("unable to sign manifest. continue (y/N)? ", &ans);
1012 if( blob_str(&ans)[0]!='y' ){
1013 fossil_exit(1);
1014 }
1015 }
1016
1017 /* If the --test option is specified, output the manifest file
1018 ** and rollback the transaction.
1019 */
1020 if( testRun ){
1021 blob_write_to_file(&manifest, "");
1022 }
1023
1024 if( outputManifest ){
1025 zManifestFile = mprintf("%smanifest", g.zLocalRoot);
1026 blob_write_to_file(&manifest, zManifestFile);
1027 blob_reset(&manifest);
1028 blob_read_from_file(&manifest, zManifestFile);
1029 free(zManifestFile);
@@ -897,11 +1035,11 @@
1035 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
1036 manifest_crosslink(nvid, &manifest);
1037 content_deltify(vid, nvid, 0);
1038 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
1039 printf("New_Version: %s\n", zUuid);
1040 if( outputManifest ){
1041 zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
1042 blob_zero(&muuid);
1043 blob_appendf(&muuid, "%s\n", zUuid);
1044 blob_write_to_file(&muuid, zManifestFile);
1045 free(zManifestFile);
@@ -918,45 +1056,51 @@
1056 " WHERE file_is_selected(id);"
1057 , vid, nvid
1058 );
1059 db_lset_int("checkout", nvid);
1060
1061 if( useCksum ){
1062 /* Verify that the repository checksum matches the expected checksum
1063 ** calculated before the checkin started (and stored as the R record
1064 ** of the manifest file).
1065 */
1066 vfile_aggregate_checksum_repository(nvid, &cksum2);
1067 if( blob_compare(&cksum1, &cksum2) ){
1068 fossil_panic("tree checksum does not match repository after commit");
1069 }
1070
1071 /* Verify that the manifest checksum matches the expected checksum */
1072 vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
1073 if( blob_compare(&cksum1, &cksum1b) ){
1074 fossil_panic("manifest checksum does not agree with manifest: "
1075 "%b versus %b", &cksum1, &cksum1b);
1076 }
1077 if( blob_compare(&cksum1, &cksum2) ){
1078 fossil_panic("tree checksum does not match manifest after commit: "
1079 "%b versus %b", &cksum1, &cksum2);
1080 }
1081
1082 /* Verify that the commit did not modify any disk images. */
1083 vfile_aggregate_checksum_disk(nvid, &cksum2);
1084 if( blob_compare(&cksum1, &cksum2) ){
1085 fossil_panic("tree checksums before and after commit do not match");
1086 }
1087 }
1088
1089 /* Clear the undo/redo stack */
1090 undo_reset();
1091
1092 /* Commit */
1093 db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
1094 if( testRun ){
1095 db_end_transaction(1);
1096 exit(1);
1097 }
1098 db_end_transaction(0);
1099
1100 if( !g.markPrivate ){
1101 autosync(AUTOSYNC_PUSH);
1102 }
1103 if( count_nonbranch_children(vid)>1 ){
1104 printf("**** warning: a fork has occurred *****\n");
1105 }
1106 }
1107
+285 -141
--- src/checkin.c
+++ src/checkin.c
@@ -264,10 +264,11 @@
264264
Blob repo;
265265
Stmt q;
266266
int n;
267267
const char *zIgnoreFlag = find_option("ignore",0,1);
268268
int allFlag = find_option("dotfiles",0,0)!=0;
269
+ int outputManifest = db_get_boolean("manifest",0);
269270
270271
db_must_be_within_tree();
271272
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
272273
n = strlen(g.zLocalRoot);
273274
blob_init(&path, g.zLocalRoot, n-1);
@@ -275,16 +276,18 @@
275276
zIgnoreFlag = db_get("ignore-glob", 0);
276277
}
277278
vfile_scan(0, &path, blob_size(&path), allFlag);
278279
db_prepare(&q,
279280
"SELECT x FROM sfile"
280
- " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_',"
281
+ " WHERE x NOT IN ('%s','%s','_FOSSIL_',"
281282
"'_FOSSIL_-journal','.fos','.fos-journal',"
282283
"'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal',"
283284
"'.fos-shm')"
284285
" AND NOT %s"
285286
" ORDER BY 1",
287
+ outputManifest ? "manifest" : "_FOSSIL_",
288
+ outputManifest ? "manifest.uuid" : "_FOSSIL_",
286289
glob_expr("x", zIgnoreFlag)
287290
);
288291
if( file_tree_name(g.zRepositoryName, &repo, 0) ){
289292
db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
290293
}
@@ -541,21 +544,158 @@
541544
zDate[10] = 'T';
542545
return zDate;
543546
}
544547
545548
/*
546
-** Return TRUE (non-zero) if a file named "zFilename" exists in
547
-** the checkout identified by vid.
548
-**
549
-** The original purpose of this routine was to check for the presence of
550
-** a "checked-in" file named "manifest" or "manifest.uuid" so as to avoid
551
-** overwriting that file with automatically generated files.
549
+** Create a manifest.
552550
*/
553
-int file_exists_in_checkout(int vid, const char *zFilename){
554
- return db_exists("SELECT 1 FROM vfile WHERE vid=%d AND pathname=%Q",
555
- vid, zFilename);
551
+static void create_manifest(
552
+ Blob *pOut, /* Write the manifest here */
553
+ const char *zBaselineUuid, /* UUID of baseline, or zero */
554
+ Manifest *pBaseline, /* Make it a delta manifest if not zero */
555
+ Blob *pComment, /* Check-in comment text */
556
+ int vid, /* blob-id of the parent manifest */
557
+ int verifyDate, /* Verify that child is younger */
558
+ Blob *pCksum, /* Repository checksum. May be 0 */
559
+ const char *zDateOvrd, /* Date override. If 0 then use 'now' */
560
+ const char *zUserOvrd, /* User override. If 0 then use g.zLogin */
561
+ const char *zBranch, /* Branch name. May be 0 */
562
+ const char *zBgColor, /* Background color. May be 0 */
563
+ int *pnFBcard /* Number of generated B- and F-cards */
564
+){
565
+ char *zDate; /* Date of the check-in */
566
+ char *zParentUuid; /* UUID of parent check-in */
567
+ Blob filename; /* A single filename */
568
+ int nBasename; /* Size of base filename */
569
+ Stmt q; /* Query of files changed */
570
+ Stmt q2; /* Query of merge parents */
571
+ Blob mcksum; /* Manifest checksum */
572
+ ManifestFile *pFile; /* File from the baseline */
573
+ int nFBcard = 0; /* Number of B-cards and F-cards */
574
+
575
+ assert( pBaseline==0 || pBaseline->zBaseline==0 );
576
+ assert( pBaseline==0 || zBaselineUuid!=0 );
577
+ blob_zero(pOut);
578
+ zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
579
+ if( pBaseline ){
580
+ blob_appendf(pOut, "B %s\n", zBaselineUuid);
581
+ manifest_file_rewind(pBaseline);
582
+ pFile = manifest_file_next(pBaseline, 0);
583
+ nFBcard++;
584
+ }else{
585
+ pFile = 0;
586
+ }
587
+ blob_appendf(pOut, "C %F\n", blob_str(pComment));
588
+ zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
589
+ blob_appendf(pOut, "D %s\n", zDate);
590
+ zDate[10] = ' ';
591
+ db_prepare(&q,
592
+ "SELECT pathname, uuid, origname, blob.rid, isexe"
593
+ " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
594
+ " WHERE NOT deleted AND vfile.vid=%d"
595
+ " ORDER BY 1", vid);
596
+ blob_zero(&filename);
597
+ blob_appendf(&filename, "%s", g.zLocalRoot);
598
+ nBasename = blob_size(&filename);
599
+ while( db_step(&q)==SQLITE_ROW ){
600
+ const char *zName = db_column_text(&q, 0);
601
+ const char *zUuid = db_column_text(&q, 1);
602
+ const char *zOrig = db_column_text(&q, 2);
603
+ int frid = db_column_int(&q, 3);
604
+ int isexe = db_column_int(&q, 4);
605
+ const char *zPerm;
606
+ int cmp;
607
+ blob_append(&filename, zName, -1);
608
+#if !defined(_WIN32)
609
+ /* For unix, extract the "executable" permission bit directly from
610
+ ** the filesystem. On windows, the "executable" bit is retained
611
+ ** unchanged from the original. */
612
+ isexe = file_isexe(blob_str(&filename));
613
+#endif
614
+ if( isexe ){
615
+ zPerm = " x";
616
+ }else{
617
+ zPerm = "";
618
+ }
619
+ if( !g.markPrivate ) content_make_public(frid);
620
+ while( pFile && strcmp(pFile->zName,zName)<0 ){
621
+ blob_appendf(pOut, "F %F\n", pFile->zName);
622
+ pFile = manifest_file_next(pBaseline, 0);
623
+ nFBcard++;
624
+ }
625
+ cmp = 1;
626
+ if( pFile==0
627
+ || (cmp = strcmp(pFile->zName,zName))!=0
628
+ || strcmp(pFile->zUuid, zUuid)!=0
629
+ ){
630
+ blob_resize(&filename, nBasename);
631
+ if( zOrig==0 || strcmp(zOrig,zName)==0 ){
632
+ blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm);
633
+ }else{
634
+ if( zPerm[0]==0 ){ zPerm = " w"; }
635
+ blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
636
+ }
637
+ nFBcard++;
638
+ }
639
+ if( cmp==0 ) pFile = manifest_file_next(pBaseline,0);
640
+ }
641
+ blob_reset(&filename);
642
+ db_finalize(&q);
643
+ while( pFile ){
644
+ blob_appendf(pOut, "F %F\n", pFile->zName);
645
+ pFile = manifest_file_next(pBaseline, 0);
646
+ nFBcard++;
647
+ }
648
+ blob_appendf(pOut, "P %s", zParentUuid);
649
+ if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate);
650
+ free(zParentUuid);
651
+ db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
652
+ db_bind_int(&q2, ":id", 0);
653
+ while( db_step(&q2)==SQLITE_ROW ){
654
+ char *zMergeUuid;
655
+ int mid = db_column_int(&q2, 0);
656
+ if( !g.markPrivate && content_is_private(mid) ) continue;
657
+ zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
658
+ if( zMergeUuid ){
659
+ blob_appendf(pOut, " %s", zMergeUuid);
660
+ if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
661
+ free(zMergeUuid);
662
+ }
663
+ }
664
+ db_finalize(&q2);
665
+ free(zDate);
666
+
667
+ blob_appendf(pOut, "\n");
668
+ if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum);
669
+ if( zBranch && zBranch[0] ){
670
+ Stmt q;
671
+ if( zBgColor && zBgColor[0] ){
672
+ blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor);
673
+ }
674
+ blob_appendf(pOut, "T *branch * %F\n", zBranch);
675
+ blob_appendf(pOut, "T *sym-%F *\n", zBranch);
676
+
677
+ /* Cancel all other symbolic tags */
678
+ db_prepare(&q,
679
+ "SELECT tagname FROM tagxref, tag"
680
+ " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
681
+ " AND tagtype>0 AND tagname GLOB 'sym-*'"
682
+ " AND tagname!='sym-'||%Q"
683
+ " ORDER BY tagname",
684
+ vid, zBranch);
685
+ while( db_step(&q)==SQLITE_ROW ){
686
+ const char *zTag = db_column_text(&q, 0);
687
+ blob_appendf(pOut, "T -%F *\n", zTag);
688
+ }
689
+ db_finalize(&q);
690
+ }
691
+ blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
692
+ md5sum_blob(pOut, &mcksum);
693
+ blob_appendf(pOut, "Z %b\n", &mcksum);
694
+ if( pnFBcard ) *pnFBcard = nFBcard;
556695
}
696
+
557697
558698
/*
559699
** COMMAND: ci
560700
** COMMAND: commit
561701
**
@@ -590,39 +730,52 @@
590730
** --branch NEW-BRANCH-NAME
591731
** --bgcolor COLOR
592732
** --nosign
593733
** --force|-f
594734
** --private
735
+** --baseline
736
+** --delta
595737
**
596738
*/
597739
void commit_cmd(void){
598
- int rc;
599
- int vid, nrid, nvid;
600
- Blob comment;
601
- const char *zComment;
602
- Stmt q;
603
- Stmt q2;
604
- char *zUuid, *zDate;
740
+ int hasChanges; /* True if unsaved changes exist */
741
+ int vid; /* blob-id of parent version */
742
+ int nrid; /* blob-id of a modified file */
743
+ int nvid; /* Blob-id of the new check-in */
744
+ Blob comment; /* Check-in comment */
745
+ const char *zComment; /* Check-in comment */
746
+ Stmt q; /* Query to find files that have been modified */
747
+ char *zUuid; /* UUID of the new check-in */
605748
int noSign = 0; /* True to omit signing the manifest using GPG */
606749
int isAMerge = 0; /* True if checking in a merge */
607750
int forceFlag = 0; /* Force a fork */
751
+ int forceDelta = 0; /* Force a delta-manifest */
752
+ int forceBaseline = 0; /* Force a baseline-manifest */
608753
char *zManifestFile; /* Name of the manifest file */
609
- int nBasename; /* Length of "g.zLocalRoot/" */
754
+ int useCksum; /* True if checksums should be computed and verified */
755
+ int outputManifest; /* True to output "manifest" and "manifest.uuid" */
756
+ int testRun; /* True for a test run. Debugging only */
610757
const char *zBranch; /* Create a new branch with this name */
611758
const char *zBgColor; /* Set background color when branching */
612759
const char *zDateOvrd; /* Override date string */
613760
const char *zUserOvrd; /* Override user name */
614761
const char *zComFile; /* Read commit message from this file */
615
- Blob filename; /* complete filename */
616
- Blob manifest;
762
+ Blob manifest; /* Manifest in baseline form */
617763
Blob muuid; /* Manifest uuid */
618
- Blob mcksum; /* Self-checksum on the manifest */
619764
Blob cksum1, cksum2; /* Before and after commit checksums */
620765
Blob cksum1b; /* Checksum recorded in the manifest */
766
+ int szD; /* Size of the delta manifest */
767
+ int szB; /* Size of the baseline manifest */
621768
622769
url_proxy_options();
623770
noSign = find_option("nosign",0,0)!=0;
771
+ forceDelta = find_option("delta",0,0)!=0;
772
+ forceBaseline = find_option("baseline",0,0)!=0;
773
+ if( forceDelta && forceBaseline ){
774
+ fossil_fatal("cannot use --delta and --baseline together");
775
+ }
776
+ testRun = find_option("test",0,0)!=0;
624777
zComment = find_option("comment","m",1);
625778
forceFlag = find_option("force", "f", 0)!=0;
626779
zBranch = find_option("branch","b",1);
627780
zBgColor = find_option("bgcolor",0,1);
628781
zComFile = find_option("message-file", "M", 1);
@@ -634,11 +787,23 @@
634787
zDateOvrd = find_option("date-override",0,1);
635788
zUserOvrd = find_option("user-override",0,1);
636789
db_must_be_within_tree();
637790
noSign = db_get_boolean("omitsign", 0)|noSign;
638791
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
792
+ useCksum = db_get_boolean("repo-cksum", 1);
793
+ outputManifest = db_get_boolean("manifest", 0);
639794
verify_all_options();
795
+
796
+ /* So that older versions of Fossil (that do not understand delta-
797
+ ** manifest) can continue to use this repository, do not create a new
798
+ ** delta-manifest unless this repository already contains one or more
799
+ ** delta-manifets, or unless the delta-manifest is explicitly requested
800
+ ** by the --delta option.
801
+ */
802
+ if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
803
+ forceBaseline = 1;
804
+ }
640805
641806
/* Get the ID of the parent manifest artifact */
642807
vid = db_lget_int("checkout", 0);
643808
if( content_is_private(vid) ){
644809
g.markPrivate = 1;
@@ -685,14 +850,14 @@
685850
*/
686851
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
687852
fossil_fatal("no such user: %s", g.zLogin);
688853
}
689854
690
- rc = unsaved_changes();
855
+ hasChanges = unsaved_changes();
691856
db_begin_transaction();
692857
db_record_repository_filename(0);
693
- if( rc==0 && !isAMerge && !forceFlag ){
858
+ if( hasChanges==0 && !isAMerge && !forceFlag ){
694859
fossil_fatal("nothing has changed");
695860
}
696861
697862
/* If one or more files that were named on the command line have not
698863
** been modified, bail out now.
@@ -724,11 +889,11 @@
724889
" WHERE tagid=%d AND rid=%d AND tagtype>0",
725890
TAG_CLOSED, vid) ){
726891
fossil_fatal("cannot commit against a closed leaf");
727892
}
728893
729
- vfile_aggregate_checksum_disk(vid, &cksum1);
894
+ if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1);
730895
if( zComment ){
731896
blob_zero(&comment);
732897
blob_append(&comment, zComment, -1);
733898
}else if( zComFile ){
734899
blob_zero(&comment);
@@ -779,113 +944,86 @@
779944
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
780945
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
781946
}
782947
db_finalize(&q);
783948
784
- /* Create the manifest */
785
- blob_zero(&manifest);
949
+ /* Create the new manifest */
786950
if( blob_size(&comment)==0 ){
787951
blob_append(&comment, "(no comment)", -1);
788952
}
789
- blob_appendf(&manifest, "C %F\n", blob_str(&comment));
790
- zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
791
- blob_appendf(&manifest, "D %s\n", zDate);
792
- zDate[10] = ' ';
793
- db_prepare(&q,
794
- "SELECT pathname, uuid, origname, blob.rid, isexe"
795
- " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
796
- " WHERE NOT deleted AND vfile.vid=%d"
797
- " ORDER BY 1", vid);
798
- blob_zero(&filename);
799
- blob_appendf(&filename, "%s", g.zLocalRoot);
800
- nBasename = blob_size(&filename);
801
- while( db_step(&q)==SQLITE_ROW ){
802
- const char *zName = db_column_text(&q, 0);
803
- const char *zUuid = db_column_text(&q, 1);
804
- const char *zOrig = db_column_text(&q, 2);
805
- int frid = db_column_int(&q, 3);
806
- int isexe = db_column_int(&q, 4);
807
- const char *zPerm;
808
- blob_append(&filename, zName, -1);
809
-#if !defined(_WIN32)
810
- /* For unix, extract the "executable" permission bit directly from
811
- ** the filesystem. On windows, the "executable" bit is retained
812
- ** unchanged from the original. */
813
- isexe = file_isexe(blob_str(&filename));
814
-#endif
815
- if( isexe ){
816
- zPerm = " x";
817
- }else{
818
- zPerm = "";
819
- }
820
- blob_resize(&filename, nBasename);
821
- if( zOrig==0 || strcmp(zOrig,zName)==0 ){
822
- blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm);
823
- }else{
824
- if( zPerm[0]==0 ){ zPerm = " w"; }
825
- blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
826
- }
827
- if( !g.markPrivate ) content_make_public(frid);
828
- }
829
- blob_reset(&filename);
830
- db_finalize(&q);
831
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
832
- blob_appendf(&manifest, "P %s", zUuid);
833
-
834
- if( !forceFlag ){
835
- checkin_verify_younger(vid, zUuid, zDate);
836
- db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
837
- db_bind_int(&q2, ":id", 0);
838
- while( db_step(&q2)==SQLITE_ROW ){
839
- int mid = db_column_int(&q2, 0);
840
- if( !g.markPrivate && content_is_private(mid) ) continue;
841
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
842
- if( zUuid ){
843
- blob_appendf(&manifest, " %s", zUuid);
844
- checkin_verify_younger(mid, zUuid, zDate);
845
- free(zUuid);
846
- }
847
- }
848
- db_finalize(&q2);
849
- }
850
-
851
- blob_appendf(&manifest, "\n");
852
- blob_appendf(&manifest, "R %b\n", &cksum1);
853
- if( zBranch && zBranch[0] ){
854
- Stmt q;
855
- if( zBgColor && zBgColor[0] ){
856
- blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor);
857
- }
858
- blob_appendf(&manifest, "T *branch * %F\n", zBranch);
859
- blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
860
-
861
- /* Cancel all other symbolic tags */
862
- db_prepare(&q,
863
- "SELECT tagname FROM tagxref, tag"
864
- " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
865
- " AND tagtype>0 AND tagname GLOB 'sym-*'"
866
- " AND tagname!='sym-'||%Q"
867
- " ORDER BY tagname",
868
- vid, zBranch);
869
- while( db_step(&q)==SQLITE_ROW ){
870
- const char *zTag = db_column_text(&q, 0);
871
- blob_appendf(&manifest, "T -%F *\n", zTag);
872
- }
873
- db_finalize(&q);
874
- }
875
- blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
876
- md5sum_blob(&manifest, &mcksum);
877
- blob_appendf(&manifest, "Z %b\n", &mcksum);
953
+ if( forceDelta ){
954
+ blob_zero(&manifest);
955
+ }else{
956
+ create_manifest(&manifest, 0, 0, &comment, vid,
957
+ !forceFlag, useCksum ? &cksum1 : 0,
958
+ zDateOvrd, zUserOvrd, zBranch, zBgColor, &szB);
959
+ }
960
+
961
+ /* See if a delta-manifest would be more appropriate */
962
+ if( !forceBaseline ){
963
+ const char *zBaselineUuid;
964
+ Manifest *pParent;
965
+ Manifest *pBaseline;
966
+ pParent = manifest_get(vid, CFTYPE_MANIFEST);
967
+ if( pParent && pParent->zBaseline ){
968
+ zBaselineUuid = pParent->zBaseline;
969
+ pBaseline = manifest_get_by_name(zBaselineUuid, 0);
970
+ }else{
971
+ zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
972
+ pBaseline = pParent;
973
+ }
974
+ if( pBaseline ){
975
+ Blob delta;
976
+ create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
977
+ !forceFlag, useCksum ? &cksum1 : 0,
978
+ zDateOvrd, zUserOvrd, zBranch, zBgColor, &szD);
979
+ /*
980
+ ** At this point, two manifests have been constructed, either of
981
+ ** which would work for this checkin. The first manifest (held
982
+ ** in the "manifest" variable) is a baseline manifest and the second
983
+ ** (held in variable named "delta") is a delta manifest. The
984
+ ** question now is: which manifest should we use?
985
+ **
986
+ ** Let B be the number of F-cards in the baseline manifest and
987
+ ** let D be the number of F-cards in the delta manifest, plus one for
988
+ ** the B-card. (B is held in the szB variable and D is held in the
989
+ ** szD variable.) Assume that all delta manifests adds X new F-cards.
990
+ ** Then to minimize the total number of F- and B-cards in the repository,
991
+ ** we should use the delta manifest if and only if:
992
+ **
993
+ ** D*D < B*X - X*X
994
+ **
995
+ ** X is an unknown here, but for most repositories, we will not be
996
+ ** far wrong if we assume X=3.
997
+ */
998
+ if( forceDelta || (szD*szD)<(szB*3-9) ){
999
+ blob_reset(&manifest);
1000
+ manifest = delta;
1001
+ }else{
1002
+ blob_reset(&delta);
1003
+ }
1004
+ }else if( forceDelta ){
1005
+ fossil_panic("unable to find a baseline-manifest for the delta");
1006
+ }
1007
+ }
8781008
if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
8791009
Blob ans;
8801010
blob_zero(&ans);
8811011
prompt_user("unable to sign manifest. continue (y/N)? ", &ans);
8821012
if( blob_str(&ans)[0]!='y' ){
8831013
fossil_exit(1);
8841014
}
8851015
}
886
- if( !file_exists_in_checkout(vid, "manifest") ){
1016
+
1017
+ /* If the --test option is specified, output the manifest file
1018
+ ** and rollback the transaction.
1019
+ */
1020
+ if( testRun ){
1021
+ blob_write_to_file(&manifest, "");
1022
+ }
1023
+
1024
+ if( outputManifest ){
8871025
zManifestFile = mprintf("%smanifest", g.zLocalRoot);
8881026
blob_write_to_file(&manifest, zManifestFile);
8891027
blob_reset(&manifest);
8901028
blob_read_from_file(&manifest, zManifestFile);
8911029
free(zManifestFile);
@@ -897,11 +1035,11 @@
8971035
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
8981036
manifest_crosslink(nvid, &manifest);
8991037
content_deltify(vid, nvid, 0);
9001038
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
9011039
printf("New_Version: %s\n", zUuid);
902
- if( !file_exists_in_checkout(vid, "manifest.uuid") ){
1040
+ if( outputManifest ){
9031041
zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
9041042
blob_zero(&muuid);
9051043
blob_appendf(&muuid, "%s\n", zUuid);
9061044
blob_write_to_file(&muuid, zManifestFile);
9071045
free(zManifestFile);
@@ -918,45 +1056,51 @@
9181056
" WHERE file_is_selected(id);"
9191057
, vid, nvid
9201058
);
9211059
db_lset_int("checkout", nvid);
9221060
923
- /* Verify that the repository checksum matches the expected checksum
924
- ** calculated before the checkin started (and stored as the R record
925
- ** of the manifest file).
926
- */
927
- vfile_aggregate_checksum_repository(nvid, &cksum2);
928
- if( blob_compare(&cksum1, &cksum2) ){
929
- fossil_panic("tree checksum does not match repository after commit");
930
- }
931
-
932
- /* Verify that the manifest checksum matches the expected checksum */
933
- vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
934
- if( blob_compare(&cksum1, &cksum1b) ){
935
- fossil_panic("manifest checksum does not agree with manifest: "
936
- "%b versus %b", &cksum1, &cksum1b);
937
- }
938
- if( blob_compare(&cksum1, &cksum2) ){
939
- fossil_panic("tree checksum does not match manifest after commit: "
940
- "%b versus %b", &cksum1, &cksum2);
941
- }
942
-
943
- /* Verify that the commit did not modify any disk images. */
944
- vfile_aggregate_checksum_disk(nvid, &cksum2);
945
- if( blob_compare(&cksum1, &cksum2) ){
946
- fossil_panic("tree checksums before and after commit do not match");
1061
+ if( useCksum ){
1062
+ /* Verify that the repository checksum matches the expected checksum
1063
+ ** calculated before the checkin started (and stored as the R record
1064
+ ** of the manifest file).
1065
+ */
1066
+ vfile_aggregate_checksum_repository(nvid, &cksum2);
1067
+ if( blob_compare(&cksum1, &cksum2) ){
1068
+ fossil_panic("tree checksum does not match repository after commit");
1069
+ }
1070
+
1071
+ /* Verify that the manifest checksum matches the expected checksum */
1072
+ vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
1073
+ if( blob_compare(&cksum1, &cksum1b) ){
1074
+ fossil_panic("manifest checksum does not agree with manifest: "
1075
+ "%b versus %b", &cksum1, &cksum1b);
1076
+ }
1077
+ if( blob_compare(&cksum1, &cksum2) ){
1078
+ fossil_panic("tree checksum does not match manifest after commit: "
1079
+ "%b versus %b", &cksum1, &cksum2);
1080
+ }
1081
+
1082
+ /* Verify that the commit did not modify any disk images. */
1083
+ vfile_aggregate_checksum_disk(nvid, &cksum2);
1084
+ if( blob_compare(&cksum1, &cksum2) ){
1085
+ fossil_panic("tree checksums before and after commit do not match");
1086
+ }
9471087
}
9481088
9491089
/* Clear the undo/redo stack */
9501090
undo_reset();
9511091
9521092
/* Commit */
9531093
db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
1094
+ if( testRun ){
1095
+ db_end_transaction(1);
1096
+ exit(1);
1097
+ }
9541098
db_end_transaction(0);
9551099
9561100
if( !g.markPrivate ){
9571101
autosync(AUTOSYNC_PUSH);
9581102
}
9591103
if( count_nonbranch_children(vid)>1 ){
9601104
printf("**** warning: a fork has occurred *****\n");
9611105
}
9621106
}
9631107
--- src/checkin.c
+++ src/checkin.c
@@ -264,10 +264,11 @@
264 Blob repo;
265 Stmt q;
266 int n;
267 const char *zIgnoreFlag = find_option("ignore",0,1);
268 int allFlag = find_option("dotfiles",0,0)!=0;
 
269
270 db_must_be_within_tree();
271 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
272 n = strlen(g.zLocalRoot);
273 blob_init(&path, g.zLocalRoot, n-1);
@@ -275,16 +276,18 @@
275 zIgnoreFlag = db_get("ignore-glob", 0);
276 }
277 vfile_scan(0, &path, blob_size(&path), allFlag);
278 db_prepare(&q,
279 "SELECT x FROM sfile"
280 " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_',"
281 "'_FOSSIL_-journal','.fos','.fos-journal',"
282 "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal',"
283 "'.fos-shm')"
284 " AND NOT %s"
285 " ORDER BY 1",
 
 
286 glob_expr("x", zIgnoreFlag)
287 );
288 if( file_tree_name(g.zRepositoryName, &repo, 0) ){
289 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
290 }
@@ -541,21 +544,158 @@
541 zDate[10] = 'T';
542 return zDate;
543 }
544
545 /*
546 ** Return TRUE (non-zero) if a file named "zFilename" exists in
547 ** the checkout identified by vid.
548 **
549 ** The original purpose of this routine was to check for the presence of
550 ** a "checked-in" file named "manifest" or "manifest.uuid" so as to avoid
551 ** overwriting that file with automatically generated files.
552 */
553 int file_exists_in_checkout(int vid, const char *zFilename){
554 return db_exists("SELECT 1 FROM vfile WHERE vid=%d AND pathname=%Q",
555 vid, zFilename);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556 }
 
557
558 /*
559 ** COMMAND: ci
560 ** COMMAND: commit
561 **
@@ -590,39 +730,52 @@
590 ** --branch NEW-BRANCH-NAME
591 ** --bgcolor COLOR
592 ** --nosign
593 ** --force|-f
594 ** --private
 
 
595 **
596 */
597 void commit_cmd(void){
598 int rc;
599 int vid, nrid, nvid;
600 Blob comment;
601 const char *zComment;
602 Stmt q;
603 Stmt q2;
604 char *zUuid, *zDate;
 
605 int noSign = 0; /* True to omit signing the manifest using GPG */
606 int isAMerge = 0; /* True if checking in a merge */
607 int forceFlag = 0; /* Force a fork */
 
 
608 char *zManifestFile; /* Name of the manifest file */
609 int nBasename; /* Length of "g.zLocalRoot/" */
 
 
610 const char *zBranch; /* Create a new branch with this name */
611 const char *zBgColor; /* Set background color when branching */
612 const char *zDateOvrd; /* Override date string */
613 const char *zUserOvrd; /* Override user name */
614 const char *zComFile; /* Read commit message from this file */
615 Blob filename; /* complete filename */
616 Blob manifest;
617 Blob muuid; /* Manifest uuid */
618 Blob mcksum; /* Self-checksum on the manifest */
619 Blob cksum1, cksum2; /* Before and after commit checksums */
620 Blob cksum1b; /* Checksum recorded in the manifest */
 
 
621
622 url_proxy_options();
623 noSign = find_option("nosign",0,0)!=0;
 
 
 
 
 
 
624 zComment = find_option("comment","m",1);
625 forceFlag = find_option("force", "f", 0)!=0;
626 zBranch = find_option("branch","b",1);
627 zBgColor = find_option("bgcolor",0,1);
628 zComFile = find_option("message-file", "M", 1);
@@ -634,11 +787,23 @@
634 zDateOvrd = find_option("date-override",0,1);
635 zUserOvrd = find_option("user-override",0,1);
636 db_must_be_within_tree();
637 noSign = db_get_boolean("omitsign", 0)|noSign;
638 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
 
 
639 verify_all_options();
 
 
 
 
 
 
 
 
 
 
640
641 /* Get the ID of the parent manifest artifact */
642 vid = db_lget_int("checkout", 0);
643 if( content_is_private(vid) ){
644 g.markPrivate = 1;
@@ -685,14 +850,14 @@
685 */
686 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
687 fossil_fatal("no such user: %s", g.zLogin);
688 }
689
690 rc = unsaved_changes();
691 db_begin_transaction();
692 db_record_repository_filename(0);
693 if( rc==0 && !isAMerge && !forceFlag ){
694 fossil_fatal("nothing has changed");
695 }
696
697 /* If one or more files that were named on the command line have not
698 ** been modified, bail out now.
@@ -724,11 +889,11 @@
724 " WHERE tagid=%d AND rid=%d AND tagtype>0",
725 TAG_CLOSED, vid) ){
726 fossil_fatal("cannot commit against a closed leaf");
727 }
728
729 vfile_aggregate_checksum_disk(vid, &cksum1);
730 if( zComment ){
731 blob_zero(&comment);
732 blob_append(&comment, zComment, -1);
733 }else if( zComFile ){
734 blob_zero(&comment);
@@ -779,113 +944,86 @@
779 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
780 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
781 }
782 db_finalize(&q);
783
784 /* Create the manifest */
785 blob_zero(&manifest);
786 if( blob_size(&comment)==0 ){
787 blob_append(&comment, "(no comment)", -1);
788 }
789 blob_appendf(&manifest, "C %F\n", blob_str(&comment));
790 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
791 blob_appendf(&manifest, "D %s\n", zDate);
792 zDate[10] = ' ';
793 db_prepare(&q,
794 "SELECT pathname, uuid, origname, blob.rid, isexe"
795 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
796 " WHERE NOT deleted AND vfile.vid=%d"
797 " ORDER BY 1", vid);
798 blob_zero(&filename);
799 blob_appendf(&filename, "%s", g.zLocalRoot);
800 nBasename = blob_size(&filename);
801 while( db_step(&q)==SQLITE_ROW ){
802 const char *zName = db_column_text(&q, 0);
803 const char *zUuid = db_column_text(&q, 1);
804 const char *zOrig = db_column_text(&q, 2);
805 int frid = db_column_int(&q, 3);
806 int isexe = db_column_int(&q, 4);
807 const char *zPerm;
808 blob_append(&filename, zName, -1);
809 #if !defined(_WIN32)
810 /* For unix, extract the "executable" permission bit directly from
811 ** the filesystem. On windows, the "executable" bit is retained
812 ** unchanged from the original. */
813 isexe = file_isexe(blob_str(&filename));
814 #endif
815 if( isexe ){
816 zPerm = " x";
817 }else{
818 zPerm = "";
819 }
820 blob_resize(&filename, nBasename);
821 if( zOrig==0 || strcmp(zOrig,zName)==0 ){
822 blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm);
823 }else{
824 if( zPerm[0]==0 ){ zPerm = " w"; }
825 blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
826 }
827 if( !g.markPrivate ) content_make_public(frid);
828 }
829 blob_reset(&filename);
830 db_finalize(&q);
831 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
832 blob_appendf(&manifest, "P %s", zUuid);
833
834 if( !forceFlag ){
835 checkin_verify_younger(vid, zUuid, zDate);
836 db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
837 db_bind_int(&q2, ":id", 0);
838 while( db_step(&q2)==SQLITE_ROW ){
839 int mid = db_column_int(&q2, 0);
840 if( !g.markPrivate && content_is_private(mid) ) continue;
841 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
842 if( zUuid ){
843 blob_appendf(&manifest, " %s", zUuid);
844 checkin_verify_younger(mid, zUuid, zDate);
845 free(zUuid);
846 }
847 }
848 db_finalize(&q2);
849 }
850
851 blob_appendf(&manifest, "\n");
852 blob_appendf(&manifest, "R %b\n", &cksum1);
853 if( zBranch && zBranch[0] ){
854 Stmt q;
855 if( zBgColor && zBgColor[0] ){
856 blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor);
857 }
858 blob_appendf(&manifest, "T *branch * %F\n", zBranch);
859 blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
860
861 /* Cancel all other symbolic tags */
862 db_prepare(&q,
863 "SELECT tagname FROM tagxref, tag"
864 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
865 " AND tagtype>0 AND tagname GLOB 'sym-*'"
866 " AND tagname!='sym-'||%Q"
867 " ORDER BY tagname",
868 vid, zBranch);
869 while( db_step(&q)==SQLITE_ROW ){
870 const char *zTag = db_column_text(&q, 0);
871 blob_appendf(&manifest, "T -%F *\n", zTag);
872 }
873 db_finalize(&q);
874 }
875 blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
876 md5sum_blob(&manifest, &mcksum);
877 blob_appendf(&manifest, "Z %b\n", &mcksum);
878 if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
879 Blob ans;
880 blob_zero(&ans);
881 prompt_user("unable to sign manifest. continue (y/N)? ", &ans);
882 if( blob_str(&ans)[0]!='y' ){
883 fossil_exit(1);
884 }
885 }
886 if( !file_exists_in_checkout(vid, "manifest") ){
 
 
 
 
 
 
 
 
887 zManifestFile = mprintf("%smanifest", g.zLocalRoot);
888 blob_write_to_file(&manifest, zManifestFile);
889 blob_reset(&manifest);
890 blob_read_from_file(&manifest, zManifestFile);
891 free(zManifestFile);
@@ -897,11 +1035,11 @@
897 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
898 manifest_crosslink(nvid, &manifest);
899 content_deltify(vid, nvid, 0);
900 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
901 printf("New_Version: %s\n", zUuid);
902 if( !file_exists_in_checkout(vid, "manifest.uuid") ){
903 zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
904 blob_zero(&muuid);
905 blob_appendf(&muuid, "%s\n", zUuid);
906 blob_write_to_file(&muuid, zManifestFile);
907 free(zManifestFile);
@@ -918,45 +1056,51 @@
918 " WHERE file_is_selected(id);"
919 , vid, nvid
920 );
921 db_lset_int("checkout", nvid);
922
923 /* Verify that the repository checksum matches the expected checksum
924 ** calculated before the checkin started (and stored as the R record
925 ** of the manifest file).
926 */
927 vfile_aggregate_checksum_repository(nvid, &cksum2);
928 if( blob_compare(&cksum1, &cksum2) ){
929 fossil_panic("tree checksum does not match repository after commit");
930 }
931
932 /* Verify that the manifest checksum matches the expected checksum */
933 vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
934 if( blob_compare(&cksum1, &cksum1b) ){
935 fossil_panic("manifest checksum does not agree with manifest: "
936 "%b versus %b", &cksum1, &cksum1b);
937 }
938 if( blob_compare(&cksum1, &cksum2) ){
939 fossil_panic("tree checksum does not match manifest after commit: "
940 "%b versus %b", &cksum1, &cksum2);
941 }
942
943 /* Verify that the commit did not modify any disk images. */
944 vfile_aggregate_checksum_disk(nvid, &cksum2);
945 if( blob_compare(&cksum1, &cksum2) ){
946 fossil_panic("tree checksums before and after commit do not match");
 
 
947 }
948
949 /* Clear the undo/redo stack */
950 undo_reset();
951
952 /* Commit */
953 db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
 
 
 
 
954 db_end_transaction(0);
955
956 if( !g.markPrivate ){
957 autosync(AUTOSYNC_PUSH);
958 }
959 if( count_nonbranch_children(vid)>1 ){
960 printf("**** warning: a fork has occurred *****\n");
961 }
962 }
963
--- src/checkin.c
+++ src/checkin.c
@@ -264,10 +264,11 @@
264 Blob repo;
265 Stmt q;
266 int n;
267 const char *zIgnoreFlag = find_option("ignore",0,1);
268 int allFlag = find_option("dotfiles",0,0)!=0;
269 int outputManifest = db_get_boolean("manifest",0);
270
271 db_must_be_within_tree();
272 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
273 n = strlen(g.zLocalRoot);
274 blob_init(&path, g.zLocalRoot, n-1);
@@ -275,16 +276,18 @@
276 zIgnoreFlag = db_get("ignore-glob", 0);
277 }
278 vfile_scan(0, &path, blob_size(&path), allFlag);
279 db_prepare(&q,
280 "SELECT x FROM sfile"
281 " WHERE x NOT IN ('%s','%s','_FOSSIL_',"
282 "'_FOSSIL_-journal','.fos','.fos-journal',"
283 "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal',"
284 "'.fos-shm')"
285 " AND NOT %s"
286 " ORDER BY 1",
287 outputManifest ? "manifest" : "_FOSSIL_",
288 outputManifest ? "manifest.uuid" : "_FOSSIL_",
289 glob_expr("x", zIgnoreFlag)
290 );
291 if( file_tree_name(g.zRepositoryName, &repo, 0) ){
292 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
293 }
@@ -541,21 +544,158 @@
544 zDate[10] = 'T';
545 return zDate;
546 }
547
548 /*
549 ** Create a manifest.
 
 
 
 
 
550 */
551 static void create_manifest(
552 Blob *pOut, /* Write the manifest here */
553 const char *zBaselineUuid, /* UUID of baseline, or zero */
554 Manifest *pBaseline, /* Make it a delta manifest if not zero */
555 Blob *pComment, /* Check-in comment text */
556 int vid, /* blob-id of the parent manifest */
557 int verifyDate, /* Verify that child is younger */
558 Blob *pCksum, /* Repository checksum. May be 0 */
559 const char *zDateOvrd, /* Date override. If 0 then use 'now' */
560 const char *zUserOvrd, /* User override. If 0 then use g.zLogin */
561 const char *zBranch, /* Branch name. May be 0 */
562 const char *zBgColor, /* Background color. May be 0 */
563 int *pnFBcard /* Number of generated B- and F-cards */
564 ){
565 char *zDate; /* Date of the check-in */
566 char *zParentUuid; /* UUID of parent check-in */
567 Blob filename; /* A single filename */
568 int nBasename; /* Size of base filename */
569 Stmt q; /* Query of files changed */
570 Stmt q2; /* Query of merge parents */
571 Blob mcksum; /* Manifest checksum */
572 ManifestFile *pFile; /* File from the baseline */
573 int nFBcard = 0; /* Number of B-cards and F-cards */
574
575 assert( pBaseline==0 || pBaseline->zBaseline==0 );
576 assert( pBaseline==0 || zBaselineUuid!=0 );
577 blob_zero(pOut);
578 zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
579 if( pBaseline ){
580 blob_appendf(pOut, "B %s\n", zBaselineUuid);
581 manifest_file_rewind(pBaseline);
582 pFile = manifest_file_next(pBaseline, 0);
583 nFBcard++;
584 }else{
585 pFile = 0;
586 }
587 blob_appendf(pOut, "C %F\n", blob_str(pComment));
588 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
589 blob_appendf(pOut, "D %s\n", zDate);
590 zDate[10] = ' ';
591 db_prepare(&q,
592 "SELECT pathname, uuid, origname, blob.rid, isexe"
593 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
594 " WHERE NOT deleted AND vfile.vid=%d"
595 " ORDER BY 1", vid);
596 blob_zero(&filename);
597 blob_appendf(&filename, "%s", g.zLocalRoot);
598 nBasename = blob_size(&filename);
599 while( db_step(&q)==SQLITE_ROW ){
600 const char *zName = db_column_text(&q, 0);
601 const char *zUuid = db_column_text(&q, 1);
602 const char *zOrig = db_column_text(&q, 2);
603 int frid = db_column_int(&q, 3);
604 int isexe = db_column_int(&q, 4);
605 const char *zPerm;
606 int cmp;
607 blob_append(&filename, zName, -1);
608 #if !defined(_WIN32)
609 /* For unix, extract the "executable" permission bit directly from
610 ** the filesystem. On windows, the "executable" bit is retained
611 ** unchanged from the original. */
612 isexe = file_isexe(blob_str(&filename));
613 #endif
614 if( isexe ){
615 zPerm = " x";
616 }else{
617 zPerm = "";
618 }
619 if( !g.markPrivate ) content_make_public(frid);
620 while( pFile && strcmp(pFile->zName,zName)<0 ){
621 blob_appendf(pOut, "F %F\n", pFile->zName);
622 pFile = manifest_file_next(pBaseline, 0);
623 nFBcard++;
624 }
625 cmp = 1;
626 if( pFile==0
627 || (cmp = strcmp(pFile->zName,zName))!=0
628 || strcmp(pFile->zUuid, zUuid)!=0
629 ){
630 blob_resize(&filename, nBasename);
631 if( zOrig==0 || strcmp(zOrig,zName)==0 ){
632 blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm);
633 }else{
634 if( zPerm[0]==0 ){ zPerm = " w"; }
635 blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
636 }
637 nFBcard++;
638 }
639 if( cmp==0 ) pFile = manifest_file_next(pBaseline,0);
640 }
641 blob_reset(&filename);
642 db_finalize(&q);
643 while( pFile ){
644 blob_appendf(pOut, "F %F\n", pFile->zName);
645 pFile = manifest_file_next(pBaseline, 0);
646 nFBcard++;
647 }
648 blob_appendf(pOut, "P %s", zParentUuid);
649 if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate);
650 free(zParentUuid);
651 db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
652 db_bind_int(&q2, ":id", 0);
653 while( db_step(&q2)==SQLITE_ROW ){
654 char *zMergeUuid;
655 int mid = db_column_int(&q2, 0);
656 if( !g.markPrivate && content_is_private(mid) ) continue;
657 zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
658 if( zMergeUuid ){
659 blob_appendf(pOut, " %s", zMergeUuid);
660 if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
661 free(zMergeUuid);
662 }
663 }
664 db_finalize(&q2);
665 free(zDate);
666
667 blob_appendf(pOut, "\n");
668 if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum);
669 if( zBranch && zBranch[0] ){
670 Stmt q;
671 if( zBgColor && zBgColor[0] ){
672 blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor);
673 }
674 blob_appendf(pOut, "T *branch * %F\n", zBranch);
675 blob_appendf(pOut, "T *sym-%F *\n", zBranch);
676
677 /* Cancel all other symbolic tags */
678 db_prepare(&q,
679 "SELECT tagname FROM tagxref, tag"
680 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
681 " AND tagtype>0 AND tagname GLOB 'sym-*'"
682 " AND tagname!='sym-'||%Q"
683 " ORDER BY tagname",
684 vid, zBranch);
685 while( db_step(&q)==SQLITE_ROW ){
686 const char *zTag = db_column_text(&q, 0);
687 blob_appendf(pOut, "T -%F *\n", zTag);
688 }
689 db_finalize(&q);
690 }
691 blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
692 md5sum_blob(pOut, &mcksum);
693 blob_appendf(pOut, "Z %b\n", &mcksum);
694 if( pnFBcard ) *pnFBcard = nFBcard;
695 }
696
697
698 /*
699 ** COMMAND: ci
700 ** COMMAND: commit
701 **
@@ -590,39 +730,52 @@
730 ** --branch NEW-BRANCH-NAME
731 ** --bgcolor COLOR
732 ** --nosign
733 ** --force|-f
734 ** --private
735 ** --baseline
736 ** --delta
737 **
738 */
739 void commit_cmd(void){
740 int hasChanges; /* True if unsaved changes exist */
741 int vid; /* blob-id of parent version */
742 int nrid; /* blob-id of a modified file */
743 int nvid; /* Blob-id of the new check-in */
744 Blob comment; /* Check-in comment */
745 const char *zComment; /* Check-in comment */
746 Stmt q; /* Query to find files that have been modified */
747 char *zUuid; /* UUID of the new check-in */
748 int noSign = 0; /* True to omit signing the manifest using GPG */
749 int isAMerge = 0; /* True if checking in a merge */
750 int forceFlag = 0; /* Force a fork */
751 int forceDelta = 0; /* Force a delta-manifest */
752 int forceBaseline = 0; /* Force a baseline-manifest */
753 char *zManifestFile; /* Name of the manifest file */
754 int useCksum; /* True if checksums should be computed and verified */
755 int outputManifest; /* True to output "manifest" and "manifest.uuid" */
756 int testRun; /* True for a test run. Debugging only */
757 const char *zBranch; /* Create a new branch with this name */
758 const char *zBgColor; /* Set background color when branching */
759 const char *zDateOvrd; /* Override date string */
760 const char *zUserOvrd; /* Override user name */
761 const char *zComFile; /* Read commit message from this file */
762 Blob manifest; /* Manifest in baseline form */
 
763 Blob muuid; /* Manifest uuid */
 
764 Blob cksum1, cksum2; /* Before and after commit checksums */
765 Blob cksum1b; /* Checksum recorded in the manifest */
766 int szD; /* Size of the delta manifest */
767 int szB; /* Size of the baseline manifest */
768
769 url_proxy_options();
770 noSign = find_option("nosign",0,0)!=0;
771 forceDelta = find_option("delta",0,0)!=0;
772 forceBaseline = find_option("baseline",0,0)!=0;
773 if( forceDelta && forceBaseline ){
774 fossil_fatal("cannot use --delta and --baseline together");
775 }
776 testRun = find_option("test",0,0)!=0;
777 zComment = find_option("comment","m",1);
778 forceFlag = find_option("force", "f", 0)!=0;
779 zBranch = find_option("branch","b",1);
780 zBgColor = find_option("bgcolor",0,1);
781 zComFile = find_option("message-file", "M", 1);
@@ -634,11 +787,23 @@
787 zDateOvrd = find_option("date-override",0,1);
788 zUserOvrd = find_option("user-override",0,1);
789 db_must_be_within_tree();
790 noSign = db_get_boolean("omitsign", 0)|noSign;
791 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
792 useCksum = db_get_boolean("repo-cksum", 1);
793 outputManifest = db_get_boolean("manifest", 0);
794 verify_all_options();
795
796 /* So that older versions of Fossil (that do not understand delta-
797 ** manifest) can continue to use this repository, do not create a new
798 ** delta-manifest unless this repository already contains one or more
799 ** delta-manifets, or unless the delta-manifest is explicitly requested
800 ** by the --delta option.
801 */
802 if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
803 forceBaseline = 1;
804 }
805
806 /* Get the ID of the parent manifest artifact */
807 vid = db_lget_int("checkout", 0);
808 if( content_is_private(vid) ){
809 g.markPrivate = 1;
@@ -685,14 +850,14 @@
850 */
851 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
852 fossil_fatal("no such user: %s", g.zLogin);
853 }
854
855 hasChanges = unsaved_changes();
856 db_begin_transaction();
857 db_record_repository_filename(0);
858 if( hasChanges==0 && !isAMerge && !forceFlag ){
859 fossil_fatal("nothing has changed");
860 }
861
862 /* If one or more files that were named on the command line have not
863 ** been modified, bail out now.
@@ -724,11 +889,11 @@
889 " WHERE tagid=%d AND rid=%d AND tagtype>0",
890 TAG_CLOSED, vid) ){
891 fossil_fatal("cannot commit against a closed leaf");
892 }
893
894 if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1);
895 if( zComment ){
896 blob_zero(&comment);
897 blob_append(&comment, zComment, -1);
898 }else if( zComFile ){
899 blob_zero(&comment);
@@ -779,113 +944,86 @@
944 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
945 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
946 }
947 db_finalize(&q);
948
949 /* Create the new manifest */
 
950 if( blob_size(&comment)==0 ){
951 blob_append(&comment, "(no comment)", -1);
952 }
953 if( forceDelta ){
954 blob_zero(&manifest);
955 }else{
956 create_manifest(&manifest, 0, 0, &comment, vid,
957 !forceFlag, useCksum ? &cksum1 : 0,
958 zDateOvrd, zUserOvrd, zBranch, zBgColor, &szB);
959 }
960
961 /* See if a delta-manifest would be more appropriate */
962 if( !forceBaseline ){
963 const char *zBaselineUuid;
964 Manifest *pParent;
965 Manifest *pBaseline;
966 pParent = manifest_get(vid, CFTYPE_MANIFEST);
967 if( pParent && pParent->zBaseline ){
968 zBaselineUuid = pParent->zBaseline;
969 pBaseline = manifest_get_by_name(zBaselineUuid, 0);
970 }else{
971 zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
972 pBaseline = pParent;
973 }
974 if( pBaseline ){
975 Blob delta;
976 create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
977 !forceFlag, useCksum ? &cksum1 : 0,
978 zDateOvrd, zUserOvrd, zBranch, zBgColor, &szD);
979 /*
980 ** At this point, two manifests have been constructed, either of
981 ** which would work for this checkin. The first manifest (held
982 ** in the "manifest" variable) is a baseline manifest and the second
983 ** (held in variable named "delta") is a delta manifest. The
984 ** question now is: which manifest should we use?
985 **
986 ** Let B be the number of F-cards in the baseline manifest and
987 ** let D be the number of F-cards in the delta manifest, plus one for
988 ** the B-card. (B is held in the szB variable and D is held in the
989 ** szD variable.) Assume that all delta manifests adds X new F-cards.
990 ** Then to minimize the total number of F- and B-cards in the repository,
991 ** we should use the delta manifest if and only if:
992 **
993 ** D*D < B*X - X*X
994 **
995 ** X is an unknown here, but for most repositories, we will not be
996 ** far wrong if we assume X=3.
997 */
998 if( forceDelta || (szD*szD)<(szB*3-9) ){
999 blob_reset(&manifest);
1000 manifest = delta;
1001 }else{
1002 blob_reset(&delta);
1003 }
1004 }else if( forceDelta ){
1005 fossil_panic("unable to find a baseline-manifest for the delta");
1006 }
1007 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1008 if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
1009 Blob ans;
1010 blob_zero(&ans);
1011 prompt_user("unable to sign manifest. continue (y/N)? ", &ans);
1012 if( blob_str(&ans)[0]!='y' ){
1013 fossil_exit(1);
1014 }
1015 }
1016
1017 /* If the --test option is specified, output the manifest file
1018 ** and rollback the transaction.
1019 */
1020 if( testRun ){
1021 blob_write_to_file(&manifest, "");
1022 }
1023
1024 if( outputManifest ){
1025 zManifestFile = mprintf("%smanifest", g.zLocalRoot);
1026 blob_write_to_file(&manifest, zManifestFile);
1027 blob_reset(&manifest);
1028 blob_read_from_file(&manifest, zManifestFile);
1029 free(zManifestFile);
@@ -897,11 +1035,11 @@
1035 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
1036 manifest_crosslink(nvid, &manifest);
1037 content_deltify(vid, nvid, 0);
1038 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
1039 printf("New_Version: %s\n", zUuid);
1040 if( outputManifest ){
1041 zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
1042 blob_zero(&muuid);
1043 blob_appendf(&muuid, "%s\n", zUuid);
1044 blob_write_to_file(&muuid, zManifestFile);
1045 free(zManifestFile);
@@ -918,45 +1056,51 @@
1056 " WHERE file_is_selected(id);"
1057 , vid, nvid
1058 );
1059 db_lset_int("checkout", nvid);
1060
1061 if( useCksum ){
1062 /* Verify that the repository checksum matches the expected checksum
1063 ** calculated before the checkin started (and stored as the R record
1064 ** of the manifest file).
1065 */
1066 vfile_aggregate_checksum_repository(nvid, &cksum2);
1067 if( blob_compare(&cksum1, &cksum2) ){
1068 fossil_panic("tree checksum does not match repository after commit");
1069 }
1070
1071 /* Verify that the manifest checksum matches the expected checksum */
1072 vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
1073 if( blob_compare(&cksum1, &cksum1b) ){
1074 fossil_panic("manifest checksum does not agree with manifest: "
1075 "%b versus %b", &cksum1, &cksum1b);
1076 }
1077 if( blob_compare(&cksum1, &cksum2) ){
1078 fossil_panic("tree checksum does not match manifest after commit: "
1079 "%b versus %b", &cksum1, &cksum2);
1080 }
1081
1082 /* Verify that the commit did not modify any disk images. */
1083 vfile_aggregate_checksum_disk(nvid, &cksum2);
1084 if( blob_compare(&cksum1, &cksum2) ){
1085 fossil_panic("tree checksums before and after commit do not match");
1086 }
1087 }
1088
1089 /* Clear the undo/redo stack */
1090 undo_reset();
1091
1092 /* Commit */
1093 db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
1094 if( testRun ){
1095 db_end_transaction(1);
1096 exit(1);
1097 }
1098 db_end_transaction(0);
1099
1100 if( !g.markPrivate ){
1101 autosync(AUTOSYNC_PUSH);
1102 }
1103 if( count_nonbranch_children(vid)>1 ){
1104 printf("**** warning: a fork has occurred *****\n");
1105 }
1106 }
1107
+60 -47
--- src/checkout.c
+++ src/checkout.c
@@ -78,87 +78,99 @@
7878
7979
/*
8080
** Load a vfile from a record ID.
8181
*/
8282
void load_vfile_from_rid(int vid){
83
- Blob manifest;
84
-
8583
if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
8684
return;
8785
}
88
- content_get(vid, &manifest);
89
- vfile_build(vid, &manifest);
90
- blob_reset(&manifest);
86
+ vfile_build(vid);
9187
}
9288
9389
/*
9490
** Set or clear the vfile.isexe flag for a file.
9591
*/
9692
static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
97
- db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
98
- onoff, vid, zFilename);
93
+ static Stmt s;
94
+ db_static_prepare(&s,
95
+ "UPDATE vfile SET isexe=:isexe"
96
+ " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
97
+ );
98
+ db_bind_int(&s, ":isexe", onoff);
99
+ db_bind_int(&s, ":vid", vid);
100
+ db_bind_text(&s, ":path", zFilename);
101
+ db_step(&s);
102
+ db_reset(&s);
99103
}
100104
101105
/*
102106
** Set or clear the execute permission bit (as appropriate) for all
103107
** files in the current check-out.
104
-**
105
-** If the checkout does not have explicit files named "manifest" and
106
-** "manifest.uuid" then automatically generate files with those names
107
-** containing, respectively, the text of the manifest and the artifact
108
-** ID of the manifest.
108
+*/
109
+void checkout_set_all_exe(int vid){
110
+ Blob filename;
111
+ int baseLen;
112
+ Manifest *pManifest;
113
+ ManifestFile *pFile;
114
+
115
+ /* Check the EXE permission status of all files
116
+ */
117
+ pManifest = manifest_get(vid, CFTYPE_MANIFEST);
118
+ if( pManifest==0 ) return;
119
+ blob_zero(&filename);
120
+ blob_appendf(&filename, "%s/", g.zLocalRoot);
121
+ baseLen = blob_size(&filename);
122
+ manifest_file_rewind(pManifest);
123
+ while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
124
+ int isExe;
125
+ blob_append(&filename, pFile->zName, -1);
126
+ isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
127
+ file_setexe(blob_str(&filename), isExe);
128
+ set_or_clear_isexe(pFile->zName, vid, isExe);
129
+ blob_resize(&filename, baseLen);
130
+ }
131
+ blob_reset(&filename);
132
+ manifest_destroy(pManifest);
133
+}
134
+
135
+
136
+/*
137
+** If the "manifest" setting is true, then automatically generate
138
+** files named "manifest" and "manifest.uuid" containing, respectively,
139
+** the text of the manifest and the artifact ID of the manifest.
109140
*/
110141
void manifest_to_disk(int vid){
111142
char *zManFile;
112143
Blob manifest;
113144
Blob hash;
114
- Blob filename;
115
- int baseLen;
116
- int i;
117
- int seenManifest = 0;
118
- int seenManifestUuid = 0;
119
- Manifest m;
120
-
121
- /* Check the EXE permission status of all files
122
- */
123
- blob_zero(&manifest);
124
- content_get(vid, &manifest);
125
- manifest_parse(&m, &manifest);
126
- blob_zero(&filename);
127
- blob_appendf(&filename, "%s/", g.zLocalRoot);
128
- baseLen = blob_size(&filename);
129
- for(i=0; i<m.nFile; i++){
130
- int isExe;
131
- blob_append(&filename, m.aFile[i].zName, -1);
132
- isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
133
- file_setexe(blob_str(&filename), isExe);
134
- set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
135
- blob_resize(&filename, baseLen);
136
- if( memcmp(m.aFile[i].zName, "manifest", 8)==0 ){
137
- if( m.aFile[i].zName[8]==0 ) seenManifest = 1;
138
- if( strcmp(&m.aFile[i].zName[8], ".uuid")==0 ) seenManifestUuid = 1;
139
- }
140
- }
141
- blob_reset(&filename);
142
- manifest_clear(&m);
143
-
144
- blob_zero(&manifest);
145
- content_get(vid, &manifest);
146
- if( !seenManifest ){
145
+
146
+ if( db_get_boolean("manifest",0) ){
147
+ blob_zero(&manifest);
148
+ content_get(vid, &manifest);
147149
zManFile = mprintf("%smanifest", g.zLocalRoot);
148150
blob_write_to_file(&manifest, zManFile);
149151
free(zManFile);
150
- }
151
- if( !seenManifestUuid ){
152152
blob_zero(&hash);
153153
sha1sum_blob(&manifest, &hash);
154154
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
155155
blob_append(&hash, "\n", 1);
156156
blob_write_to_file(&hash, zManFile);
157157
free(zManFile);
158158
blob_reset(&hash);
159
+ }else{
160
+ if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
161
+ zManFile = mprintf("%smanifest", g.zLocalRoot);
162
+ unlink(zManFile);
163
+ free(zManFile);
164
+ }
165
+ if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
166
+ zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
167
+ unlink(zManFile);
168
+ free(zManFile);
169
+ }
159170
}
171
+
160172
}
161173
162174
/*
163175
** COMMAND: checkout
164176
** COMMAND: co
@@ -228,10 +240,11 @@
228240
}
229241
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
230242
if( !keepFlag ){
231243
vfile_to_disk(vid, 0, 1, promptFlag);
232244
}
245
+ checkout_set_all_exe(vid);
233246
manifest_to_disk(vid);
234247
db_lset_int("checkout", vid);
235248
undo_reset();
236249
db_multi_exec("DELETE FROM vmerge");
237250
if( !keepFlag ){
238251
--- src/checkout.c
+++ src/checkout.c
@@ -78,87 +78,99 @@
78
79 /*
80 ** Load a vfile from a record ID.
81 */
82 void load_vfile_from_rid(int vid){
83 Blob manifest;
84
85 if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
86 return;
87 }
88 content_get(vid, &manifest);
89 vfile_build(vid, &manifest);
90 blob_reset(&manifest);
91 }
92
93 /*
94 ** Set or clear the vfile.isexe flag for a file.
95 */
96 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
97 db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
98 onoff, vid, zFilename);
 
 
 
 
 
 
 
 
99 }
100
101 /*
102 ** Set or clear the execute permission bit (as appropriate) for all
103 ** files in the current check-out.
104 **
105 ** If the checkout does not have explicit files named "manifest" and
106 ** "manifest.uuid" then automatically generate files with those names
107 ** containing, respectively, the text of the manifest and the artifact
108 ** ID of the manifest.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109 */
110 void manifest_to_disk(int vid){
111 char *zManFile;
112 Blob manifest;
113 Blob hash;
114 Blob filename;
115 int baseLen;
116 int i;
117 int seenManifest = 0;
118 int seenManifestUuid = 0;
119 Manifest m;
120
121 /* Check the EXE permission status of all files
122 */
123 blob_zero(&manifest);
124 content_get(vid, &manifest);
125 manifest_parse(&m, &manifest);
126 blob_zero(&filename);
127 blob_appendf(&filename, "%s/", g.zLocalRoot);
128 baseLen = blob_size(&filename);
129 for(i=0; i<m.nFile; i++){
130 int isExe;
131 blob_append(&filename, m.aFile[i].zName, -1);
132 isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
133 file_setexe(blob_str(&filename), isExe);
134 set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
135 blob_resize(&filename, baseLen);
136 if( memcmp(m.aFile[i].zName, "manifest", 8)==0 ){
137 if( m.aFile[i].zName[8]==0 ) seenManifest = 1;
138 if( strcmp(&m.aFile[i].zName[8], ".uuid")==0 ) seenManifestUuid = 1;
139 }
140 }
141 blob_reset(&filename);
142 manifest_clear(&m);
143
144 blob_zero(&manifest);
145 content_get(vid, &manifest);
146 if( !seenManifest ){
147 zManFile = mprintf("%smanifest", g.zLocalRoot);
148 blob_write_to_file(&manifest, zManFile);
149 free(zManFile);
150 }
151 if( !seenManifestUuid ){
152 blob_zero(&hash);
153 sha1sum_blob(&manifest, &hash);
154 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
155 blob_append(&hash, "\n", 1);
156 blob_write_to_file(&hash, zManFile);
157 free(zManFile);
158 blob_reset(&hash);
 
 
 
 
 
 
 
 
 
 
 
159 }
 
160 }
161
162 /*
163 ** COMMAND: checkout
164 ** COMMAND: co
@@ -228,10 +240,11 @@
228 }
229 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
230 if( !keepFlag ){
231 vfile_to_disk(vid, 0, 1, promptFlag);
232 }
 
233 manifest_to_disk(vid);
234 db_lset_int("checkout", vid);
235 undo_reset();
236 db_multi_exec("DELETE FROM vmerge");
237 if( !keepFlag ){
238
--- src/checkout.c
+++ src/checkout.c
@@ -78,87 +78,99 @@
78
79 /*
80 ** Load a vfile from a record ID.
81 */
82 void load_vfile_from_rid(int vid){
 
 
83 if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
84 return;
85 }
86 vfile_build(vid);
 
 
87 }
88
89 /*
90 ** Set or clear the vfile.isexe flag for a file.
91 */
92 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
93 static Stmt s;
94 db_static_prepare(&s,
95 "UPDATE vfile SET isexe=:isexe"
96 " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
97 );
98 db_bind_int(&s, ":isexe", onoff);
99 db_bind_int(&s, ":vid", vid);
100 db_bind_text(&s, ":path", zFilename);
101 db_step(&s);
102 db_reset(&s);
103 }
104
105 /*
106 ** Set or clear the execute permission bit (as appropriate) for all
107 ** files in the current check-out.
108 */
109 void checkout_set_all_exe(int vid){
110 Blob filename;
111 int baseLen;
112 Manifest *pManifest;
113 ManifestFile *pFile;
114
115 /* Check the EXE permission status of all files
116 */
117 pManifest = manifest_get(vid, CFTYPE_MANIFEST);
118 if( pManifest==0 ) return;
119 blob_zero(&filename);
120 blob_appendf(&filename, "%s/", g.zLocalRoot);
121 baseLen = blob_size(&filename);
122 manifest_file_rewind(pManifest);
123 while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
124 int isExe;
125 blob_append(&filename, pFile->zName, -1);
126 isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
127 file_setexe(blob_str(&filename), isExe);
128 set_or_clear_isexe(pFile->zName, vid, isExe);
129 blob_resize(&filename, baseLen);
130 }
131 blob_reset(&filename);
132 manifest_destroy(pManifest);
133 }
134
135
136 /*
137 ** If the "manifest" setting is true, then automatically generate
138 ** files named "manifest" and "manifest.uuid" containing, respectively,
139 ** the text of the manifest and the artifact ID of the manifest.
140 */
141 void manifest_to_disk(int vid){
142 char *zManFile;
143 Blob manifest;
144 Blob hash;
145
146 if( db_get_boolean("manifest",0) ){
147 blob_zero(&manifest);
148 content_get(vid, &manifest);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149 zManFile = mprintf("%smanifest", g.zLocalRoot);
150 blob_write_to_file(&manifest, zManFile);
151 free(zManFile);
 
 
152 blob_zero(&hash);
153 sha1sum_blob(&manifest, &hash);
154 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
155 blob_append(&hash, "\n", 1);
156 blob_write_to_file(&hash, zManFile);
157 free(zManFile);
158 blob_reset(&hash);
159 }else{
160 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
161 zManFile = mprintf("%smanifest", g.zLocalRoot);
162 unlink(zManFile);
163 free(zManFile);
164 }
165 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
166 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
167 unlink(zManFile);
168 free(zManFile);
169 }
170 }
171
172 }
173
174 /*
175 ** COMMAND: checkout
176 ** COMMAND: co
@@ -228,10 +240,11 @@
240 }
241 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
242 if( !keepFlag ){
243 vfile_to_disk(vid, 0, 1, promptFlag);
244 }
245 checkout_set_all_exe(vid);
246 manifest_to_disk(vid);
247 db_lset_int("checkout", vid);
248 undo_reset();
249 db_multi_exec("DELETE FROM vmerge");
250 if( !keepFlag ){
251
+60 -47
--- src/checkout.c
+++ src/checkout.c
@@ -78,87 +78,99 @@
7878
7979
/*
8080
** Load a vfile from a record ID.
8181
*/
8282
void load_vfile_from_rid(int vid){
83
- Blob manifest;
84
-
8583
if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
8684
return;
8785
}
88
- content_get(vid, &manifest);
89
- vfile_build(vid, &manifest);
90
- blob_reset(&manifest);
86
+ vfile_build(vid);
9187
}
9288
9389
/*
9490
** Set or clear the vfile.isexe flag for a file.
9591
*/
9692
static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
97
- db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
98
- onoff, vid, zFilename);
93
+ static Stmt s;
94
+ db_static_prepare(&s,
95
+ "UPDATE vfile SET isexe=:isexe"
96
+ " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
97
+ );
98
+ db_bind_int(&s, ":isexe", onoff);
99
+ db_bind_int(&s, ":vid", vid);
100
+ db_bind_text(&s, ":path", zFilename);
101
+ db_step(&s);
102
+ db_reset(&s);
99103
}
100104
101105
/*
102106
** Set or clear the execute permission bit (as appropriate) for all
103107
** files in the current check-out.
104
-**
105
-** If the checkout does not have explicit files named "manifest" and
106
-** "manifest.uuid" then automatically generate files with those names
107
-** containing, respectively, the text of the manifest and the artifact
108
-** ID of the manifest.
108
+*/
109
+void checkout_set_all_exe(int vid){
110
+ Blob filename;
111
+ int baseLen;
112
+ Manifest *pManifest;
113
+ ManifestFile *pFile;
114
+
115
+ /* Check the EXE permission status of all files
116
+ */
117
+ pManifest = manifest_get(vid, CFTYPE_MANIFEST);
118
+ if( pManifest==0 ) return;
119
+ blob_zero(&filename);
120
+ blob_appendf(&filename, "%s/", g.zLocalRoot);
121
+ baseLen = blob_size(&filename);
122
+ manifest_file_rewind(pManifest);
123
+ while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
124
+ int isExe;
125
+ blob_append(&filename, pFile->zName, -1);
126
+ isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
127
+ file_setexe(blob_str(&filename), isExe);
128
+ set_or_clear_isexe(pFile->zName, vid, isExe);
129
+ blob_resize(&filename, baseLen);
130
+ }
131
+ blob_reset(&filename);
132
+ manifest_destroy(pManifest);
133
+}
134
+
135
+
136
+/*
137
+** If the "manifest" setting is true, then automatically generate
138
+** files named "manifest" and "manifest.uuid" containing, respectively,
139
+** the text of the manifest and the artifact ID of the manifest.
109140
*/
110141
void manifest_to_disk(int vid){
111142
char *zManFile;
112143
Blob manifest;
113144
Blob hash;
114
- Blob filename;
115
- int baseLen;
116
- int i;
117
- int seenManifest = 0;
118
- int seenManifestUuid = 0;
119
- Manifest m;
120
-
121
- /* Check the EXE permission status of all files
122
- */
123
- blob_zero(&manifest);
124
- content_get(vid, &manifest);
125
- manifest_parse(&m, &manifest);
126
- blob_zero(&filename);
127
- blob_appendf(&filename, "%s/", g.zLocalRoot);
128
- baseLen = blob_size(&filename);
129
- for(i=0; i<m.nFile; i++){
130
- int isExe;
131
- blob_append(&filename, m.aFile[i].zName, -1);
132
- isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
133
- file_setexe(blob_str(&filename), isExe);
134
- set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
135
- blob_resize(&filename, baseLen);
136
- if( memcmp(m.aFile[i].zName, "manifest", 8)==0 ){
137
- if( m.aFile[i].zName[8]==0 ) seenManifest = 1;
138
- if( strcmp(&m.aFile[i].zName[8], ".uuid")==0 ) seenManifestUuid = 1;
139
- }
140
- }
141
- blob_reset(&filename);
142
- manifest_clear(&m);
143
-
144
- blob_zero(&manifest);
145
- content_get(vid, &manifest);
146
- if( !seenManifest ){
145
+
146
+ if( db_get_boolean("manifest",0) ){
147
+ blob_zero(&manifest);
148
+ content_get(vid, &manifest);
147149
zManFile = mprintf("%smanifest", g.zLocalRoot);
148150
blob_write_to_file(&manifest, zManFile);
149151
free(zManFile);
150
- }
151
- if( !seenManifestUuid ){
152152
blob_zero(&hash);
153153
sha1sum_blob(&manifest, &hash);
154154
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
155155
blob_append(&hash, "\n", 1);
156156
blob_write_to_file(&hash, zManFile);
157157
free(zManFile);
158158
blob_reset(&hash);
159
+ }else{
160
+ if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
161
+ zManFile = mprintf("%smanifest", g.zLocalRoot);
162
+ unlink(zManFile);
163
+ free(zManFile);
164
+ }
165
+ if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
166
+ zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
167
+ unlink(zManFile);
168
+ free(zManFile);
169
+ }
159170
}
171
+
160172
}
161173
162174
/*
163175
** COMMAND: checkout
164176
** COMMAND: co
@@ -228,10 +240,11 @@
228240
}
229241
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
230242
if( !keepFlag ){
231243
vfile_to_disk(vid, 0, 1, promptFlag);
232244
}
245
+ checkout_set_all_exe(vid);
233246
manifest_to_disk(vid);
234247
db_lset_int("checkout", vid);
235248
undo_reset();
236249
db_multi_exec("DELETE FROM vmerge");
237250
if( !keepFlag ){
238251
--- src/checkout.c
+++ src/checkout.c
@@ -78,87 +78,99 @@
78
79 /*
80 ** Load a vfile from a record ID.
81 */
82 void load_vfile_from_rid(int vid){
83 Blob manifest;
84
85 if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
86 return;
87 }
88 content_get(vid, &manifest);
89 vfile_build(vid, &manifest);
90 blob_reset(&manifest);
91 }
92
93 /*
94 ** Set or clear the vfile.isexe flag for a file.
95 */
96 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
97 db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
98 onoff, vid, zFilename);
 
 
 
 
 
 
 
 
99 }
100
101 /*
102 ** Set or clear the execute permission bit (as appropriate) for all
103 ** files in the current check-out.
104 **
105 ** If the checkout does not have explicit files named "manifest" and
106 ** "manifest.uuid" then automatically generate files with those names
107 ** containing, respectively, the text of the manifest and the artifact
108 ** ID of the manifest.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109 */
110 void manifest_to_disk(int vid){
111 char *zManFile;
112 Blob manifest;
113 Blob hash;
114 Blob filename;
115 int baseLen;
116 int i;
117 int seenManifest = 0;
118 int seenManifestUuid = 0;
119 Manifest m;
120
121 /* Check the EXE permission status of all files
122 */
123 blob_zero(&manifest);
124 content_get(vid, &manifest);
125 manifest_parse(&m, &manifest);
126 blob_zero(&filename);
127 blob_appendf(&filename, "%s/", g.zLocalRoot);
128 baseLen = blob_size(&filename);
129 for(i=0; i<m.nFile; i++){
130 int isExe;
131 blob_append(&filename, m.aFile[i].zName, -1);
132 isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
133 file_setexe(blob_str(&filename), isExe);
134 set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
135 blob_resize(&filename, baseLen);
136 if( memcmp(m.aFile[i].zName, "manifest", 8)==0 ){
137 if( m.aFile[i].zName[8]==0 ) seenManifest = 1;
138 if( strcmp(&m.aFile[i].zName[8], ".uuid")==0 ) seenManifestUuid = 1;
139 }
140 }
141 blob_reset(&filename);
142 manifest_clear(&m);
143
144 blob_zero(&manifest);
145 content_get(vid, &manifest);
146 if( !seenManifest ){
147 zManFile = mprintf("%smanifest", g.zLocalRoot);
148 blob_write_to_file(&manifest, zManFile);
149 free(zManFile);
150 }
151 if( !seenManifestUuid ){
152 blob_zero(&hash);
153 sha1sum_blob(&manifest, &hash);
154 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
155 blob_append(&hash, "\n", 1);
156 blob_write_to_file(&hash, zManFile);
157 free(zManFile);
158 blob_reset(&hash);
 
 
 
 
 
 
 
 
 
 
 
159 }
 
160 }
161
162 /*
163 ** COMMAND: checkout
164 ** COMMAND: co
@@ -228,10 +240,11 @@
228 }
229 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
230 if( !keepFlag ){
231 vfile_to_disk(vid, 0, 1, promptFlag);
232 }
 
233 manifest_to_disk(vid);
234 db_lset_int("checkout", vid);
235 undo_reset();
236 db_multi_exec("DELETE FROM vmerge");
237 if( !keepFlag ){
238
--- src/checkout.c
+++ src/checkout.c
@@ -78,87 +78,99 @@
78
79 /*
80 ** Load a vfile from a record ID.
81 */
82 void load_vfile_from_rid(int vid){
 
 
83 if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
84 return;
85 }
86 vfile_build(vid);
 
 
87 }
88
89 /*
90 ** Set or clear the vfile.isexe flag for a file.
91 */
92 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
93 static Stmt s;
94 db_static_prepare(&s,
95 "UPDATE vfile SET isexe=:isexe"
96 " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
97 );
98 db_bind_int(&s, ":isexe", onoff);
99 db_bind_int(&s, ":vid", vid);
100 db_bind_text(&s, ":path", zFilename);
101 db_step(&s);
102 db_reset(&s);
103 }
104
105 /*
106 ** Set or clear the execute permission bit (as appropriate) for all
107 ** files in the current check-out.
108 */
109 void checkout_set_all_exe(int vid){
110 Blob filename;
111 int baseLen;
112 Manifest *pManifest;
113 ManifestFile *pFile;
114
115 /* Check the EXE permission status of all files
116 */
117 pManifest = manifest_get(vid, CFTYPE_MANIFEST);
118 if( pManifest==0 ) return;
119 blob_zero(&filename);
120 blob_appendf(&filename, "%s/", g.zLocalRoot);
121 baseLen = blob_size(&filename);
122 manifest_file_rewind(pManifest);
123 while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
124 int isExe;
125 blob_append(&filename, pFile->zName, -1);
126 isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
127 file_setexe(blob_str(&filename), isExe);
128 set_or_clear_isexe(pFile->zName, vid, isExe);
129 blob_resize(&filename, baseLen);
130 }
131 blob_reset(&filename);
132 manifest_destroy(pManifest);
133 }
134
135
136 /*
137 ** If the "manifest" setting is true, then automatically generate
138 ** files named "manifest" and "manifest.uuid" containing, respectively,
139 ** the text of the manifest and the artifact ID of the manifest.
140 */
141 void manifest_to_disk(int vid){
142 char *zManFile;
143 Blob manifest;
144 Blob hash;
145
146 if( db_get_boolean("manifest",0) ){
147 blob_zero(&manifest);
148 content_get(vid, &manifest);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149 zManFile = mprintf("%smanifest", g.zLocalRoot);
150 blob_write_to_file(&manifest, zManFile);
151 free(zManFile);
 
 
152 blob_zero(&hash);
153 sha1sum_blob(&manifest, &hash);
154 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
155 blob_append(&hash, "\n", 1);
156 blob_write_to_file(&hash, zManFile);
157 free(zManFile);
158 blob_reset(&hash);
159 }else{
160 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
161 zManFile = mprintf("%smanifest", g.zLocalRoot);
162 unlink(zManFile);
163 free(zManFile);
164 }
165 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
166 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
167 unlink(zManFile);
168 free(zManFile);
169 }
170 }
171
172 }
173
174 /*
175 ** COMMAND: checkout
176 ** COMMAND: co
@@ -228,10 +240,11 @@
240 }
241 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
242 if( !keepFlag ){
243 vfile_to_disk(vid, 0, 1, promptFlag);
244 }
245 checkout_set_all_exe(vid);
246 manifest_to_disk(vid);
247 db_lset_int("checkout", vid);
248 undo_reset();
249 db_multi_exec("DELETE FROM vmerge");
250 if( !keepFlag ){
251
--- src/configure.c
+++ src/configure.c
@@ -74,10 +74,11 @@
7474
{ "footer", CONFIGSET_SKIN },
7575
{ "logo-mimetype", CONFIGSET_SKIN },
7676
{ "logo-image", CONFIGSET_SKIN },
7777
{ "project-name", CONFIGSET_PROJ },
7878
{ "project-description", CONFIGSET_PROJ },
79
+ { "manifest", CONFIGSET_PROJ },
7980
{ "index-page", CONFIGSET_SKIN },
8081
{ "timeline-block-markup", CONFIGSET_SKIN },
8182
{ "timeline-max-comment", CONFIGSET_SKIN },
8283
{ "ticket-table", CONFIGSET_TKT },
8384
{ "ticket-common", CONFIGSET_TKT },
8485
--- src/configure.c
+++ src/configure.c
@@ -74,10 +74,11 @@
74 { "footer", CONFIGSET_SKIN },
75 { "logo-mimetype", CONFIGSET_SKIN },
76 { "logo-image", CONFIGSET_SKIN },
77 { "project-name", CONFIGSET_PROJ },
78 { "project-description", CONFIGSET_PROJ },
 
79 { "index-page", CONFIGSET_SKIN },
80 { "timeline-block-markup", CONFIGSET_SKIN },
81 { "timeline-max-comment", CONFIGSET_SKIN },
82 { "ticket-table", CONFIGSET_TKT },
83 { "ticket-common", CONFIGSET_TKT },
84
--- src/configure.c
+++ src/configure.c
@@ -74,10 +74,11 @@
74 { "footer", CONFIGSET_SKIN },
75 { "logo-mimetype", CONFIGSET_SKIN },
76 { "logo-image", CONFIGSET_SKIN },
77 { "project-name", CONFIGSET_PROJ },
78 { "project-description", CONFIGSET_PROJ },
79 { "manifest", CONFIGSET_PROJ },
80 { "index-page", CONFIGSET_SKIN },
81 { "timeline-block-markup", CONFIGSET_SKIN },
82 { "timeline-max-comment", CONFIGSET_SKIN },
83 { "ticket-table", CONFIGSET_TKT },
84 { "ticket-common", CONFIGSET_TKT },
85
--- src/configure.c
+++ src/configure.c
@@ -74,10 +74,11 @@
7474
{ "footer", CONFIGSET_SKIN },
7575
{ "logo-mimetype", CONFIGSET_SKIN },
7676
{ "logo-image", CONFIGSET_SKIN },
7777
{ "project-name", CONFIGSET_PROJ },
7878
{ "project-description", CONFIGSET_PROJ },
79
+ { "manifest", CONFIGSET_PROJ },
7980
{ "index-page", CONFIGSET_SKIN },
8081
{ "timeline-block-markup", CONFIGSET_SKIN },
8182
{ "timeline-max-comment", CONFIGSET_SKIN },
8283
{ "ticket-table", CONFIGSET_TKT },
8384
{ "ticket-common", CONFIGSET_TKT },
8485
--- src/configure.c
+++ src/configure.c
@@ -74,10 +74,11 @@
74 { "footer", CONFIGSET_SKIN },
75 { "logo-mimetype", CONFIGSET_SKIN },
76 { "logo-image", CONFIGSET_SKIN },
77 { "project-name", CONFIGSET_PROJ },
78 { "project-description", CONFIGSET_PROJ },
 
79 { "index-page", CONFIGSET_SKIN },
80 { "timeline-block-markup", CONFIGSET_SKIN },
81 { "timeline-max-comment", CONFIGSET_SKIN },
82 { "ticket-table", CONFIGSET_TKT },
83 { "ticket-common", CONFIGSET_TKT },
84
--- src/configure.c
+++ src/configure.c
@@ -74,10 +74,11 @@
74 { "footer", CONFIGSET_SKIN },
75 { "logo-mimetype", CONFIGSET_SKIN },
76 { "logo-image", CONFIGSET_SKIN },
77 { "project-name", CONFIGSET_PROJ },
78 { "project-description", CONFIGSET_PROJ },
79 { "manifest", CONFIGSET_PROJ },
80 { "index-page", CONFIGSET_SKIN },
81 { "timeline-block-markup", CONFIGSET_SKIN },
82 { "timeline-max-comment", CONFIGSET_SKIN },
83 { "ticket-table", CONFIGSET_TKT },
84 { "ticket-common", CONFIGSET_TKT },
85
+2 -2
--- src/content.c
+++ src/content.c
@@ -281,11 +281,11 @@
281281
}
282282
return rc;
283283
}
284284
285285
/*
286
-** COMMAND: artifact
286
+** COMMAND: artifact
287287
**
288288
** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME?
289289
**
290290
** Extract an artifact by its SHA1 hash and write the results on
291291
** standard output, or if the optional 4th argument is given, in
@@ -293,13 +293,13 @@
293293
*/
294294
void artifact_cmd(void){
295295
int rid;
296296
Blob content;
297297
const char *zFile;
298
+ db_find_and_open_repository(1);
298299
if( g.argc!=4 && g.argc!=3 ) usage("RECORDID ?FILENAME?");
299300
zFile = g.argc==4 ? g.argv[3] : "-";
300
- db_must_be_within_tree();
301301
rid = name_to_rid(g.argv[2]);
302302
content_get(rid, &content);
303303
blob_write_to_file(&content, zFile);
304304
}
305305
306306
--- src/content.c
+++ src/content.c
@@ -281,11 +281,11 @@
281 }
282 return rc;
283 }
284
285 /*
286 ** COMMAND: artifact
287 **
288 ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME?
289 **
290 ** Extract an artifact by its SHA1 hash and write the results on
291 ** standard output, or if the optional 4th argument is given, in
@@ -293,13 +293,13 @@
293 */
294 void artifact_cmd(void){
295 int rid;
296 Blob content;
297 const char *zFile;
 
298 if( g.argc!=4 && g.argc!=3 ) usage("RECORDID ?FILENAME?");
299 zFile = g.argc==4 ? g.argv[3] : "-";
300 db_must_be_within_tree();
301 rid = name_to_rid(g.argv[2]);
302 content_get(rid, &content);
303 blob_write_to_file(&content, zFile);
304 }
305
306
--- src/content.c
+++ src/content.c
@@ -281,11 +281,11 @@
281 }
282 return rc;
283 }
284
285 /*
286 ** COMMAND: artifact
287 **
288 ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME?
289 **
290 ** Extract an artifact by its SHA1 hash and write the results on
291 ** standard output, or if the optional 4th argument is given, in
@@ -293,13 +293,13 @@
293 */
294 void artifact_cmd(void){
295 int rid;
296 Blob content;
297 const char *zFile;
298 db_find_and_open_repository(1);
299 if( g.argc!=4 && g.argc!=3 ) usage("RECORDID ?FILENAME?");
300 zFile = g.argc==4 ? g.argv[3] : "-";
 
301 rid = name_to_rid(g.argv[2]);
302 content_get(rid, &content);
303 blob_write_to_file(&content, zFile);
304 }
305
306
+27 -9
--- src/db.c
+++ src/db.c
@@ -1510,23 +1510,24 @@
15101510
int width; /* Width of display. 0 for boolean values */
15111511
char const *def; /* Default value */
15121512
};
15131513
#endif /* INTERFACE */
15141514
struct stControlSettings const ctrlSettings[] = {
1515
- { "auto-captcha", "autocaptcha", 0, "0" },
1516
- { "auto-shun", 0, 0, "1" },
1517
- { "autosync", 0, 0, "0" },
1518
- { "binary-glob", 0, 0, "1" },
1519
- { "clearsign", 0, 0, "0" },
1520
- { "diff-command", 0, 16, "diff" },
1521
- { "dont-push", 0, 0, "0" },
1515
+ { "auto-captcha", "autocaptcha", 0, "on" },
1516
+ { "auto-shun", 0, 0, "on" },
1517
+ { "autosync", 0, 0, "on" },
1518
+ { "binary-glob", 0, 32, "" },
1519
+ { "clearsign", 0, 0, "off" },
1520
+ { "diff-command", 0, 16, "" },
1521
+ { "dont-push", 0, 0, "off" },
15221522
{ "editor", 0, 16, "" },
15231523
{ "gdiff-command", 0, 16, "gdiff" },
15241524
{ "ignore-glob", 0, 40, "" },
15251525
{ "http-port", 0, 16, "8080" },
1526
- { "localauth", 0, 0, "0" },
1527
- { "mtime-changes", 0, 0, "0" },
1526
+ { "localauth", 0, 0, "off" },
1527
+ { "manifest", 0, 0, "off" },
1528
+ { "mtime-changes", 0, 0, "off" },
15281529
{ "pgp-command", 0, 32, "gpg --clearsign -o " },
15291530
{ "proxy", 0, 32, "off" },
15301531
{ "push-hook-cmd", 0, 32, "" },
15311532
{ "push-hook-force",
15321533
0, 0, "" },
@@ -1534,10 +1535,11 @@
15341535
0, 32, "" },
15351536
{ "push-hook-pattern-server",
15361537
0, 32, "" },
15371538
{ "push-hook-privilege",
15381539
0, 1, "" },
1540
+ { "repo-cksum", 0, 0, "on" },
15391541
{ "ssh-command", 0, 32, "" },
15401542
{ "web-browser", 0, 32, "" },
15411543
{ 0,0,0,0 }
15421544
};
15431545
@@ -1622,14 +1624,16 @@
16221624
** if this is set on the client, it will request always
16231625
** the hook activation, even if no files where pushed on
16241626
** the sync.
16251627
** if this is set on the server, it will accept hook
16261628
** activiation, even if no files where pushed.
1629
+** Default: on
16271630
**
16281631
** push-hook-pattern-client
16291632
** if set, a client push will sent this message to the
16301633
** server, to activate the push hook command.
1634
+** Default: on
16311635
**
16321636
** push-hook-pattern-server
16331637
** if set, and a client send this pattern at the end of
16341638
** a push, the push hook command will be executed. This
16351639
** might be a prefix of the pattern, sent by the client.
@@ -1639,10 +1643,15 @@
16391643
** to trigger the hook. Valid privileges are:
16401644
** s (setup), a (admin), i (checkin) or o (checkout)
16411645
**
16421646
** ssh-command Command used to talk to a remote machine with
16431647
** the "ssh://" protocol.
1648
+**
1649
+** repo-cksum Compute checksums over all files in each checkout
1650
+** as a double-check of correctness. Defaults to "on".
1651
+** Disable on large repositories for a performance
1652
+** improvement.
16441653
**
16451654
** web-browser A shell command used to launch your preferred
16461655
** web browser when given a URL as an argument.
16471656
** Defaults to "start" on windows, "open" on Mac,
16481657
** and "firefox" on Unix.
@@ -1667,24 +1676,33 @@
16671676
for(i=0; ctrlSettings[i].name; i++){
16681677
print_setting(ctrlSettings[i].name);
16691678
}
16701679
}else if( g.argc==3 || g.argc==4 ){
16711680
const char *zName = g.argv[2];
1681
+ int isManifest;
16721682
int n = strlen(zName);
16731683
for(i=0; ctrlSettings[i].name; i++){
16741684
if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
16751685
}
16761686
if( !ctrlSettings[i].name ){
16771687
fossil_fatal("no such setting: %s", zName);
16781688
}
1689
+ isManifest = strcmp(ctrlSettings[i].name, "manifest")==0;
1690
+ if( isManifest && globalFlag ){
1691
+ fossil_fatal("cannot set 'manifest' globally");
1692
+ }
16791693
if( unsetFlag ){
16801694
db_unset(ctrlSettings[i].name, globalFlag);
16811695
}else if( g.argc==4 ){
16821696
db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
16831697
}else{
1698
+ isManifest = 0;
16841699
print_setting(ctrlSettings[i].name);
16851700
}
1701
+ if( isManifest ){
1702
+ manifest_to_disk(db_lget_int("checkout", 0));
1703
+ }
16861704
}else{
16871705
usage("?PROPERTY? ?VALUE?");
16881706
}
16891707
}
16901708
16911709
--- src/db.c
+++ src/db.c
@@ -1510,23 +1510,24 @@
1510 int width; /* Width of display. 0 for boolean values */
1511 char const *def; /* Default value */
1512 };
1513 #endif /* INTERFACE */
1514 struct stControlSettings const ctrlSettings[] = {
1515 { "auto-captcha", "autocaptcha", 0, "0" },
1516 { "auto-shun", 0, 0, "1" },
1517 { "autosync", 0, 0, "0" },
1518 { "binary-glob", 0, 0, "1" },
1519 { "clearsign", 0, 0, "0" },
1520 { "diff-command", 0, 16, "diff" },
1521 { "dont-push", 0, 0, "0" },
1522 { "editor", 0, 16, "" },
1523 { "gdiff-command", 0, 16, "gdiff" },
1524 { "ignore-glob", 0, 40, "" },
1525 { "http-port", 0, 16, "8080" },
1526 { "localauth", 0, 0, "0" },
1527 { "mtime-changes", 0, 0, "0" },
 
1528 { "pgp-command", 0, 32, "gpg --clearsign -o " },
1529 { "proxy", 0, 32, "off" },
1530 { "push-hook-cmd", 0, 32, "" },
1531 { "push-hook-force",
1532 0, 0, "" },
@@ -1534,10 +1535,11 @@
1534 0, 32, "" },
1535 { "push-hook-pattern-server",
1536 0, 32, "" },
1537 { "push-hook-privilege",
1538 0, 1, "" },
 
1539 { "ssh-command", 0, 32, "" },
1540 { "web-browser", 0, 32, "" },
1541 { 0,0,0,0 }
1542 };
1543
@@ -1622,14 +1624,16 @@
1622 ** if this is set on the client, it will request always
1623 ** the hook activation, even if no files where pushed on
1624 ** the sync.
1625 ** if this is set on the server, it will accept hook
1626 ** activiation, even if no files where pushed.
 
1627 **
1628 ** push-hook-pattern-client
1629 ** if set, a client push will sent this message to the
1630 ** server, to activate the push hook command.
 
1631 **
1632 ** push-hook-pattern-server
1633 ** if set, and a client send this pattern at the end of
1634 ** a push, the push hook command will be executed. This
1635 ** might be a prefix of the pattern, sent by the client.
@@ -1639,10 +1643,15 @@
1639 ** to trigger the hook. Valid privileges are:
1640 ** s (setup), a (admin), i (checkin) or o (checkout)
1641 **
1642 ** ssh-command Command used to talk to a remote machine with
1643 ** the "ssh://" protocol.
 
 
 
 
 
1644 **
1645 ** web-browser A shell command used to launch your preferred
1646 ** web browser when given a URL as an argument.
1647 ** Defaults to "start" on windows, "open" on Mac,
1648 ** and "firefox" on Unix.
@@ -1667,24 +1676,33 @@
1667 for(i=0; ctrlSettings[i].name; i++){
1668 print_setting(ctrlSettings[i].name);
1669 }
1670 }else if( g.argc==3 || g.argc==4 ){
1671 const char *zName = g.argv[2];
 
1672 int n = strlen(zName);
1673 for(i=0; ctrlSettings[i].name; i++){
1674 if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
1675 }
1676 if( !ctrlSettings[i].name ){
1677 fossil_fatal("no such setting: %s", zName);
1678 }
 
 
 
 
1679 if( unsetFlag ){
1680 db_unset(ctrlSettings[i].name, globalFlag);
1681 }else if( g.argc==4 ){
1682 db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
1683 }else{
 
1684 print_setting(ctrlSettings[i].name);
1685 }
 
 
 
1686 }else{
1687 usage("?PROPERTY? ?VALUE?");
1688 }
1689 }
1690
1691
--- src/db.c
+++ src/db.c
@@ -1510,23 +1510,24 @@
1510 int width; /* Width of display. 0 for boolean values */
1511 char const *def; /* Default value */
1512 };
1513 #endif /* INTERFACE */
1514 struct stControlSettings const ctrlSettings[] = {
1515 { "auto-captcha", "autocaptcha", 0, "on" },
1516 { "auto-shun", 0, 0, "on" },
1517 { "autosync", 0, 0, "on" },
1518 { "binary-glob", 0, 32, "" },
1519 { "clearsign", 0, 0, "off" },
1520 { "diff-command", 0, 16, "" },
1521 { "dont-push", 0, 0, "off" },
1522 { "editor", 0, 16, "" },
1523 { "gdiff-command", 0, 16, "gdiff" },
1524 { "ignore-glob", 0, 40, "" },
1525 { "http-port", 0, 16, "8080" },
1526 { "localauth", 0, 0, "off" },
1527 { "manifest", 0, 0, "off" },
1528 { "mtime-changes", 0, 0, "off" },
1529 { "pgp-command", 0, 32, "gpg --clearsign -o " },
1530 { "proxy", 0, 32, "off" },
1531 { "push-hook-cmd", 0, 32, "" },
1532 { "push-hook-force",
1533 0, 0, "" },
@@ -1534,10 +1535,11 @@
1535 0, 32, "" },
1536 { "push-hook-pattern-server",
1537 0, 32, "" },
1538 { "push-hook-privilege",
1539 0, 1, "" },
1540 { "repo-cksum", 0, 0, "on" },
1541 { "ssh-command", 0, 32, "" },
1542 { "web-browser", 0, 32, "" },
1543 { 0,0,0,0 }
1544 };
1545
@@ -1622,14 +1624,16 @@
1624 ** if this is set on the client, it will request always
1625 ** the hook activation, even if no files where pushed on
1626 ** the sync.
1627 ** if this is set on the server, it will accept hook
1628 ** activiation, even if no files where pushed.
1629 ** Default: on
1630 **
1631 ** push-hook-pattern-client
1632 ** if set, a client push will sent this message to the
1633 ** server, to activate the push hook command.
1634 ** Default: on
1635 **
1636 ** push-hook-pattern-server
1637 ** if set, and a client send this pattern at the end of
1638 ** a push, the push hook command will be executed. This
1639 ** might be a prefix of the pattern, sent by the client.
@@ -1639,10 +1643,15 @@
1643 ** to trigger the hook. Valid privileges are:
1644 ** s (setup), a (admin), i (checkin) or o (checkout)
1645 **
1646 ** ssh-command Command used to talk to a remote machine with
1647 ** the "ssh://" protocol.
1648 **
1649 ** repo-cksum Compute checksums over all files in each checkout
1650 ** as a double-check of correctness. Defaults to "on".
1651 ** Disable on large repositories for a performance
1652 ** improvement.
1653 **
1654 ** web-browser A shell command used to launch your preferred
1655 ** web browser when given a URL as an argument.
1656 ** Defaults to "start" on windows, "open" on Mac,
1657 ** and "firefox" on Unix.
@@ -1667,24 +1676,33 @@
1676 for(i=0; ctrlSettings[i].name; i++){
1677 print_setting(ctrlSettings[i].name);
1678 }
1679 }else if( g.argc==3 || g.argc==4 ){
1680 const char *zName = g.argv[2];
1681 int isManifest;
1682 int n = strlen(zName);
1683 for(i=0; ctrlSettings[i].name; i++){
1684 if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
1685 }
1686 if( !ctrlSettings[i].name ){
1687 fossil_fatal("no such setting: %s", zName);
1688 }
1689 isManifest = strcmp(ctrlSettings[i].name, "manifest")==0;
1690 if( isManifest && globalFlag ){
1691 fossil_fatal("cannot set 'manifest' globally");
1692 }
1693 if( unsetFlag ){
1694 db_unset(ctrlSettings[i].name, globalFlag);
1695 }else if( g.argc==4 ){
1696 db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
1697 }else{
1698 isManifest = 0;
1699 print_setting(ctrlSettings[i].name);
1700 }
1701 if( isManifest ){
1702 manifest_to_disk(db_lget_int("checkout", 0));
1703 }
1704 }else{
1705 usage("?PROPERTY? ?VALUE?");
1706 }
1707 }
1708
1709
+27 -9
--- src/db.c
+++ src/db.c
@@ -1510,23 +1510,24 @@
15101510
int width; /* Width of display. 0 for boolean values */
15111511
char const *def; /* Default value */
15121512
};
15131513
#endif /* INTERFACE */
15141514
struct stControlSettings const ctrlSettings[] = {
1515
- { "auto-captcha", "autocaptcha", 0, "0" },
1516
- { "auto-shun", 0, 0, "1" },
1517
- { "autosync", 0, 0, "0" },
1518
- { "binary-glob", 0, 0, "1" },
1519
- { "clearsign", 0, 0, "0" },
1520
- { "diff-command", 0, 16, "diff" },
1521
- { "dont-push", 0, 0, "0" },
1515
+ { "auto-captcha", "autocaptcha", 0, "on" },
1516
+ { "auto-shun", 0, 0, "on" },
1517
+ { "autosync", 0, 0, "on" },
1518
+ { "binary-glob", 0, 32, "" },
1519
+ { "clearsign", 0, 0, "off" },
1520
+ { "diff-command", 0, 16, "" },
1521
+ { "dont-push", 0, 0, "off" },
15221522
{ "editor", 0, 16, "" },
15231523
{ "gdiff-command", 0, 16, "gdiff" },
15241524
{ "ignore-glob", 0, 40, "" },
15251525
{ "http-port", 0, 16, "8080" },
1526
- { "localauth", 0, 0, "0" },
1527
- { "mtime-changes", 0, 0, "0" },
1526
+ { "localauth", 0, 0, "off" },
1527
+ { "manifest", 0, 0, "off" },
1528
+ { "mtime-changes", 0, 0, "off" },
15281529
{ "pgp-command", 0, 32, "gpg --clearsign -o " },
15291530
{ "proxy", 0, 32, "off" },
15301531
{ "push-hook-cmd", 0, 32, "" },
15311532
{ "push-hook-force",
15321533
0, 0, "" },
@@ -1534,10 +1535,11 @@
15341535
0, 32, "" },
15351536
{ "push-hook-pattern-server",
15361537
0, 32, "" },
15371538
{ "push-hook-privilege",
15381539
0, 1, "" },
1540
+ { "repo-cksum", 0, 0, "on" },
15391541
{ "ssh-command", 0, 32, "" },
15401542
{ "web-browser", 0, 32, "" },
15411543
{ 0,0,0,0 }
15421544
};
15431545
@@ -1622,14 +1624,16 @@
16221624
** if this is set on the client, it will request always
16231625
** the hook activation, even if no files where pushed on
16241626
** the sync.
16251627
** if this is set on the server, it will accept hook
16261628
** activiation, even if no files where pushed.
1629
+** Default: on
16271630
**
16281631
** push-hook-pattern-client
16291632
** if set, a client push will sent this message to the
16301633
** server, to activate the push hook command.
1634
+** Default: on
16311635
**
16321636
** push-hook-pattern-server
16331637
** if set, and a client send this pattern at the end of
16341638
** a push, the push hook command will be executed. This
16351639
** might be a prefix of the pattern, sent by the client.
@@ -1639,10 +1643,15 @@
16391643
** to trigger the hook. Valid privileges are:
16401644
** s (setup), a (admin), i (checkin) or o (checkout)
16411645
**
16421646
** ssh-command Command used to talk to a remote machine with
16431647
** the "ssh://" protocol.
1648
+**
1649
+** repo-cksum Compute checksums over all files in each checkout
1650
+** as a double-check of correctness. Defaults to "on".
1651
+** Disable on large repositories for a performance
1652
+** improvement.
16441653
**
16451654
** web-browser A shell command used to launch your preferred
16461655
** web browser when given a URL as an argument.
16471656
** Defaults to "start" on windows, "open" on Mac,
16481657
** and "firefox" on Unix.
@@ -1667,24 +1676,33 @@
16671676
for(i=0; ctrlSettings[i].name; i++){
16681677
print_setting(ctrlSettings[i].name);
16691678
}
16701679
}else if( g.argc==3 || g.argc==4 ){
16711680
const char *zName = g.argv[2];
1681
+ int isManifest;
16721682
int n = strlen(zName);
16731683
for(i=0; ctrlSettings[i].name; i++){
16741684
if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
16751685
}
16761686
if( !ctrlSettings[i].name ){
16771687
fossil_fatal("no such setting: %s", zName);
16781688
}
1689
+ isManifest = strcmp(ctrlSettings[i].name, "manifest")==0;
1690
+ if( isManifest && globalFlag ){
1691
+ fossil_fatal("cannot set 'manifest' globally");
1692
+ }
16791693
if( unsetFlag ){
16801694
db_unset(ctrlSettings[i].name, globalFlag);
16811695
}else if( g.argc==4 ){
16821696
db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
16831697
}else{
1698
+ isManifest = 0;
16841699
print_setting(ctrlSettings[i].name);
16851700
}
1701
+ if( isManifest ){
1702
+ manifest_to_disk(db_lget_int("checkout", 0));
1703
+ }
16861704
}else{
16871705
usage("?PROPERTY? ?VALUE?");
16881706
}
16891707
}
16901708
16911709
--- src/db.c
+++ src/db.c
@@ -1510,23 +1510,24 @@
1510 int width; /* Width of display. 0 for boolean values */
1511 char const *def; /* Default value */
1512 };
1513 #endif /* INTERFACE */
1514 struct stControlSettings const ctrlSettings[] = {
1515 { "auto-captcha", "autocaptcha", 0, "0" },
1516 { "auto-shun", 0, 0, "1" },
1517 { "autosync", 0, 0, "0" },
1518 { "binary-glob", 0, 0, "1" },
1519 { "clearsign", 0, 0, "0" },
1520 { "diff-command", 0, 16, "diff" },
1521 { "dont-push", 0, 0, "0" },
1522 { "editor", 0, 16, "" },
1523 { "gdiff-command", 0, 16, "gdiff" },
1524 { "ignore-glob", 0, 40, "" },
1525 { "http-port", 0, 16, "8080" },
1526 { "localauth", 0, 0, "0" },
1527 { "mtime-changes", 0, 0, "0" },
 
1528 { "pgp-command", 0, 32, "gpg --clearsign -o " },
1529 { "proxy", 0, 32, "off" },
1530 { "push-hook-cmd", 0, 32, "" },
1531 { "push-hook-force",
1532 0, 0, "" },
@@ -1534,10 +1535,11 @@
1534 0, 32, "" },
1535 { "push-hook-pattern-server",
1536 0, 32, "" },
1537 { "push-hook-privilege",
1538 0, 1, "" },
 
1539 { "ssh-command", 0, 32, "" },
1540 { "web-browser", 0, 32, "" },
1541 { 0,0,0,0 }
1542 };
1543
@@ -1622,14 +1624,16 @@
1622 ** if this is set on the client, it will request always
1623 ** the hook activation, even if no files where pushed on
1624 ** the sync.
1625 ** if this is set on the server, it will accept hook
1626 ** activiation, even if no files where pushed.
 
1627 **
1628 ** push-hook-pattern-client
1629 ** if set, a client push will sent this message to the
1630 ** server, to activate the push hook command.
 
1631 **
1632 ** push-hook-pattern-server
1633 ** if set, and a client send this pattern at the end of
1634 ** a push, the push hook command will be executed. This
1635 ** might be a prefix of the pattern, sent by the client.
@@ -1639,10 +1643,15 @@
1639 ** to trigger the hook. Valid privileges are:
1640 ** s (setup), a (admin), i (checkin) or o (checkout)
1641 **
1642 ** ssh-command Command used to talk to a remote machine with
1643 ** the "ssh://" protocol.
 
 
 
 
 
1644 **
1645 ** web-browser A shell command used to launch your preferred
1646 ** web browser when given a URL as an argument.
1647 ** Defaults to "start" on windows, "open" on Mac,
1648 ** and "firefox" on Unix.
@@ -1667,24 +1676,33 @@
1667 for(i=0; ctrlSettings[i].name; i++){
1668 print_setting(ctrlSettings[i].name);
1669 }
1670 }else if( g.argc==3 || g.argc==4 ){
1671 const char *zName = g.argv[2];
 
1672 int n = strlen(zName);
1673 for(i=0; ctrlSettings[i].name; i++){
1674 if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
1675 }
1676 if( !ctrlSettings[i].name ){
1677 fossil_fatal("no such setting: %s", zName);
1678 }
 
 
 
 
1679 if( unsetFlag ){
1680 db_unset(ctrlSettings[i].name, globalFlag);
1681 }else if( g.argc==4 ){
1682 db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
1683 }else{
 
1684 print_setting(ctrlSettings[i].name);
1685 }
 
 
 
1686 }else{
1687 usage("?PROPERTY? ?VALUE?");
1688 }
1689 }
1690
1691
--- src/db.c
+++ src/db.c
@@ -1510,23 +1510,24 @@
1510 int width; /* Width of display. 0 for boolean values */
1511 char const *def; /* Default value */
1512 };
1513 #endif /* INTERFACE */
1514 struct stControlSettings const ctrlSettings[] = {
1515 { "auto-captcha", "autocaptcha", 0, "on" },
1516 { "auto-shun", 0, 0, "on" },
1517 { "autosync", 0, 0, "on" },
1518 { "binary-glob", 0, 32, "" },
1519 { "clearsign", 0, 0, "off" },
1520 { "diff-command", 0, 16, "" },
1521 { "dont-push", 0, 0, "off" },
1522 { "editor", 0, 16, "" },
1523 { "gdiff-command", 0, 16, "gdiff" },
1524 { "ignore-glob", 0, 40, "" },
1525 { "http-port", 0, 16, "8080" },
1526 { "localauth", 0, 0, "off" },
1527 { "manifest", 0, 0, "off" },
1528 { "mtime-changes", 0, 0, "off" },
1529 { "pgp-command", 0, 32, "gpg --clearsign -o " },
1530 { "proxy", 0, 32, "off" },
1531 { "push-hook-cmd", 0, 32, "" },
1532 { "push-hook-force",
1533 0, 0, "" },
@@ -1534,10 +1535,11 @@
1535 0, 32, "" },
1536 { "push-hook-pattern-server",
1537 0, 32, "" },
1538 { "push-hook-privilege",
1539 0, 1, "" },
1540 { "repo-cksum", 0, 0, "on" },
1541 { "ssh-command", 0, 32, "" },
1542 { "web-browser", 0, 32, "" },
1543 { 0,0,0,0 }
1544 };
1545
@@ -1622,14 +1624,16 @@
1624 ** if this is set on the client, it will request always
1625 ** the hook activation, even if no files where pushed on
1626 ** the sync.
1627 ** if this is set on the server, it will accept hook
1628 ** activiation, even if no files where pushed.
1629 ** Default: on
1630 **
1631 ** push-hook-pattern-client
1632 ** if set, a client push will sent this message to the
1633 ** server, to activate the push hook command.
1634 ** Default: on
1635 **
1636 ** push-hook-pattern-server
1637 ** if set, and a client send this pattern at the end of
1638 ** a push, the push hook command will be executed. This
1639 ** might be a prefix of the pattern, sent by the client.
@@ -1639,10 +1643,15 @@
1643 ** to trigger the hook. Valid privileges are:
1644 ** s (setup), a (admin), i (checkin) or o (checkout)
1645 **
1646 ** ssh-command Command used to talk to a remote machine with
1647 ** the "ssh://" protocol.
1648 **
1649 ** repo-cksum Compute checksums over all files in each checkout
1650 ** as a double-check of correctness. Defaults to "on".
1651 ** Disable on large repositories for a performance
1652 ** improvement.
1653 **
1654 ** web-browser A shell command used to launch your preferred
1655 ** web browser when given a URL as an argument.
1656 ** Defaults to "start" on windows, "open" on Mac,
1657 ** and "firefox" on Unix.
@@ -1667,24 +1676,33 @@
1676 for(i=0; ctrlSettings[i].name; i++){
1677 print_setting(ctrlSettings[i].name);
1678 }
1679 }else if( g.argc==3 || g.argc==4 ){
1680 const char *zName = g.argv[2];
1681 int isManifest;
1682 int n = strlen(zName);
1683 for(i=0; ctrlSettings[i].name; i++){
1684 if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
1685 }
1686 if( !ctrlSettings[i].name ){
1687 fossil_fatal("no such setting: %s", zName);
1688 }
1689 isManifest = strcmp(ctrlSettings[i].name, "manifest")==0;
1690 if( isManifest && globalFlag ){
1691 fossil_fatal("cannot set 'manifest' globally");
1692 }
1693 if( unsetFlag ){
1694 db_unset(ctrlSettings[i].name, globalFlag);
1695 }else if( g.argc==4 ){
1696 db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
1697 }else{
1698 isManifest = 0;
1699 print_setting(ctrlSettings[i].name);
1700 }
1701 if( isManifest ){
1702 manifest_to_disk(db_lget_int("checkout", 0));
1703 }
1704 }else{
1705 usage("?PROPERTY? ?VALUE?");
1706 }
1707 }
1708
1709
+21 -10
--- src/delta.c
+++ src/delta.c
@@ -195,31 +195,38 @@
195195
/*
196196
** Compute a 32-bit checksum on the N-byte buffer. Return the result.
197197
*/
198198
static unsigned int checksum(const char *zIn, size_t N){
199199
const unsigned char *z = (const unsigned char *)zIn;
200
- unsigned sum = 0;
200
+ unsigned sum0 = 0;
201
+ unsigned sum1 = 0;
202
+ unsigned sum2 = 0;
203
+ unsigned sum3 = 0;
201204
while(N >= 16){
202
- sum += ((unsigned)z[0] + z[4] + z[8] + z[12]) << 24;
203
- sum += ((unsigned)z[1] + z[5] + z[9] + z[13]) << 16;
204
- sum += ((unsigned)z[2] + z[6] + z[10]+ z[14]) << 8;
205
- sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
205
+ sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
206
+ sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
207
+ sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
208
+ sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
206209
z += 16;
207210
N -= 16;
208211
}
209212
while(N >= 4){
210
- sum += (z[0]<<24) | (z[1]<<16) | (z[2]<<8) | z[3];
213
+ sum0 += z[0];
214
+ sum1 += z[1];
215
+ sum2 += z[2];
216
+ sum3 += z[3];
211217
z += 4;
212218
N -= 4;
213219
}
220
+ sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
214221
switch(N){
215
- case 3: sum += (z[2] << 8);
216
- case 2: sum += (z[1] << 16);
217
- case 1: sum += (z[0] << 24);
222
+ case 3: sum3 += (z[2] << 8);
223
+ case 2: sum3 += (z[1] << 16);
224
+ case 1: sum3 += (z[0] << 24);
218225
default: ;
219226
}
220
- return sum;
227
+ return sum3;
221228
}
222229
223230
/*
224231
** Create a new delta.
225232
**
@@ -512,11 +519,13 @@
512519
int lenDelta, /* Length of the delta */
513520
char *zOut /* Write the output into this preallocated buffer */
514521
){
515522
unsigned int limit;
516523
unsigned int total = 0;
524
+#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
517525
char *zOrigOut = zOut;
526
+#endif
518527
519528
limit = getInt(&zDelta, &lenDelta);
520529
if( *zDelta!='\n' ){
521530
/* ERROR: size integer not terminated by "\n" */
522531
return -1;
@@ -567,14 +576,16 @@
567576
break;
568577
}
569578
case ';': {
570579
zDelta++; lenDelta--;
571580
zOut[0] = 0;
581
+#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
572582
if( cnt!=checksum(zOrigOut, total) ){
573583
/* ERROR: bad checksum */
574584
return -1;
575585
}
586
+#endif
576587
if( total!=limit ){
577588
/* ERROR: generated size does not match predicted size */
578589
return -1;
579590
}
580591
return total;
581592
--- src/delta.c
+++ src/delta.c
@@ -195,31 +195,38 @@
195 /*
196 ** Compute a 32-bit checksum on the N-byte buffer. Return the result.
197 */
198 static unsigned int checksum(const char *zIn, size_t N){
199 const unsigned char *z = (const unsigned char *)zIn;
200 unsigned sum = 0;
 
 
 
201 while(N >= 16){
202 sum += ((unsigned)z[0] + z[4] + z[8] + z[12]) << 24;
203 sum += ((unsigned)z[1] + z[5] + z[9] + z[13]) << 16;
204 sum += ((unsigned)z[2] + z[6] + z[10]+ z[14]) << 8;
205 sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
206 z += 16;
207 N -= 16;
208 }
209 while(N >= 4){
210 sum += (z[0]<<24) | (z[1]<<16) | (z[2]<<8) | z[3];
 
 
 
211 z += 4;
212 N -= 4;
213 }
 
214 switch(N){
215 case 3: sum += (z[2] << 8);
216 case 2: sum += (z[1] << 16);
217 case 1: sum += (z[0] << 24);
218 default: ;
219 }
220 return sum;
221 }
222
223 /*
224 ** Create a new delta.
225 **
@@ -512,11 +519,13 @@
512 int lenDelta, /* Length of the delta */
513 char *zOut /* Write the output into this preallocated buffer */
514 ){
515 unsigned int limit;
516 unsigned int total = 0;
 
517 char *zOrigOut = zOut;
 
518
519 limit = getInt(&zDelta, &lenDelta);
520 if( *zDelta!='\n' ){
521 /* ERROR: size integer not terminated by "\n" */
522 return -1;
@@ -567,14 +576,16 @@
567 break;
568 }
569 case ';': {
570 zDelta++; lenDelta--;
571 zOut[0] = 0;
 
572 if( cnt!=checksum(zOrigOut, total) ){
573 /* ERROR: bad checksum */
574 return -1;
575 }
 
576 if( total!=limit ){
577 /* ERROR: generated size does not match predicted size */
578 return -1;
579 }
580 return total;
581
--- src/delta.c
+++ src/delta.c
@@ -195,31 +195,38 @@
195 /*
196 ** Compute a 32-bit checksum on the N-byte buffer. Return the result.
197 */
198 static unsigned int checksum(const char *zIn, size_t N){
199 const unsigned char *z = (const unsigned char *)zIn;
200 unsigned sum0 = 0;
201 unsigned sum1 = 0;
202 unsigned sum2 = 0;
203 unsigned sum3 = 0;
204 while(N >= 16){
205 sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
206 sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
207 sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
208 sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
209 z += 16;
210 N -= 16;
211 }
212 while(N >= 4){
213 sum0 += z[0];
214 sum1 += z[1];
215 sum2 += z[2];
216 sum3 += z[3];
217 z += 4;
218 N -= 4;
219 }
220 sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
221 switch(N){
222 case 3: sum3 += (z[2] << 8);
223 case 2: sum3 += (z[1] << 16);
224 case 1: sum3 += (z[0] << 24);
225 default: ;
226 }
227 return sum3;
228 }
229
230 /*
231 ** Create a new delta.
232 **
@@ -512,11 +519,13 @@
519 int lenDelta, /* Length of the delta */
520 char *zOut /* Write the output into this preallocated buffer */
521 ){
522 unsigned int limit;
523 unsigned int total = 0;
524 #ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
525 char *zOrigOut = zOut;
526 #endif
527
528 limit = getInt(&zDelta, &lenDelta);
529 if( *zDelta!='\n' ){
530 /* ERROR: size integer not terminated by "\n" */
531 return -1;
@@ -567,14 +576,16 @@
576 break;
577 }
578 case ';': {
579 zDelta++; lenDelta--;
580 zOut[0] = 0;
581 #ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
582 if( cnt!=checksum(zOrigOut, total) ){
583 /* ERROR: bad checksum */
584 return -1;
585 }
586 #endif
587 if( total!=limit ){
588 /* ERROR: generated size does not match predicted size */
589 return -1;
590 }
591 return total;
592
+37 -34
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -356,53 +356,56 @@
356356
const char *zFrom,
357357
const char *zTo,
358358
const char *zDiffCmd,
359359
int diffFlags
360360
){
361
- Manifest mFrom, mTo;
362
- int iFrom, iTo;
361
+ Manifest *pFrom, *pTo;
362
+ ManifestFile *pFromFile, *pToFile;
363363
int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
364364
int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
365365
366
- manifest_from_name(zFrom, &mFrom);
367
- manifest_from_name(zTo, &mTo);
368
- iFrom = iTo = 0;
369
- while( iFrom<mFrom.nFile || iTo<mTo.nFile ){
366
+ pFrom = manifest_get_by_name(zFrom, 0);
367
+ manifest_file_rewind(pFrom);
368
+ pFromFile = manifest_file_next(pFrom,0);
369
+ pTo = manifest_get_by_name(zTo, 0);
370
+ manifest_file_rewind(pTo);
371
+ pToFile = manifest_file_next(pTo,0);
372
+
373
+ while( pFromFile || pToFile ){
370374
int cmp;
371
- if( iFrom>=mFrom.nFile ){
375
+ if( pFromFile==0 ){
372376
cmp = +1;
373
- }else if( iTo>=mTo.nFile ){
377
+ }else if( pToFile==0 ){
374378
cmp = -1;
375379
}else{
376
- cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
380
+ cmp = strcmp(pFromFile->zName, pToFile->zName);
377381
}
378382
if( cmp<0 ){
379
- printf("DELETED %s\n", mFrom.aFile[iFrom].zName);
380
- if( asNewFlag ){
381
- diff_manifest_entry(&mFrom.aFile[iFrom], 0, zDiffCmd, ignoreEolWs);
382
- }
383
- iFrom++;
384
- }else if( cmp>0 ){
385
- printf("ADDED %s\n", mTo.aFile[iTo].zName);
386
- if( asNewFlag ){
387
- diff_manifest_entry(0, &mTo.aFile[iTo], zDiffCmd, ignoreEolWs);
388
- }
389
- iTo++;
390
- }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
391
- /* No changes */
392
- iFrom++;
393
- iTo++;
394
- }else{
395
- printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
396
- diff_manifest_entry(&mFrom.aFile[iFrom], &mTo.aFile[iTo],
397
- zDiffCmd, ignoreEolWs);
398
- iFrom++;
399
- iTo++;
400
- }
401
- }
402
- manifest_clear(&mFrom);
403
- manifest_clear(&mTo);
383
+ printf("DELETED %s\n", pFromFile->zName);
384
+ if( asNewFlag ){
385
+ diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs);
386
+ }
387
+ pFromFile = manifest_file_next(pFrom,0);
388
+ }else if( cmp>0 ){
389
+ printf("ADDED %s\n", pToFile->zName);
390
+ if( asNewFlag ){
391
+ diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs);
392
+ }
393
+ pToFile = manifest_file_next(pTo,0);
394
+ }else if( strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
395
+ /* No changes */
396
+ pFromFile = manifest_file_next(pFrom,0);
397
+ pToFile = manifest_file_next(pTo,0);
398
+ }else{
399
+ printf("CHANGED %s\n", pFromFile->zName);
400
+ diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs);
401
+ pFromFile = manifest_file_next(pFrom,0);
402
+ pToFile = manifest_file_next(pTo,0);
403
+ }
404
+ }
405
+ manifest_destroy(pFrom);
406
+ manifest_destroy(pTo);
404407
}
405408
406409
/*
407410
** COMMAND: diff
408411
** COMMAND: gdiff
409412
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -356,53 +356,56 @@
356 const char *zFrom,
357 const char *zTo,
358 const char *zDiffCmd,
359 int diffFlags
360 ){
361 Manifest mFrom, mTo;
362 int iFrom, iTo;
363 int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
364 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
365
366 manifest_from_name(zFrom, &mFrom);
367 manifest_from_name(zTo, &mTo);
368 iFrom = iTo = 0;
369 while( iFrom<mFrom.nFile || iTo<mTo.nFile ){
 
 
 
 
370 int cmp;
371 if( iFrom>=mFrom.nFile ){
372 cmp = +1;
373 }else if( iTo>=mTo.nFile ){
374 cmp = -1;
375 }else{
376 cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
377 }
378 if( cmp<0 ){
379 printf("DELETED %s\n", mFrom.aFile[iFrom].zName);
380 if( asNewFlag ){
381 diff_manifest_entry(&mFrom.aFile[iFrom], 0, zDiffCmd, ignoreEolWs);
382 }
383 iFrom++;
384 }else if( cmp>0 ){
385 printf("ADDED %s\n", mTo.aFile[iTo].zName);
386 if( asNewFlag ){
387 diff_manifest_entry(0, &mTo.aFile[iTo], zDiffCmd, ignoreEolWs);
388 }
389 iTo++;
390 }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
391 /* No changes */
392 iFrom++;
393 iTo++;
394 }else{
395 printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
396 diff_manifest_entry(&mFrom.aFile[iFrom], &mTo.aFile[iTo],
397 zDiffCmd, ignoreEolWs);
398 iFrom++;
399 iTo++;
400 }
401 }
402 manifest_clear(&mFrom);
403 manifest_clear(&mTo);
404 }
405
406 /*
407 ** COMMAND: diff
408 ** COMMAND: gdiff
409
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -356,53 +356,56 @@
356 const char *zFrom,
357 const char *zTo,
358 const char *zDiffCmd,
359 int diffFlags
360 ){
361 Manifest *pFrom, *pTo;
362 ManifestFile *pFromFile, *pToFile;
363 int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
364 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
365
366 pFrom = manifest_get_by_name(zFrom, 0);
367 manifest_file_rewind(pFrom);
368 pFromFile = manifest_file_next(pFrom,0);
369 pTo = manifest_get_by_name(zTo, 0);
370 manifest_file_rewind(pTo);
371 pToFile = manifest_file_next(pTo,0);
372
373 while( pFromFile || pToFile ){
374 int cmp;
375 if( pFromFile==0 ){
376 cmp = +1;
377 }else if( pToFile==0 ){
378 cmp = -1;
379 }else{
380 cmp = strcmp(pFromFile->zName, pToFile->zName);
381 }
382 if( cmp<0 ){
383 printf("DELETED %s\n", pFromFile->zName);
384 if( asNewFlag ){
385 diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs);
386 }
387 pFromFile = manifest_file_next(pFrom,0);
388 }else if( cmp>0 ){
389 printf("ADDED %s\n", pToFile->zName);
390 if( asNewFlag ){
391 diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs);
392 }
393 pToFile = manifest_file_next(pTo,0);
394 }else if( strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
395 /* No changes */
396 pFromFile = manifest_file_next(pFrom,0);
397 pToFile = manifest_file_next(pTo,0);
398 }else{
399 printf("CHANGED %s\n", pFromFile->zName);
400 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs);
401 pFromFile = manifest_file_next(pFrom,0);
402 pToFile = manifest_file_next(pTo,0);
403 }
404 }
405 manifest_destroy(pFrom);
406 manifest_destroy(pTo);
 
407 }
408
409 /*
410 ** COMMAND: diff
411 ** COMMAND: gdiff
412
+37 -34
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -356,53 +356,56 @@
356356
const char *zFrom,
357357
const char *zTo,
358358
const char *zDiffCmd,
359359
int diffFlags
360360
){
361
- Manifest mFrom, mTo;
362
- int iFrom, iTo;
361
+ Manifest *pFrom, *pTo;
362
+ ManifestFile *pFromFile, *pToFile;
363363
int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
364364
int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
365365
366
- manifest_from_name(zFrom, &mFrom);
367
- manifest_from_name(zTo, &mTo);
368
- iFrom = iTo = 0;
369
- while( iFrom<mFrom.nFile || iTo<mTo.nFile ){
366
+ pFrom = manifest_get_by_name(zFrom, 0);
367
+ manifest_file_rewind(pFrom);
368
+ pFromFile = manifest_file_next(pFrom,0);
369
+ pTo = manifest_get_by_name(zTo, 0);
370
+ manifest_file_rewind(pTo);
371
+ pToFile = manifest_file_next(pTo,0);
372
+
373
+ while( pFromFile || pToFile ){
370374
int cmp;
371
- if( iFrom>=mFrom.nFile ){
375
+ if( pFromFile==0 ){
372376
cmp = +1;
373
- }else if( iTo>=mTo.nFile ){
377
+ }else if( pToFile==0 ){
374378
cmp = -1;
375379
}else{
376
- cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
380
+ cmp = strcmp(pFromFile->zName, pToFile->zName);
377381
}
378382
if( cmp<0 ){
379
- printf("DELETED %s\n", mFrom.aFile[iFrom].zName);
380
- if( asNewFlag ){
381
- diff_manifest_entry(&mFrom.aFile[iFrom], 0, zDiffCmd, ignoreEolWs);
382
- }
383
- iFrom++;
384
- }else if( cmp>0 ){
385
- printf("ADDED %s\n", mTo.aFile[iTo].zName);
386
- if( asNewFlag ){
387
- diff_manifest_entry(0, &mTo.aFile[iTo], zDiffCmd, ignoreEolWs);
388
- }
389
- iTo++;
390
- }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
391
- /* No changes */
392
- iFrom++;
393
- iTo++;
394
- }else{
395
- printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
396
- diff_manifest_entry(&mFrom.aFile[iFrom], &mTo.aFile[iTo],
397
- zDiffCmd, ignoreEolWs);
398
- iFrom++;
399
- iTo++;
400
- }
401
- }
402
- manifest_clear(&mFrom);
403
- manifest_clear(&mTo);
383
+ printf("DELETED %s\n", pFromFile->zName);
384
+ if( asNewFlag ){
385
+ diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs);
386
+ }
387
+ pFromFile = manifest_file_next(pFrom,0);
388
+ }else if( cmp>0 ){
389
+ printf("ADDED %s\n", pToFile->zName);
390
+ if( asNewFlag ){
391
+ diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs);
392
+ }
393
+ pToFile = manifest_file_next(pTo,0);
394
+ }else if( strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
395
+ /* No changes */
396
+ pFromFile = manifest_file_next(pFrom,0);
397
+ pToFile = manifest_file_next(pTo,0);
398
+ }else{
399
+ printf("CHANGED %s\n", pFromFile->zName);
400
+ diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs);
401
+ pFromFile = manifest_file_next(pFrom,0);
402
+ pToFile = manifest_file_next(pTo,0);
403
+ }
404
+ }
405
+ manifest_destroy(pFrom);
406
+ manifest_destroy(pTo);
404407
}
405408
406409
/*
407410
** COMMAND: diff
408411
** COMMAND: gdiff
409412
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -356,53 +356,56 @@
356 const char *zFrom,
357 const char *zTo,
358 const char *zDiffCmd,
359 int diffFlags
360 ){
361 Manifest mFrom, mTo;
362 int iFrom, iTo;
363 int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
364 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
365
366 manifest_from_name(zFrom, &mFrom);
367 manifest_from_name(zTo, &mTo);
368 iFrom = iTo = 0;
369 while( iFrom<mFrom.nFile || iTo<mTo.nFile ){
 
 
 
 
370 int cmp;
371 if( iFrom>=mFrom.nFile ){
372 cmp = +1;
373 }else if( iTo>=mTo.nFile ){
374 cmp = -1;
375 }else{
376 cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
377 }
378 if( cmp<0 ){
379 printf("DELETED %s\n", mFrom.aFile[iFrom].zName);
380 if( asNewFlag ){
381 diff_manifest_entry(&mFrom.aFile[iFrom], 0, zDiffCmd, ignoreEolWs);
382 }
383 iFrom++;
384 }else if( cmp>0 ){
385 printf("ADDED %s\n", mTo.aFile[iTo].zName);
386 if( asNewFlag ){
387 diff_manifest_entry(0, &mTo.aFile[iTo], zDiffCmd, ignoreEolWs);
388 }
389 iTo++;
390 }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
391 /* No changes */
392 iFrom++;
393 iTo++;
394 }else{
395 printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
396 diff_manifest_entry(&mFrom.aFile[iFrom], &mTo.aFile[iTo],
397 zDiffCmd, ignoreEolWs);
398 iFrom++;
399 iTo++;
400 }
401 }
402 manifest_clear(&mFrom);
403 manifest_clear(&mTo);
404 }
405
406 /*
407 ** COMMAND: diff
408 ** COMMAND: gdiff
409
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -356,53 +356,56 @@
356 const char *zFrom,
357 const char *zTo,
358 const char *zDiffCmd,
359 int diffFlags
360 ){
361 Manifest *pFrom, *pTo;
362 ManifestFile *pFromFile, *pToFile;
363 int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
364 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
365
366 pFrom = manifest_get_by_name(zFrom, 0);
367 manifest_file_rewind(pFrom);
368 pFromFile = manifest_file_next(pFrom,0);
369 pTo = manifest_get_by_name(zTo, 0);
370 manifest_file_rewind(pTo);
371 pToFile = manifest_file_next(pTo,0);
372
373 while( pFromFile || pToFile ){
374 int cmp;
375 if( pFromFile==0 ){
376 cmp = +1;
377 }else if( pToFile==0 ){
378 cmp = -1;
379 }else{
380 cmp = strcmp(pFromFile->zName, pToFile->zName);
381 }
382 if( cmp<0 ){
383 printf("DELETED %s\n", pFromFile->zName);
384 if( asNewFlag ){
385 diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs);
386 }
387 pFromFile = manifest_file_next(pFrom,0);
388 }else if( cmp>0 ){
389 printf("ADDED %s\n", pToFile->zName);
390 if( asNewFlag ){
391 diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs);
392 }
393 pToFile = manifest_file_next(pTo,0);
394 }else if( strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
395 /* No changes */
396 pFromFile = manifest_file_next(pFrom,0);
397 pToFile = manifest_file_next(pTo,0);
398 }else{
399 printf("CHANGED %s\n", pFromFile->zName);
400 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs);
401 pFromFile = manifest_file_next(pFrom,0);
402 pToFile = manifest_file_next(pTo,0);
403 }
404 }
405 manifest_destroy(pFrom);
406 manifest_destroy(pTo);
 
407 }
408
409 /*
410 ** COMMAND: diff
411 ** COMMAND: gdiff
412
+9 -10
--- src/doc.c
+++ src/doc.c
@@ -388,37 +388,36 @@
388388
goto doc_not_found;
389389
}
390390
391391
if( rid==0 ){
392392
Stmt s;
393
- Blob baseline;
394
- Manifest m;
393
+ Manifest *pM;
394
+ ManifestFile *pFile;
395395
396396
/* Add the vid baseline to the cache */
397397
if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){
398398
db_multi_exec("DELETE FROM vcache");
399399
}
400
- if( content_get(vid, &baseline)==0 ){
401
- goto doc_not_found;
402
- }
403
- if( manifest_parse(&m, &baseline)==0 || m.type!=CFTYPE_MANIFEST ){
400
+ pM = manifest_get(vid, CFTYPE_MANIFEST);
401
+ if( pM==0 ){
404402
goto doc_not_found;
405403
}
406404
db_prepare(&s,
407405
"INSERT INTO vcache(vid,fname,rid)"
408406
" SELECT %d, :fname, rid FROM blob"
409407
" WHERE uuid=:uuid",
410408
vid
411409
);
412
- for(i=0; i<m.nFile; i++){
413
- db_bind_text(&s, ":fname", m.aFile[i].zName);
414
- db_bind_text(&s, ":uuid", m.aFile[i].zUuid);
410
+ manifest_file_rewind(pM);
411
+ while( (pFile = manifest_file_next(pM,0))!=0 ){
412
+ db_bind_text(&s, ":fname", pFile->zName);
413
+ db_bind_text(&s, ":uuid", pFile->zUuid);
415414
db_step(&s);
416415
db_reset(&s);
417416
}
418417
db_finalize(&s);
419
- manifest_clear(&m);
418
+ manifest_destroy(pM);
420419
421420
/* Try again to find the file */
422421
rid = db_int(0, "SELECT rid FROM vcache"
423422
" WHERE vid=%d AND fname=%Q", vid, zName);
424423
}
425424
--- src/doc.c
+++ src/doc.c
@@ -388,37 +388,36 @@
388 goto doc_not_found;
389 }
390
391 if( rid==0 ){
392 Stmt s;
393 Blob baseline;
394 Manifest m;
395
396 /* Add the vid baseline to the cache */
397 if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){
398 db_multi_exec("DELETE FROM vcache");
399 }
400 if( content_get(vid, &baseline)==0 ){
401 goto doc_not_found;
402 }
403 if( manifest_parse(&m, &baseline)==0 || m.type!=CFTYPE_MANIFEST ){
404 goto doc_not_found;
405 }
406 db_prepare(&s,
407 "INSERT INTO vcache(vid,fname,rid)"
408 " SELECT %d, :fname, rid FROM blob"
409 " WHERE uuid=:uuid",
410 vid
411 );
412 for(i=0; i<m.nFile; i++){
413 db_bind_text(&s, ":fname", m.aFile[i].zName);
414 db_bind_text(&s, ":uuid", m.aFile[i].zUuid);
 
415 db_step(&s);
416 db_reset(&s);
417 }
418 db_finalize(&s);
419 manifest_clear(&m);
420
421 /* Try again to find the file */
422 rid = db_int(0, "SELECT rid FROM vcache"
423 " WHERE vid=%d AND fname=%Q", vid, zName);
424 }
425
--- src/doc.c
+++ src/doc.c
@@ -388,37 +388,36 @@
388 goto doc_not_found;
389 }
390
391 if( rid==0 ){
392 Stmt s;
393 Manifest *pM;
394 ManifestFile *pFile;
395
396 /* Add the vid baseline to the cache */
397 if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){
398 db_multi_exec("DELETE FROM vcache");
399 }
400 pM = manifest_get(vid, CFTYPE_MANIFEST);
401 if( pM==0 ){
 
 
402 goto doc_not_found;
403 }
404 db_prepare(&s,
405 "INSERT INTO vcache(vid,fname,rid)"
406 " SELECT %d, :fname, rid FROM blob"
407 " WHERE uuid=:uuid",
408 vid
409 );
410 manifest_file_rewind(pM);
411 while( (pFile = manifest_file_next(pM,0))!=0 ){
412 db_bind_text(&s, ":fname", pFile->zName);
413 db_bind_text(&s, ":uuid", pFile->zUuid);
414 db_step(&s);
415 db_reset(&s);
416 }
417 db_finalize(&s);
418 manifest_destroy(pM);
419
420 /* Try again to find the file */
421 rid = db_int(0, "SELECT rid FROM vcache"
422 " WHERE vid=%d AND fname=%Q", vid, zName);
423 }
424
+3 -2
--- src/encode.c
+++ src/encode.c
@@ -267,12 +267,13 @@
267267
/*
268268
** Decode a fossilized string in-place.
269269
*/
270270
void defossilize(char *z){
271271
int i, j, c;
272
- for(i=j=0; z[i]; i++){
273
- c = z[i];
272
+ for(i=0; (c=z[i])!=0 && c!='\\'; i++){}
273
+ if( c==0 ) return;
274
+ for(j=i; (c=z[i])!=0; i++){
274275
if( c=='\\' && z[i+1] ){
275276
i++;
276277
switch( z[i] ){
277278
case 'n': c = '\n'; break;
278279
case 's': c = ' '; break;
279280
--- src/encode.c
+++ src/encode.c
@@ -267,12 +267,13 @@
267 /*
268 ** Decode a fossilized string in-place.
269 */
270 void defossilize(char *z){
271 int i, j, c;
272 for(i=j=0; z[i]; i++){
273 c = z[i];
 
274 if( c=='\\' && z[i+1] ){
275 i++;
276 switch( z[i] ){
277 case 'n': c = '\n'; break;
278 case 's': c = ' '; break;
279
--- src/encode.c
+++ src/encode.c
@@ -267,12 +267,13 @@
267 /*
268 ** Decode a fossilized string in-place.
269 */
270 void defossilize(char *z){
271 int i, j, c;
272 for(i=0; (c=z[i])!=0 && c!='\\'; i++){}
273 if( c==0 ) return;
274 for(j=i; (c=z[i])!=0; i++){
275 if( c=='\\' && z[i+1] ){
276 i++;
277 switch( z[i] ){
278 case 'n': c = '\n'; break;
279 case 's': c = ' '; break;
280
+18 -26
--- src/event.c
+++ src/event.c
@@ -63,12 +63,11 @@
6363
const char *zEventId; /* Event identifier */
6464
char *zETime; /* Time of the event */
6565
char *zATime; /* Time the artifact was created */
6666
int specRid; /* rid specified by aid= parameter */
6767
int prevRid, nextRid; /* Previous or next edits of this event */
68
- Manifest m; /* Parsed event artifact */
69
- Blob content; /* Original event artifact content */
68
+ Manifest *pEvent; /* Parsed event artifact */
7069
Blob fullbody; /* Complete content of the event body */
7170
Blob title; /* Title extracted from the event body */
7271
Blob tail; /* Event body that comes after the title */
7372
Stmt q1; /* Query to search for the event */
7473
int showDetail; /* True to show details */
@@ -113,18 +112,15 @@
113112
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
114113
showDetail = atoi(PD("detail","0"));
115114
116115
/* Extract the event content.
117116
*/
118
- memset(&m, 0, sizeof(m));
119
- blob_zero(&m.content);
120
- content_get(rid, &content);
121
- manifest_parse(&m, &content);
122
- if( m.type!=CFTYPE_EVENT ){
117
+ pEvent = manifest_get(rid, CFTYPE_EVENT);
118
+ if( pEvent==0 ){
123119
fossil_panic("Object #%d is not an event", rid);
124120
}
125
- blob_init(&fullbody, m.zWiki, -1);
121
+ blob_init(&fullbody, pEvent->zWiki, -1);
126122
if( wiki_find_title(&fullbody, &title, &tail) ){
127123
style_header(blob_str(&title));
128124
}else{
129125
style_header("Event %S", zEventId);
130126
tail = fullbody;
@@ -131,11 +127,11 @@
131127
}
132128
if( g.okWrWiki && g.okWrite && nextRid==0 ){
133129
style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
134130
g.zTop, zEventId);
135131
}
136
- zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
132
+ zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
137133
style_submenu_element("Context", "Context", "%s/timeline?c=%T",
138134
g.zTop, zETime);
139135
if( g.okHistory ){
140136
if( showDetail ){
141137
style_submenu_element("Plain", "Plain", "%s/event?name=%s&amp;aid=%s",
@@ -166,37 +162,37 @@
166162
if( showDetail && g.okHistory ){
167163
int i;
168164
const char *zClr = 0;
169165
Blob comment;
170166
171
- zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate);
167
+ zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate);
172168
@ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
173169
@ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>]
174
- @ entered by user <b>%h(m.zUser)</b> on
170
+ @ entered by user <b>%h(pEvent->zUser)</b> on
175171
@ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
176172
@ <blockquote>
177
- for(i=0; i<m.nTag; i++){
178
- if( strcmp(m.aTag[i].zName,"+bgcolor")==0 ){
179
- zClr = m.aTag[i].zValue;
173
+ for(i=0; i<pEvent->nTag; i++){
174
+ if( strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){
175
+ zClr = pEvent->aTag[i].zValue;
180176
}
181177
}
182178
if( zClr && zClr[0]==0 ) zClr = 0;
183179
if( zClr ){
184180
@ <div style="background-color: %h(zClr);">
185181
}else{
186182
@ <div>
187183
}
188
- blob_init(&comment, m.zComment, -1);
184
+ blob_init(&comment, pEvent->zComment, -1);
189185
wiki_convert(&comment, 0, WIKI_INLINE);
190186
blob_reset(&comment);
191187
@ </div>
192188
@ </blockquote><hr />
193189
}
194190
195191
wiki_convert(&tail, 0, 0);
196192
style_footer();
197
- manifest_clear(&m);
193
+ manifest_destroy(pEvent);
198194
}
199195
200196
/*
201197
** WEBPAGE: eventedit
202198
** URL: /eventedit?name=EVENTID
@@ -259,22 +255,18 @@
259255
260256
/* If editing an existing event, extract the key fields to use as
261257
** a starting point for the edit.
262258
*/
263259
if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
264
- Manifest m;
265
- Blob content;
266
- memset(&m, 0, sizeof(m));
267
- blob_zero(&m.content);
268
- content_get(rid, &content);
269
- manifest_parse(&m, &content);
270
- if( m.type==CFTYPE_EVENT ){
271
- if( zBody==0 ) zBody = m.zWiki;
260
+ Manifest *pEvent;
261
+ pEvent = manifest_get(rid, CFTYPE_EVENT);
262
+ if( pEvent && pEvent->type==CFTYPE_EVENT ){
263
+ if( zBody==0 ) zBody = pEvent->zWiki;
272264
if( zETime==0 ){
273
- zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
265
+ zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
274266
}
275
- if( zComment==0 ) zComment = m.zComment;
267
+ if( zComment==0 ) zComment = pEvent->zComment;
276268
}
277269
if( zTags==0 ){
278270
zTags = db_text(0,
279271
"SELECT group_concat(substr(tagname,5),', ')"
280272
" FROM tagxref, tag"
281273
--- src/event.c
+++ src/event.c
@@ -63,12 +63,11 @@
63 const char *zEventId; /* Event identifier */
64 char *zETime; /* Time of the event */
65 char *zATime; /* Time the artifact was created */
66 int specRid; /* rid specified by aid= parameter */
67 int prevRid, nextRid; /* Previous or next edits of this event */
68 Manifest m; /* Parsed event artifact */
69 Blob content; /* Original event artifact content */
70 Blob fullbody; /* Complete content of the event body */
71 Blob title; /* Title extracted from the event body */
72 Blob tail; /* Event body that comes after the title */
73 Stmt q1; /* Query to search for the event */
74 int showDetail; /* True to show details */
@@ -113,18 +112,15 @@
113 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
114 showDetail = atoi(PD("detail","0"));
115
116 /* Extract the event content.
117 */
118 memset(&m, 0, sizeof(m));
119 blob_zero(&m.content);
120 content_get(rid, &content);
121 manifest_parse(&m, &content);
122 if( m.type!=CFTYPE_EVENT ){
123 fossil_panic("Object #%d is not an event", rid);
124 }
125 blob_init(&fullbody, m.zWiki, -1);
126 if( wiki_find_title(&fullbody, &title, &tail) ){
127 style_header(blob_str(&title));
128 }else{
129 style_header("Event %S", zEventId);
130 tail = fullbody;
@@ -131,11 +127,11 @@
131 }
132 if( g.okWrWiki && g.okWrite && nextRid==0 ){
133 style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
134 g.zTop, zEventId);
135 }
136 zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
137 style_submenu_element("Context", "Context", "%s/timeline?c=%T",
138 g.zTop, zETime);
139 if( g.okHistory ){
140 if( showDetail ){
141 style_submenu_element("Plain", "Plain", "%s/event?name=%s&amp;aid=%s",
@@ -166,37 +162,37 @@
166 if( showDetail && g.okHistory ){
167 int i;
168 const char *zClr = 0;
169 Blob comment;
170
171 zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate);
172 @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
173 @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>]
174 @ entered by user <b>%h(m.zUser)</b> on
175 @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
176 @ <blockquote>
177 for(i=0; i<m.nTag; i++){
178 if( strcmp(m.aTag[i].zName,"+bgcolor")==0 ){
179 zClr = m.aTag[i].zValue;
180 }
181 }
182 if( zClr && zClr[0]==0 ) zClr = 0;
183 if( zClr ){
184 @ <div style="background-color: %h(zClr);">
185 }else{
186 @ <div>
187 }
188 blob_init(&comment, m.zComment, -1);
189 wiki_convert(&comment, 0, WIKI_INLINE);
190 blob_reset(&comment);
191 @ </div>
192 @ </blockquote><hr />
193 }
194
195 wiki_convert(&tail, 0, 0);
196 style_footer();
197 manifest_clear(&m);
198 }
199
200 /*
201 ** WEBPAGE: eventedit
202 ** URL: /eventedit?name=EVENTID
@@ -259,22 +255,18 @@
259
260 /* If editing an existing event, extract the key fields to use as
261 ** a starting point for the edit.
262 */
263 if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
264 Manifest m;
265 Blob content;
266 memset(&m, 0, sizeof(m));
267 blob_zero(&m.content);
268 content_get(rid, &content);
269 manifest_parse(&m, &content);
270 if( m.type==CFTYPE_EVENT ){
271 if( zBody==0 ) zBody = m.zWiki;
272 if( zETime==0 ){
273 zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
274 }
275 if( zComment==0 ) zComment = m.zComment;
276 }
277 if( zTags==0 ){
278 zTags = db_text(0,
279 "SELECT group_concat(substr(tagname,5),', ')"
280 " FROM tagxref, tag"
281
--- src/event.c
+++ src/event.c
@@ -63,12 +63,11 @@
63 const char *zEventId; /* Event identifier */
64 char *zETime; /* Time of the event */
65 char *zATime; /* Time the artifact was created */
66 int specRid; /* rid specified by aid= parameter */
67 int prevRid, nextRid; /* Previous or next edits of this event */
68 Manifest *pEvent; /* Parsed event artifact */
 
69 Blob fullbody; /* Complete content of the event body */
70 Blob title; /* Title extracted from the event body */
71 Blob tail; /* Event body that comes after the title */
72 Stmt q1; /* Query to search for the event */
73 int showDetail; /* True to show details */
@@ -113,18 +112,15 @@
112 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
113 showDetail = atoi(PD("detail","0"));
114
115 /* Extract the event content.
116 */
117 pEvent = manifest_get(rid, CFTYPE_EVENT);
118 if( pEvent==0 ){
 
 
 
119 fossil_panic("Object #%d is not an event", rid);
120 }
121 blob_init(&fullbody, pEvent->zWiki, -1);
122 if( wiki_find_title(&fullbody, &title, &tail) ){
123 style_header(blob_str(&title));
124 }else{
125 style_header("Event %S", zEventId);
126 tail = fullbody;
@@ -131,11 +127,11 @@
127 }
128 if( g.okWrWiki && g.okWrite && nextRid==0 ){
129 style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
130 g.zTop, zEventId);
131 }
132 zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
133 style_submenu_element("Context", "Context", "%s/timeline?c=%T",
134 g.zTop, zETime);
135 if( g.okHistory ){
136 if( showDetail ){
137 style_submenu_element("Plain", "Plain", "%s/event?name=%s&amp;aid=%s",
@@ -166,37 +162,37 @@
162 if( showDetail && g.okHistory ){
163 int i;
164 const char *zClr = 0;
165 Blob comment;
166
167 zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate);
168 @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
169 @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>]
170 @ entered by user <b>%h(pEvent->zUser)</b> on
171 @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
172 @ <blockquote>
173 for(i=0; i<pEvent->nTag; i++){
174 if( strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){
175 zClr = pEvent->aTag[i].zValue;
176 }
177 }
178 if( zClr && zClr[0]==0 ) zClr = 0;
179 if( zClr ){
180 @ <div style="background-color: %h(zClr);">
181 }else{
182 @ <div>
183 }
184 blob_init(&comment, pEvent->zComment, -1);
185 wiki_convert(&comment, 0, WIKI_INLINE);
186 blob_reset(&comment);
187 @ </div>
188 @ </blockquote><hr />
189 }
190
191 wiki_convert(&tail, 0, 0);
192 style_footer();
193 manifest_destroy(pEvent);
194 }
195
196 /*
197 ** WEBPAGE: eventedit
198 ** URL: /eventedit?name=EVENTID
@@ -259,22 +255,18 @@
255
256 /* If editing an existing event, extract the key fields to use as
257 ** a starting point for the edit.
258 */
259 if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
260 Manifest *pEvent;
261 pEvent = manifest_get(rid, CFTYPE_EVENT);
262 if( pEvent && pEvent->type==CFTYPE_EVENT ){
263 if( zBody==0 ) zBody = pEvent->zWiki;
 
 
 
 
264 if( zETime==0 ){
265 zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
266 }
267 if( zComment==0 ) zComment = pEvent->zComment;
268 }
269 if( zTags==0 ){
270 zTags = db_text(0,
271 "SELECT group_concat(substr(tagname,5),', ')"
272 " FROM tagxref, tag"
273
+18 -26
--- src/event.c
+++ src/event.c
@@ -63,12 +63,11 @@
6363
const char *zEventId; /* Event identifier */
6464
char *zETime; /* Time of the event */
6565
char *zATime; /* Time the artifact was created */
6666
int specRid; /* rid specified by aid= parameter */
6767
int prevRid, nextRid; /* Previous or next edits of this event */
68
- Manifest m; /* Parsed event artifact */
69
- Blob content; /* Original event artifact content */
68
+ Manifest *pEvent; /* Parsed event artifact */
7069
Blob fullbody; /* Complete content of the event body */
7170
Blob title; /* Title extracted from the event body */
7271
Blob tail; /* Event body that comes after the title */
7372
Stmt q1; /* Query to search for the event */
7473
int showDetail; /* True to show details */
@@ -113,18 +112,15 @@
113112
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
114113
showDetail = atoi(PD("detail","0"));
115114
116115
/* Extract the event content.
117116
*/
118
- memset(&m, 0, sizeof(m));
119
- blob_zero(&m.content);
120
- content_get(rid, &content);
121
- manifest_parse(&m, &content);
122
- if( m.type!=CFTYPE_EVENT ){
117
+ pEvent = manifest_get(rid, CFTYPE_EVENT);
118
+ if( pEvent==0 ){
123119
fossil_panic("Object #%d is not an event", rid);
124120
}
125
- blob_init(&fullbody, m.zWiki, -1);
121
+ blob_init(&fullbody, pEvent->zWiki, -1);
126122
if( wiki_find_title(&fullbody, &title, &tail) ){
127123
style_header(blob_str(&title));
128124
}else{
129125
style_header("Event %S", zEventId);
130126
tail = fullbody;
@@ -131,11 +127,11 @@
131127
}
132128
if( g.okWrWiki && g.okWrite && nextRid==0 ){
133129
style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
134130
g.zTop, zEventId);
135131
}
136
- zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
132
+ zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
137133
style_submenu_element("Context", "Context", "%s/timeline?c=%T",
138134
g.zTop, zETime);
139135
if( g.okHistory ){
140136
if( showDetail ){
141137
style_submenu_element("Plain", "Plain", "%s/event?name=%s&amp;aid=%s",
@@ -166,37 +162,37 @@
166162
if( showDetail && g.okHistory ){
167163
int i;
168164
const char *zClr = 0;
169165
Blob comment;
170166
171
- zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate);
167
+ zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate);
172168
@ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
173169
@ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>]
174
- @ entered by user <b>%h(m.zUser)</b> on
170
+ @ entered by user <b>%h(pEvent->zUser)</b> on
175171
@ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
176172
@ <blockquote>
177
- for(i=0; i<m.nTag; i++){
178
- if( strcmp(m.aTag[i].zName,"+bgcolor")==0 ){
179
- zClr = m.aTag[i].zValue;
173
+ for(i=0; i<pEvent->nTag; i++){
174
+ if( strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){
175
+ zClr = pEvent->aTag[i].zValue;
180176
}
181177
}
182178
if( zClr && zClr[0]==0 ) zClr = 0;
183179
if( zClr ){
184180
@ <div style="background-color: %h(zClr);">
185181
}else{
186182
@ <div>
187183
}
188
- blob_init(&comment, m.zComment, -1);
184
+ blob_init(&comment, pEvent->zComment, -1);
189185
wiki_convert(&comment, 0, WIKI_INLINE);
190186
blob_reset(&comment);
191187
@ </div>
192188
@ </blockquote><hr />
193189
}
194190
195191
wiki_convert(&tail, 0, 0);
196192
style_footer();
197
- manifest_clear(&m);
193
+ manifest_destroy(pEvent);
198194
}
199195
200196
/*
201197
** WEBPAGE: eventedit
202198
** URL: /eventedit?name=EVENTID
@@ -259,22 +255,18 @@
259255
260256
/* If editing an existing event, extract the key fields to use as
261257
** a starting point for the edit.
262258
*/
263259
if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
264
- Manifest m;
265
- Blob content;
266
- memset(&m, 0, sizeof(m));
267
- blob_zero(&m.content);
268
- content_get(rid, &content);
269
- manifest_parse(&m, &content);
270
- if( m.type==CFTYPE_EVENT ){
271
- if( zBody==0 ) zBody = m.zWiki;
260
+ Manifest *pEvent;
261
+ pEvent = manifest_get(rid, CFTYPE_EVENT);
262
+ if( pEvent && pEvent->type==CFTYPE_EVENT ){
263
+ if( zBody==0 ) zBody = pEvent->zWiki;
272264
if( zETime==0 ){
273
- zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
265
+ zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
274266
}
275
- if( zComment==0 ) zComment = m.zComment;
267
+ if( zComment==0 ) zComment = pEvent->zComment;
276268
}
277269
if( zTags==0 ){
278270
zTags = db_text(0,
279271
"SELECT group_concat(substr(tagname,5),', ')"
280272
" FROM tagxref, tag"
281273
--- src/event.c
+++ src/event.c
@@ -63,12 +63,11 @@
63 const char *zEventId; /* Event identifier */
64 char *zETime; /* Time of the event */
65 char *zATime; /* Time the artifact was created */
66 int specRid; /* rid specified by aid= parameter */
67 int prevRid, nextRid; /* Previous or next edits of this event */
68 Manifest m; /* Parsed event artifact */
69 Blob content; /* Original event artifact content */
70 Blob fullbody; /* Complete content of the event body */
71 Blob title; /* Title extracted from the event body */
72 Blob tail; /* Event body that comes after the title */
73 Stmt q1; /* Query to search for the event */
74 int showDetail; /* True to show details */
@@ -113,18 +112,15 @@
113 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
114 showDetail = atoi(PD("detail","0"));
115
116 /* Extract the event content.
117 */
118 memset(&m, 0, sizeof(m));
119 blob_zero(&m.content);
120 content_get(rid, &content);
121 manifest_parse(&m, &content);
122 if( m.type!=CFTYPE_EVENT ){
123 fossil_panic("Object #%d is not an event", rid);
124 }
125 blob_init(&fullbody, m.zWiki, -1);
126 if( wiki_find_title(&fullbody, &title, &tail) ){
127 style_header(blob_str(&title));
128 }else{
129 style_header("Event %S", zEventId);
130 tail = fullbody;
@@ -131,11 +127,11 @@
131 }
132 if( g.okWrWiki && g.okWrite && nextRid==0 ){
133 style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
134 g.zTop, zEventId);
135 }
136 zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
137 style_submenu_element("Context", "Context", "%s/timeline?c=%T",
138 g.zTop, zETime);
139 if( g.okHistory ){
140 if( showDetail ){
141 style_submenu_element("Plain", "Plain", "%s/event?name=%s&amp;aid=%s",
@@ -166,37 +162,37 @@
166 if( showDetail && g.okHistory ){
167 int i;
168 const char *zClr = 0;
169 Blob comment;
170
171 zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate);
172 @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
173 @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>]
174 @ entered by user <b>%h(m.zUser)</b> on
175 @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
176 @ <blockquote>
177 for(i=0; i<m.nTag; i++){
178 if( strcmp(m.aTag[i].zName,"+bgcolor")==0 ){
179 zClr = m.aTag[i].zValue;
180 }
181 }
182 if( zClr && zClr[0]==0 ) zClr = 0;
183 if( zClr ){
184 @ <div style="background-color: %h(zClr);">
185 }else{
186 @ <div>
187 }
188 blob_init(&comment, m.zComment, -1);
189 wiki_convert(&comment, 0, WIKI_INLINE);
190 blob_reset(&comment);
191 @ </div>
192 @ </blockquote><hr />
193 }
194
195 wiki_convert(&tail, 0, 0);
196 style_footer();
197 manifest_clear(&m);
198 }
199
200 /*
201 ** WEBPAGE: eventedit
202 ** URL: /eventedit?name=EVENTID
@@ -259,22 +255,18 @@
259
260 /* If editing an existing event, extract the key fields to use as
261 ** a starting point for the edit.
262 */
263 if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
264 Manifest m;
265 Blob content;
266 memset(&m, 0, sizeof(m));
267 blob_zero(&m.content);
268 content_get(rid, &content);
269 manifest_parse(&m, &content);
270 if( m.type==CFTYPE_EVENT ){
271 if( zBody==0 ) zBody = m.zWiki;
272 if( zETime==0 ){
273 zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
274 }
275 if( zComment==0 ) zComment = m.zComment;
276 }
277 if( zTags==0 ){
278 zTags = db_text(0,
279 "SELECT group_concat(substr(tagname,5),', ')"
280 " FROM tagxref, tag"
281
--- src/event.c
+++ src/event.c
@@ -63,12 +63,11 @@
63 const char *zEventId; /* Event identifier */
64 char *zETime; /* Time of the event */
65 char *zATime; /* Time the artifact was created */
66 int specRid; /* rid specified by aid= parameter */
67 int prevRid, nextRid; /* Previous or next edits of this event */
68 Manifest *pEvent; /* Parsed event artifact */
 
69 Blob fullbody; /* Complete content of the event body */
70 Blob title; /* Title extracted from the event body */
71 Blob tail; /* Event body that comes after the title */
72 Stmt q1; /* Query to search for the event */
73 int showDetail; /* True to show details */
@@ -113,18 +112,15 @@
112 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
113 showDetail = atoi(PD("detail","0"));
114
115 /* Extract the event content.
116 */
117 pEvent = manifest_get(rid, CFTYPE_EVENT);
118 if( pEvent==0 ){
 
 
 
119 fossil_panic("Object #%d is not an event", rid);
120 }
121 blob_init(&fullbody, pEvent->zWiki, -1);
122 if( wiki_find_title(&fullbody, &title, &tail) ){
123 style_header(blob_str(&title));
124 }else{
125 style_header("Event %S", zEventId);
126 tail = fullbody;
@@ -131,11 +127,11 @@
127 }
128 if( g.okWrWiki && g.okWrite && nextRid==0 ){
129 style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
130 g.zTop, zEventId);
131 }
132 zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
133 style_submenu_element("Context", "Context", "%s/timeline?c=%T",
134 g.zTop, zETime);
135 if( g.okHistory ){
136 if( showDetail ){
137 style_submenu_element("Plain", "Plain", "%s/event?name=%s&amp;aid=%s",
@@ -166,37 +162,37 @@
162 if( showDetail && g.okHistory ){
163 int i;
164 const char *zClr = 0;
165 Blob comment;
166
167 zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate);
168 @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
169 @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>]
170 @ entered by user <b>%h(pEvent->zUser)</b> on
171 @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
172 @ <blockquote>
173 for(i=0; i<pEvent->nTag; i++){
174 if( strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){
175 zClr = pEvent->aTag[i].zValue;
176 }
177 }
178 if( zClr && zClr[0]==0 ) zClr = 0;
179 if( zClr ){
180 @ <div style="background-color: %h(zClr);">
181 }else{
182 @ <div>
183 }
184 blob_init(&comment, pEvent->zComment, -1);
185 wiki_convert(&comment, 0, WIKI_INLINE);
186 blob_reset(&comment);
187 @ </div>
188 @ </blockquote><hr />
189 }
190
191 wiki_convert(&tail, 0, 0);
192 style_footer();
193 manifest_destroy(pEvent);
194 }
195
196 /*
197 ** WEBPAGE: eventedit
198 ** URL: /eventedit?name=EVENTID
@@ -259,22 +255,18 @@
255
256 /* If editing an existing event, extract the key fields to use as
257 ** a starting point for the edit.
258 */
259 if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
260 Manifest *pEvent;
261 pEvent = manifest_get(rid, CFTYPE_EVENT);
262 if( pEvent && pEvent->type==CFTYPE_EVENT ){
263 if( zBody==0 ) zBody = pEvent->zWiki;
 
 
 
 
264 if( zETime==0 ){
265 zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
266 }
267 if( zComment==0 ) zComment = pEvent->zComment;
268 }
269 if( zTags==0 ){
270 zTags = db_text(0,
271 "SELECT group_concat(substr(tagname,5),', ')"
272 " FROM tagxref, tag"
273
+1 -1
--- src/finfo.c
+++ src/finfo.c
@@ -134,11 +134,11 @@
134134
TAG_BRANCH,
135135
zFilename
136136
);
137137
blob_zero(&title);
138138
blob_appendf(&title, "History of ");
139
- hyperlinked_path(zFilename, &title);
139
+ hyperlinked_path(zFilename, &title, 0);
140140
@ <h2>%b(&title)</h2>
141141
blob_reset(&title);
142142
pGraph = graph_init();
143143
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
144144
@ <table class="timelineTable">
145145
--- src/finfo.c
+++ src/finfo.c
@@ -134,11 +134,11 @@
134 TAG_BRANCH,
135 zFilename
136 );
137 blob_zero(&title);
138 blob_appendf(&title, "History of ");
139 hyperlinked_path(zFilename, &title);
140 @ <h2>%b(&title)</h2>
141 blob_reset(&title);
142 pGraph = graph_init();
143 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
144 @ <table class="timelineTable">
145
--- src/finfo.c
+++ src/finfo.c
@@ -134,11 +134,11 @@
134 TAG_BRANCH,
135 zFilename
136 );
137 blob_zero(&title);
138 blob_appendf(&title, "History of ");
139 hyperlinked_path(zFilename, &title, 0);
140 @ <h2>%b(&title)</h2>
141 blob_reset(&title);
142 pGraph = graph_init();
143 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
144 @ <table class="timelineTable">
145
+1 -1
--- src/finfo.c
+++ src/finfo.c
@@ -134,11 +134,11 @@
134134
TAG_BRANCH,
135135
zFilename
136136
);
137137
blob_zero(&title);
138138
blob_appendf(&title, "History of ");
139
- hyperlinked_path(zFilename, &title);
139
+ hyperlinked_path(zFilename, &title, 0);
140140
@ <h2>%b(&title)</h2>
141141
blob_reset(&title);
142142
pGraph = graph_init();
143143
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
144144
@ <table class="timelineTable">
145145
--- src/finfo.c
+++ src/finfo.c
@@ -134,11 +134,11 @@
134 TAG_BRANCH,
135 zFilename
136 );
137 blob_zero(&title);
138 blob_appendf(&title, "History of ");
139 hyperlinked_path(zFilename, &title);
140 @ <h2>%b(&title)</h2>
141 blob_reset(&title);
142 pGraph = graph_init();
143 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
144 @ <table class="timelineTable">
145
--- src/finfo.c
+++ src/finfo.c
@@ -134,11 +134,11 @@
134 TAG_BRANCH,
135 zFilename
136 );
137 blob_zero(&title);
138 blob_appendf(&title, "History of ");
139 hyperlinked_path(zFilename, &title, 0);
140 @ <h2>%b(&title)</h2>
141 blob_reset(&title);
142 pGraph = graph_init();
143 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
144 @ <table class="timelineTable">
145
+63 -68
--- src/info.c
+++ src/info.c
@@ -545,24 +545,20 @@
545545
rid = 0;
546546
}
547547
db_finalize(&q);
548548
showTags(rid, "wiki-*");
549549
if( rid ){
550
- Blob content;
551
- Manifest m;
552
- memset(&m, 0, sizeof(m));
553
- blob_zero(&m.content);
554
- content_get(rid, &content);
555
- manifest_parse(&m, &content);
556
- if( m.type==CFTYPE_WIKI ){
550
+ Manifest *pWiki;
551
+ pWiki = manifest_get(rid, CFTYPE_WIKI);
552
+ if( pWiki ){
557553
Blob wiki;
558
- blob_init(&wiki, m.zWiki, -1);
554
+ blob_init(&wiki, pWiki->zWiki, -1);
559555
@ <div class="section">Content</div>
560556
wiki_convert(&wiki, 0, 0);
561557
blob_reset(&wiki);
562558
}
563
- manifest_clear(&m);
559
+ manifest_destroy(pWiki);
564560
}
565561
style_footer_cmdref("info",0);
566562
}
567563
568564
/*
@@ -582,26 +578,23 @@
582578
583579
/*
584580
** Find an checkin based on query parameter zParam and parse its
585581
** manifest. Return the number of errors.
586582
*/
587
-static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){
583
+static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
588584
int rid;
589
- Blob content;
590585
591586
*pRid = rid = name_to_rid_www(zParam);
592587
if( rid==0 ){
593588
webpage_error("Missing \"%s\" query parameter.", zParam);
594
- return 1;
589
+ return 0;
595590
}
596591
if( !is_a_version(rid) ){
597592
webpage_error("Artifact %s is not a checkin.", P(zParam));
598
- return 1;
593
+ return 0;
599594
}
600
- content_get(rid, &content);
601
- manifest_parse(pM, &content);
602
- return 0;
595
+ return manifest_get(rid, CFTYPE_MANIFEST);
603596
}
604597
605598
/*
606599
** Output a description of a check-in
607600
*/
@@ -639,12 +632,12 @@
639632
void vdiff_page(void){
640633
int ridFrom, ridTo;
641634
int showDetail = atoi(PD("detail","0"));
642635
const char *zFrom = P("from");
643636
const char *zTo = P("to");
644
- int iFrom, iTo;
645
- Manifest mFrom, mTo;
637
+ Manifest *pFrom, *pTo;
638
+ ManifestFile *pFileFrom, *pFileTo;
646639
647640
login_check_credentials();
648641
if( !g.okRead ){ login_needed(); return; }
649642
login_anonymous_available();
650643
@@ -665,55 +658,59 @@
665658
@ checked="checked" value="1" /></td></tr>
666659
@ <tr><td></td><td></td><td>
667660
@ <input type="submit" name="diff" value="diff" /></td></tr></table>
668661
@ </div></form>
669662
style_footer_cmdref("diff",0);
670
- return;
671
- }else if( vdiff_parse_manifest("from", &ridFrom, &mFrom)
672
- || vdiff_parse_manifest("to", &ridTo, &mTo)
673
- ){
674663
return;
675664
}
665
+ pFrom = vdiff_parse_manifest("from", &ridFrom);
666
+ if( pFrom==0 ) return;
667
+ pTo = vdiff_parse_manifest("to", &ridTo);
668
+ if( pTo==0 ) return;
669
+ showDetail = atoi(PD("detail","0"));
676670
style_header("Check-in Differences");
677671
@ <h2>Difference From:</h2><blockquote>
678672
checkin_description(ridFrom);
679673
@ </blockquote><h2>To:</h2><blockquote>
680674
checkin_description(ridTo);
681675
@ </blockquote><hr /><p>
682676
683
- iFrom = iTo = 0;
684
- while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
677
+ manifest_file_rewind(pFrom);
678
+ pFileFrom = manifest_file_next(pFrom, 0);
679
+ manifest_file_rewind(pTo);
680
+ pFileTo = manifest_file_next(pTo, 0);
681
+ while( pFileFrom || pFileTo ){
685682
int cmp;
686
- if( iFrom>=mFrom.nFile ){
683
+ if( pFileFrom==0 ){
687684
cmp = +1;
688
- }else if( iTo>=mTo.nFile ){
685
+ }else if( pFileTo==0 ){
689686
cmp = -1;
690687
}else{
691
- cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
688
+ cmp = strcmp(pFileFrom->zName, pFileTo->zName);
692689
}
693690
if( cmp<0 ){
694
- append_file_change_line(mFrom.aFile[iFrom].zName,
695
- mFrom.aFile[iFrom].zUuid, 0, 0);
696
- iFrom++;
691
+ append_file_change_line(pFileFrom->zName,
692
+ pFileFrom->zUuid, 0, 0);
693
+ pFileFrom = manifest_file_next(pFrom, 0);
697694
}else if( cmp>0 ){
698
- append_file_change_line(mTo.aFile[iTo].zName,
699
- 0, mTo.aFile[iTo].zUuid, 0);
700
- iTo++;
701
- }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
695
+ append_file_change_line(pFileTo->zName,
696
+ 0, pFileTo->zUuid, 0);
697
+ pFileTo = manifest_file_next(pTo, 0);
698
+ }else if( strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
702699
/* No changes */
703
- iFrom++;
704
- iTo++;
700
+ pFileFrom = manifest_file_next(pFrom, 0);
701
+ pFileTo = manifest_file_next(pTo, 0);
705702
}else{
706
- append_file_change_line(mFrom.aFile[iFrom].zName,
707
- mFrom.aFile[iFrom].zUuid,
708
- mTo.aFile[iTo].zUuid, showDetail);
709
- iFrom++;
710
- iTo++;
703
+ append_file_change_line(pFileFrom->zName,
704
+ pFileFrom->zUuid,
705
+ pFileTo->zUuid, showDetail);
706
+ pFileFrom = manifest_file_next(pFrom, 0);
707
+ pFileTo = manifest_file_next(pTo, 0);
711708
}
712709
}
713
- manifest_clear(&mFrom);
714
- manifest_clear(&mTo);
710
+ manifest_destroy(pFrom);
711
+ manifest_destroy(pTo);
715712
716713
style_footer_cmdref("diff",0);
717714
}
718715
719716
/*
@@ -1069,25 +1066,26 @@
10691066
*/
10701067
int artifact_from_ci_and_filename(void){
10711068
const char *zFilename;
10721069
const char *zCI;
10731070
int cirid;
1074
- Blob content;
1075
- Manifest m;
1076
- int i;
1071
+ Manifest *pManifest;
1072
+ ManifestFile *pFile;
10771073
10781074
zCI = P("ci");
10791075
if( zCI==0 ) return 0;
10801076
zFilename = P("filename");
10811077
if( zFilename==0 ) return 0;
10821078
cirid = name_to_rid_www("ci");
1083
- if( !content_get(cirid, &content) ) return 0;
1084
- if( !manifest_parse(&m, &content) ) return 0;
1085
- if( m.type!=CFTYPE_MANIFEST ) return 0;
1086
- for(i=0; i<m.nFile; i++){
1087
- if( strcmp(zFilename, m.aFile[i].zName)==0 ){
1088
- return db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", m.aFile[i].zUuid);
1079
+ pManifest = manifest_get(cirid, CFTYPE_MANIFEST);
1080
+ if( pManifest==0 ) return 0;
1081
+ manifest_file_rewind(pManifest);
1082
+ while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1083
+ if( strcmp(zFilename, pFile->zName)==0 ){
1084
+ int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1085
+ manifest_destroy(pManifest);
1086
+ return rid;
10891087
}
10901088
}
10911089
return 0;
10921090
}
10931091
@@ -1194,15 +1192,14 @@
11941192
**
11951193
** Show the details of a ticket change control artifact.
11961194
*/
11971195
void tinfo_page(void){
11981196
int rid;
1199
- Blob content;
12001197
char *zDate;
12011198
const char *zUuid;
12021199
char zTktName[20];
1203
- Manifest m;
1200
+ Manifest *pTktChng;
12041201
12051202
login_check_credentials();
12061203
if( !g.okRdTkt ){ login_needed(); return; }
12071204
rid = name_to_rid_www("name");
12081205
if( rid==0 ){ fossil_redirect_home(); }
@@ -1214,39 +1211,37 @@
12141211
}else{
12151212
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
12161213
g.zTop, zUuid);
12171214
}
12181215
}
1219
- content_get(rid, &content);
1220
- if( manifest_parse(&m, &content)==0 ){
1221
- fossil_redirect_home();
1222
- }
1223
- if( m.type!=CFTYPE_TICKET ){
1216
+ pTktChng = manifest_get(rid, CFTYPE_TICKET);
1217
+ if( pTktChng==0 ){
12241218
fossil_redirect_home();
12251219
}
12261220
style_header("Ticket Change Details");
1227
- zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
1228
- memcpy(zTktName, m.zTicketUuid, 10);
1221
+ zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
1222
+ memcpy(zTktName, pTktChng->zTicketUuid, 10);
12291223
zTktName[10] = 0;
12301224
if( g.okHistory ){
1231
- @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2>
1225
+ @ <h2>Changes to ticket
1226
+ @ <a href="%s(pTktChng->zTicketUuid)">%s(zTktName)</a></h2>
12321227
@
1233
- @ <p>By %h(m.zUser) on %s(zDate). See also:
1228
+ @ <p>By %h(pTktChng->zUser) on %s(zDate). See also:
12341229
@ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
1235
- @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a>
1236
- @ </p>
1230
+ @ <a href="%s(g.zTop)/tkthistory/%s(pTktChng->zTicketUuid)">ticket
1231
+ @ history</a></p>
12371232
}else{
12381233
@ <h2>Changes to ticket %s(zTktName)</h2>
12391234
@
1240
- @ <p>By %h(m.zUser) on %s(zDate).
1235
+ @ <p>By %h(pTktChng->zUser) on %s(zDate).
12411236
@ </p>
12421237
}
12431238
@
12441239
@ <ol>
12451240
free(zDate);
1246
- ticket_output_change_artifact(&m);
1247
- manifest_clear(&m);
1241
+ ticket_output_change_artifact(pTktChng);
1242
+ manifest_destroy(pTktChng);
12481243
style_footer_cmdref("info",0);
12491244
}
12501245
12511246
12521247
/*
12531248
--- src/info.c
+++ src/info.c
@@ -545,24 +545,20 @@
545 rid = 0;
546 }
547 db_finalize(&q);
548 showTags(rid, "wiki-*");
549 if( rid ){
550 Blob content;
551 Manifest m;
552 memset(&m, 0, sizeof(m));
553 blob_zero(&m.content);
554 content_get(rid, &content);
555 manifest_parse(&m, &content);
556 if( m.type==CFTYPE_WIKI ){
557 Blob wiki;
558 blob_init(&wiki, m.zWiki, -1);
559 @ <div class="section">Content</div>
560 wiki_convert(&wiki, 0, 0);
561 blob_reset(&wiki);
562 }
563 manifest_clear(&m);
564 }
565 style_footer_cmdref("info",0);
566 }
567
568 /*
@@ -582,26 +578,23 @@
582
583 /*
584 ** Find an checkin based on query parameter zParam and parse its
585 ** manifest. Return the number of errors.
586 */
587 static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){
588 int rid;
589 Blob content;
590
591 *pRid = rid = name_to_rid_www(zParam);
592 if( rid==0 ){
593 webpage_error("Missing \"%s\" query parameter.", zParam);
594 return 1;
595 }
596 if( !is_a_version(rid) ){
597 webpage_error("Artifact %s is not a checkin.", P(zParam));
598 return 1;
599 }
600 content_get(rid, &content);
601 manifest_parse(pM, &content);
602 return 0;
603 }
604
605 /*
606 ** Output a description of a check-in
607 */
@@ -639,12 +632,12 @@
639 void vdiff_page(void){
640 int ridFrom, ridTo;
641 int showDetail = atoi(PD("detail","0"));
642 const char *zFrom = P("from");
643 const char *zTo = P("to");
644 int iFrom, iTo;
645 Manifest mFrom, mTo;
646
647 login_check_credentials();
648 if( !g.okRead ){ login_needed(); return; }
649 login_anonymous_available();
650
@@ -665,55 +658,59 @@
665 @ checked="checked" value="1" /></td></tr>
666 @ <tr><td></td><td></td><td>
667 @ <input type="submit" name="diff" value="diff" /></td></tr></table>
668 @ </div></form>
669 style_footer_cmdref("diff",0);
670 return;
671 }else if( vdiff_parse_manifest("from", &ridFrom, &mFrom)
672 || vdiff_parse_manifest("to", &ridTo, &mTo)
673 ){
674 return;
675 }
 
 
 
 
 
676 style_header("Check-in Differences");
677 @ <h2>Difference From:</h2><blockquote>
678 checkin_description(ridFrom);
679 @ </blockquote><h2>To:</h2><blockquote>
680 checkin_description(ridTo);
681 @ </blockquote><hr /><p>
682
683 iFrom = iTo = 0;
684 while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
 
 
 
685 int cmp;
686 if( iFrom>=mFrom.nFile ){
687 cmp = +1;
688 }else if( iTo>=mTo.nFile ){
689 cmp = -1;
690 }else{
691 cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
692 }
693 if( cmp<0 ){
694 append_file_change_line(mFrom.aFile[iFrom].zName,
695 mFrom.aFile[iFrom].zUuid, 0, 0);
696 iFrom++;
697 }else if( cmp>0 ){
698 append_file_change_line(mTo.aFile[iTo].zName,
699 0, mTo.aFile[iTo].zUuid, 0);
700 iTo++;
701 }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
702 /* No changes */
703 iFrom++;
704 iTo++;
705 }else{
706 append_file_change_line(mFrom.aFile[iFrom].zName,
707 mFrom.aFile[iFrom].zUuid,
708 mTo.aFile[iTo].zUuid, showDetail);
709 iFrom++;
710 iTo++;
711 }
712 }
713 manifest_clear(&mFrom);
714 manifest_clear(&mTo);
715
716 style_footer_cmdref("diff",0);
717 }
718
719 /*
@@ -1069,25 +1066,26 @@
1069 */
1070 int artifact_from_ci_and_filename(void){
1071 const char *zFilename;
1072 const char *zCI;
1073 int cirid;
1074 Blob content;
1075 Manifest m;
1076 int i;
1077
1078 zCI = P("ci");
1079 if( zCI==0 ) return 0;
1080 zFilename = P("filename");
1081 if( zFilename==0 ) return 0;
1082 cirid = name_to_rid_www("ci");
1083 if( !content_get(cirid, &content) ) return 0;
1084 if( !manifest_parse(&m, &content) ) return 0;
1085 if( m.type!=CFTYPE_MANIFEST ) return 0;
1086 for(i=0; i<m.nFile; i++){
1087 if( strcmp(zFilename, m.aFile[i].zName)==0 ){
1088 return db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", m.aFile[i].zUuid);
 
 
1089 }
1090 }
1091 return 0;
1092 }
1093
@@ -1194,15 +1192,14 @@
1194 **
1195 ** Show the details of a ticket change control artifact.
1196 */
1197 void tinfo_page(void){
1198 int rid;
1199 Blob content;
1200 char *zDate;
1201 const char *zUuid;
1202 char zTktName[20];
1203 Manifest m;
1204
1205 login_check_credentials();
1206 if( !g.okRdTkt ){ login_needed(); return; }
1207 rid = name_to_rid_www("name");
1208 if( rid==0 ){ fossil_redirect_home(); }
@@ -1214,39 +1211,37 @@
1214 }else{
1215 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1216 g.zTop, zUuid);
1217 }
1218 }
1219 content_get(rid, &content);
1220 if( manifest_parse(&m, &content)==0 ){
1221 fossil_redirect_home();
1222 }
1223 if( m.type!=CFTYPE_TICKET ){
1224 fossil_redirect_home();
1225 }
1226 style_header("Ticket Change Details");
1227 zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
1228 memcpy(zTktName, m.zTicketUuid, 10);
1229 zTktName[10] = 0;
1230 if( g.okHistory ){
1231 @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2>
 
1232 @
1233 @ <p>By %h(m.zUser) on %s(zDate). See also:
1234 @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
1235 @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a>
1236 @ </p>
1237 }else{
1238 @ <h2>Changes to ticket %s(zTktName)</h2>
1239 @
1240 @ <p>By %h(m.zUser) on %s(zDate).
1241 @ </p>
1242 }
1243 @
1244 @ <ol>
1245 free(zDate);
1246 ticket_output_change_artifact(&m);
1247 manifest_clear(&m);
1248 style_footer_cmdref("info",0);
1249 }
1250
1251
1252 /*
1253
--- src/info.c
+++ src/info.c
@@ -545,24 +545,20 @@
545 rid = 0;
546 }
547 db_finalize(&q);
548 showTags(rid, "wiki-*");
549 if( rid ){
550 Manifest *pWiki;
551 pWiki = manifest_get(rid, CFTYPE_WIKI);
552 if( pWiki ){
 
 
 
 
553 Blob wiki;
554 blob_init(&wiki, pWiki->zWiki, -1);
555 @ <div class="section">Content</div>
556 wiki_convert(&wiki, 0, 0);
557 blob_reset(&wiki);
558 }
559 manifest_destroy(pWiki);
560 }
561 style_footer_cmdref("info",0);
562 }
563
564 /*
@@ -582,26 +578,23 @@
578
579 /*
580 ** Find an checkin based on query parameter zParam and parse its
581 ** manifest. Return the number of errors.
582 */
583 static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
584 int rid;
 
585
586 *pRid = rid = name_to_rid_www(zParam);
587 if( rid==0 ){
588 webpage_error("Missing \"%s\" query parameter.", zParam);
589 return 0;
590 }
591 if( !is_a_version(rid) ){
592 webpage_error("Artifact %s is not a checkin.", P(zParam));
593 return 0;
594 }
595 return manifest_get(rid, CFTYPE_MANIFEST);
 
 
596 }
597
598 /*
599 ** Output a description of a check-in
600 */
@@ -639,12 +632,12 @@
632 void vdiff_page(void){
633 int ridFrom, ridTo;
634 int showDetail = atoi(PD("detail","0"));
635 const char *zFrom = P("from");
636 const char *zTo = P("to");
637 Manifest *pFrom, *pTo;
638 ManifestFile *pFileFrom, *pFileTo;
639
640 login_check_credentials();
641 if( !g.okRead ){ login_needed(); return; }
642 login_anonymous_available();
643
@@ -665,55 +658,59 @@
658 @ checked="checked" value="1" /></td></tr>
659 @ <tr><td></td><td></td><td>
660 @ <input type="submit" name="diff" value="diff" /></td></tr></table>
661 @ </div></form>
662 style_footer_cmdref("diff",0);
 
 
 
 
663 return;
664 }
665 pFrom = vdiff_parse_manifest("from", &ridFrom);
666 if( pFrom==0 ) return;
667 pTo = vdiff_parse_manifest("to", &ridTo);
668 if( pTo==0 ) return;
669 showDetail = atoi(PD("detail","0"));
670 style_header("Check-in Differences");
671 @ <h2>Difference From:</h2><blockquote>
672 checkin_description(ridFrom);
673 @ </blockquote><h2>To:</h2><blockquote>
674 checkin_description(ridTo);
675 @ </blockquote><hr /><p>
676
677 manifest_file_rewind(pFrom);
678 pFileFrom = manifest_file_next(pFrom, 0);
679 manifest_file_rewind(pTo);
680 pFileTo = manifest_file_next(pTo, 0);
681 while( pFileFrom || pFileTo ){
682 int cmp;
683 if( pFileFrom==0 ){
684 cmp = +1;
685 }else if( pFileTo==0 ){
686 cmp = -1;
687 }else{
688 cmp = strcmp(pFileFrom->zName, pFileTo->zName);
689 }
690 if( cmp<0 ){
691 append_file_change_line(pFileFrom->zName,
692 pFileFrom->zUuid, 0, 0);
693 pFileFrom = manifest_file_next(pFrom, 0);
694 }else if( cmp>0 ){
695 append_file_change_line(pFileTo->zName,
696 0, pFileTo->zUuid, 0);
697 pFileTo = manifest_file_next(pTo, 0);
698 }else if( strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
699 /* No changes */
700 pFileFrom = manifest_file_next(pFrom, 0);
701 pFileTo = manifest_file_next(pTo, 0);
702 }else{
703 append_file_change_line(pFileFrom->zName,
704 pFileFrom->zUuid,
705 pFileTo->zUuid, showDetail);
706 pFileFrom = manifest_file_next(pFrom, 0);
707 pFileTo = manifest_file_next(pTo, 0);
708 }
709 }
710 manifest_destroy(pFrom);
711 manifest_destroy(pTo);
712
713 style_footer_cmdref("diff",0);
714 }
715
716 /*
@@ -1069,25 +1066,26 @@
1066 */
1067 int artifact_from_ci_and_filename(void){
1068 const char *zFilename;
1069 const char *zCI;
1070 int cirid;
1071 Manifest *pManifest;
1072 ManifestFile *pFile;
 
1073
1074 zCI = P("ci");
1075 if( zCI==0 ) return 0;
1076 zFilename = P("filename");
1077 if( zFilename==0 ) return 0;
1078 cirid = name_to_rid_www("ci");
1079 pManifest = manifest_get(cirid, CFTYPE_MANIFEST);
1080 if( pManifest==0 ) return 0;
1081 manifest_file_rewind(pManifest);
1082 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1083 if( strcmp(zFilename, pFile->zName)==0 ){
1084 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1085 manifest_destroy(pManifest);
1086 return rid;
1087 }
1088 }
1089 return 0;
1090 }
1091
@@ -1194,15 +1192,14 @@
1192 **
1193 ** Show the details of a ticket change control artifact.
1194 */
1195 void tinfo_page(void){
1196 int rid;
 
1197 char *zDate;
1198 const char *zUuid;
1199 char zTktName[20];
1200 Manifest *pTktChng;
1201
1202 login_check_credentials();
1203 if( !g.okRdTkt ){ login_needed(); return; }
1204 rid = name_to_rid_www("name");
1205 if( rid==0 ){ fossil_redirect_home(); }
@@ -1214,39 +1211,37 @@
1211 }else{
1212 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1213 g.zTop, zUuid);
1214 }
1215 }
1216 pTktChng = manifest_get(rid, CFTYPE_TICKET);
1217 if( pTktChng==0 ){
 
 
 
1218 fossil_redirect_home();
1219 }
1220 style_header("Ticket Change Details");
1221 zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
1222 memcpy(zTktName, pTktChng->zTicketUuid, 10);
1223 zTktName[10] = 0;
1224 if( g.okHistory ){
1225 @ <h2>Changes to ticket
1226 @ <a href="%s(pTktChng->zTicketUuid)">%s(zTktName)</a></h2>
1227 @
1228 @ <p>By %h(pTktChng->zUser) on %s(zDate). See also:
1229 @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
1230 @ <a href="%s(g.zTop)/tkthistory/%s(pTktChng->zTicketUuid)">ticket
1231 @ history</a></p>
1232 }else{
1233 @ <h2>Changes to ticket %s(zTktName)</h2>
1234 @
1235 @ <p>By %h(pTktChng->zUser) on %s(zDate).
1236 @ </p>
1237 }
1238 @
1239 @ <ol>
1240 free(zDate);
1241 ticket_output_change_artifact(pTktChng);
1242 manifest_destroy(pTktChng);
1243 style_footer_cmdref("info",0);
1244 }
1245
1246
1247 /*
1248
+63 -68
--- src/info.c
+++ src/info.c
@@ -545,24 +545,20 @@
545545
rid = 0;
546546
}
547547
db_finalize(&q);
548548
showTags(rid, "wiki-*");
549549
if( rid ){
550
- Blob content;
551
- Manifest m;
552
- memset(&m, 0, sizeof(m));
553
- blob_zero(&m.content);
554
- content_get(rid, &content);
555
- manifest_parse(&m, &content);
556
- if( m.type==CFTYPE_WIKI ){
550
+ Manifest *pWiki;
551
+ pWiki = manifest_get(rid, CFTYPE_WIKI);
552
+ if( pWiki ){
557553
Blob wiki;
558
- blob_init(&wiki, m.zWiki, -1);
554
+ blob_init(&wiki, pWiki->zWiki, -1);
559555
@ <div class="section">Content</div>
560556
wiki_convert(&wiki, 0, 0);
561557
blob_reset(&wiki);
562558
}
563
- manifest_clear(&m);
559
+ manifest_destroy(pWiki);
564560
}
565561
style_footer_cmdref("info",0);
566562
}
567563
568564
/*
@@ -582,26 +578,23 @@
582578
583579
/*
584580
** Find an checkin based on query parameter zParam and parse its
585581
** manifest. Return the number of errors.
586582
*/
587
-static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){
583
+static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
588584
int rid;
589
- Blob content;
590585
591586
*pRid = rid = name_to_rid_www(zParam);
592587
if( rid==0 ){
593588
webpage_error("Missing \"%s\" query parameter.", zParam);
594
- return 1;
589
+ return 0;
595590
}
596591
if( !is_a_version(rid) ){
597592
webpage_error("Artifact %s is not a checkin.", P(zParam));
598
- return 1;
593
+ return 0;
599594
}
600
- content_get(rid, &content);
601
- manifest_parse(pM, &content);
602
- return 0;
595
+ return manifest_get(rid, CFTYPE_MANIFEST);
603596
}
604597
605598
/*
606599
** Output a description of a check-in
607600
*/
@@ -639,12 +632,12 @@
639632
void vdiff_page(void){
640633
int ridFrom, ridTo;
641634
int showDetail = atoi(PD("detail","0"));
642635
const char *zFrom = P("from");
643636
const char *zTo = P("to");
644
- int iFrom, iTo;
645
- Manifest mFrom, mTo;
637
+ Manifest *pFrom, *pTo;
638
+ ManifestFile *pFileFrom, *pFileTo;
646639
647640
login_check_credentials();
648641
if( !g.okRead ){ login_needed(); return; }
649642
login_anonymous_available();
650643
@@ -665,55 +658,59 @@
665658
@ checked="checked" value="1" /></td></tr>
666659
@ <tr><td></td><td></td><td>
667660
@ <input type="submit" name="diff" value="diff" /></td></tr></table>
668661
@ </div></form>
669662
style_footer_cmdref("diff",0);
670
- return;
671
- }else if( vdiff_parse_manifest("from", &ridFrom, &mFrom)
672
- || vdiff_parse_manifest("to", &ridTo, &mTo)
673
- ){
674663
return;
675664
}
665
+ pFrom = vdiff_parse_manifest("from", &ridFrom);
666
+ if( pFrom==0 ) return;
667
+ pTo = vdiff_parse_manifest("to", &ridTo);
668
+ if( pTo==0 ) return;
669
+ showDetail = atoi(PD("detail","0"));
676670
style_header("Check-in Differences");
677671
@ <h2>Difference From:</h2><blockquote>
678672
checkin_description(ridFrom);
679673
@ </blockquote><h2>To:</h2><blockquote>
680674
checkin_description(ridTo);
681675
@ </blockquote><hr /><p>
682676
683
- iFrom = iTo = 0;
684
- while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
677
+ manifest_file_rewind(pFrom);
678
+ pFileFrom = manifest_file_next(pFrom, 0);
679
+ manifest_file_rewind(pTo);
680
+ pFileTo = manifest_file_next(pTo, 0);
681
+ while( pFileFrom || pFileTo ){
685682
int cmp;
686
- if( iFrom>=mFrom.nFile ){
683
+ if( pFileFrom==0 ){
687684
cmp = +1;
688
- }else if( iTo>=mTo.nFile ){
685
+ }else if( pFileTo==0 ){
689686
cmp = -1;
690687
}else{
691
- cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
688
+ cmp = strcmp(pFileFrom->zName, pFileTo->zName);
692689
}
693690
if( cmp<0 ){
694
- append_file_change_line(mFrom.aFile[iFrom].zName,
695
- mFrom.aFile[iFrom].zUuid, 0, 0);
696
- iFrom++;
691
+ append_file_change_line(pFileFrom->zName,
692
+ pFileFrom->zUuid, 0, 0);
693
+ pFileFrom = manifest_file_next(pFrom, 0);
697694
}else if( cmp>0 ){
698
- append_file_change_line(mTo.aFile[iTo].zName,
699
- 0, mTo.aFile[iTo].zUuid, 0);
700
- iTo++;
701
- }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
695
+ append_file_change_line(pFileTo->zName,
696
+ 0, pFileTo->zUuid, 0);
697
+ pFileTo = manifest_file_next(pTo, 0);
698
+ }else if( strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
702699
/* No changes */
703
- iFrom++;
704
- iTo++;
700
+ pFileFrom = manifest_file_next(pFrom, 0);
701
+ pFileTo = manifest_file_next(pTo, 0);
705702
}else{
706
- append_file_change_line(mFrom.aFile[iFrom].zName,
707
- mFrom.aFile[iFrom].zUuid,
708
- mTo.aFile[iTo].zUuid, showDetail);
709
- iFrom++;
710
- iTo++;
703
+ append_file_change_line(pFileFrom->zName,
704
+ pFileFrom->zUuid,
705
+ pFileTo->zUuid, showDetail);
706
+ pFileFrom = manifest_file_next(pFrom, 0);
707
+ pFileTo = manifest_file_next(pTo, 0);
711708
}
712709
}
713
- manifest_clear(&mFrom);
714
- manifest_clear(&mTo);
710
+ manifest_destroy(pFrom);
711
+ manifest_destroy(pTo);
715712
716713
style_footer_cmdref("diff",0);
717714
}
718715
719716
/*
@@ -1069,25 +1066,26 @@
10691066
*/
10701067
int artifact_from_ci_and_filename(void){
10711068
const char *zFilename;
10721069
const char *zCI;
10731070
int cirid;
1074
- Blob content;
1075
- Manifest m;
1076
- int i;
1071
+ Manifest *pManifest;
1072
+ ManifestFile *pFile;
10771073
10781074
zCI = P("ci");
10791075
if( zCI==0 ) return 0;
10801076
zFilename = P("filename");
10811077
if( zFilename==0 ) return 0;
10821078
cirid = name_to_rid_www("ci");
1083
- if( !content_get(cirid, &content) ) return 0;
1084
- if( !manifest_parse(&m, &content) ) return 0;
1085
- if( m.type!=CFTYPE_MANIFEST ) return 0;
1086
- for(i=0; i<m.nFile; i++){
1087
- if( strcmp(zFilename, m.aFile[i].zName)==0 ){
1088
- return db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", m.aFile[i].zUuid);
1079
+ pManifest = manifest_get(cirid, CFTYPE_MANIFEST);
1080
+ if( pManifest==0 ) return 0;
1081
+ manifest_file_rewind(pManifest);
1082
+ while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1083
+ if( strcmp(zFilename, pFile->zName)==0 ){
1084
+ int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1085
+ manifest_destroy(pManifest);
1086
+ return rid;
10891087
}
10901088
}
10911089
return 0;
10921090
}
10931091
@@ -1194,15 +1192,14 @@
11941192
**
11951193
** Show the details of a ticket change control artifact.
11961194
*/
11971195
void tinfo_page(void){
11981196
int rid;
1199
- Blob content;
12001197
char *zDate;
12011198
const char *zUuid;
12021199
char zTktName[20];
1203
- Manifest m;
1200
+ Manifest *pTktChng;
12041201
12051202
login_check_credentials();
12061203
if( !g.okRdTkt ){ login_needed(); return; }
12071204
rid = name_to_rid_www("name");
12081205
if( rid==0 ){ fossil_redirect_home(); }
@@ -1214,39 +1211,37 @@
12141211
}else{
12151212
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
12161213
g.zTop, zUuid);
12171214
}
12181215
}
1219
- content_get(rid, &content);
1220
- if( manifest_parse(&m, &content)==0 ){
1221
- fossil_redirect_home();
1222
- }
1223
- if( m.type!=CFTYPE_TICKET ){
1216
+ pTktChng = manifest_get(rid, CFTYPE_TICKET);
1217
+ if( pTktChng==0 ){
12241218
fossil_redirect_home();
12251219
}
12261220
style_header("Ticket Change Details");
1227
- zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
1228
- memcpy(zTktName, m.zTicketUuid, 10);
1221
+ zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
1222
+ memcpy(zTktName, pTktChng->zTicketUuid, 10);
12291223
zTktName[10] = 0;
12301224
if( g.okHistory ){
1231
- @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2>
1225
+ @ <h2>Changes to ticket
1226
+ @ <a href="%s(pTktChng->zTicketUuid)">%s(zTktName)</a></h2>
12321227
@
1233
- @ <p>By %h(m.zUser) on %s(zDate). See also:
1228
+ @ <p>By %h(pTktChng->zUser) on %s(zDate). See also:
12341229
@ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
1235
- @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a>
1236
- @ </p>
1230
+ @ <a href="%s(g.zTop)/tkthistory/%s(pTktChng->zTicketUuid)">ticket
1231
+ @ history</a></p>
12371232
}else{
12381233
@ <h2>Changes to ticket %s(zTktName)</h2>
12391234
@
1240
- @ <p>By %h(m.zUser) on %s(zDate).
1235
+ @ <p>By %h(pTktChng->zUser) on %s(zDate).
12411236
@ </p>
12421237
}
12431238
@
12441239
@ <ol>
12451240
free(zDate);
1246
- ticket_output_change_artifact(&m);
1247
- manifest_clear(&m);
1241
+ ticket_output_change_artifact(pTktChng);
1242
+ manifest_destroy(pTktChng);
12481243
style_footer_cmdref("info",0);
12491244
}
12501245
12511246
12521247
/*
12531248
--- src/info.c
+++ src/info.c
@@ -545,24 +545,20 @@
545 rid = 0;
546 }
547 db_finalize(&q);
548 showTags(rid, "wiki-*");
549 if( rid ){
550 Blob content;
551 Manifest m;
552 memset(&m, 0, sizeof(m));
553 blob_zero(&m.content);
554 content_get(rid, &content);
555 manifest_parse(&m, &content);
556 if( m.type==CFTYPE_WIKI ){
557 Blob wiki;
558 blob_init(&wiki, m.zWiki, -1);
559 @ <div class="section">Content</div>
560 wiki_convert(&wiki, 0, 0);
561 blob_reset(&wiki);
562 }
563 manifest_clear(&m);
564 }
565 style_footer_cmdref("info",0);
566 }
567
568 /*
@@ -582,26 +578,23 @@
582
583 /*
584 ** Find an checkin based on query parameter zParam and parse its
585 ** manifest. Return the number of errors.
586 */
587 static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){
588 int rid;
589 Blob content;
590
591 *pRid = rid = name_to_rid_www(zParam);
592 if( rid==0 ){
593 webpage_error("Missing \"%s\" query parameter.", zParam);
594 return 1;
595 }
596 if( !is_a_version(rid) ){
597 webpage_error("Artifact %s is not a checkin.", P(zParam));
598 return 1;
599 }
600 content_get(rid, &content);
601 manifest_parse(pM, &content);
602 return 0;
603 }
604
605 /*
606 ** Output a description of a check-in
607 */
@@ -639,12 +632,12 @@
639 void vdiff_page(void){
640 int ridFrom, ridTo;
641 int showDetail = atoi(PD("detail","0"));
642 const char *zFrom = P("from");
643 const char *zTo = P("to");
644 int iFrom, iTo;
645 Manifest mFrom, mTo;
646
647 login_check_credentials();
648 if( !g.okRead ){ login_needed(); return; }
649 login_anonymous_available();
650
@@ -665,55 +658,59 @@
665 @ checked="checked" value="1" /></td></tr>
666 @ <tr><td></td><td></td><td>
667 @ <input type="submit" name="diff" value="diff" /></td></tr></table>
668 @ </div></form>
669 style_footer_cmdref("diff",0);
670 return;
671 }else if( vdiff_parse_manifest("from", &ridFrom, &mFrom)
672 || vdiff_parse_manifest("to", &ridTo, &mTo)
673 ){
674 return;
675 }
 
 
 
 
 
676 style_header("Check-in Differences");
677 @ <h2>Difference From:</h2><blockquote>
678 checkin_description(ridFrom);
679 @ </blockquote><h2>To:</h2><blockquote>
680 checkin_description(ridTo);
681 @ </blockquote><hr /><p>
682
683 iFrom = iTo = 0;
684 while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
 
 
 
685 int cmp;
686 if( iFrom>=mFrom.nFile ){
687 cmp = +1;
688 }else if( iTo>=mTo.nFile ){
689 cmp = -1;
690 }else{
691 cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
692 }
693 if( cmp<0 ){
694 append_file_change_line(mFrom.aFile[iFrom].zName,
695 mFrom.aFile[iFrom].zUuid, 0, 0);
696 iFrom++;
697 }else if( cmp>0 ){
698 append_file_change_line(mTo.aFile[iTo].zName,
699 0, mTo.aFile[iTo].zUuid, 0);
700 iTo++;
701 }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){
702 /* No changes */
703 iFrom++;
704 iTo++;
705 }else{
706 append_file_change_line(mFrom.aFile[iFrom].zName,
707 mFrom.aFile[iFrom].zUuid,
708 mTo.aFile[iTo].zUuid, showDetail);
709 iFrom++;
710 iTo++;
711 }
712 }
713 manifest_clear(&mFrom);
714 manifest_clear(&mTo);
715
716 style_footer_cmdref("diff",0);
717 }
718
719 /*
@@ -1069,25 +1066,26 @@
1069 */
1070 int artifact_from_ci_and_filename(void){
1071 const char *zFilename;
1072 const char *zCI;
1073 int cirid;
1074 Blob content;
1075 Manifest m;
1076 int i;
1077
1078 zCI = P("ci");
1079 if( zCI==0 ) return 0;
1080 zFilename = P("filename");
1081 if( zFilename==0 ) return 0;
1082 cirid = name_to_rid_www("ci");
1083 if( !content_get(cirid, &content) ) return 0;
1084 if( !manifest_parse(&m, &content) ) return 0;
1085 if( m.type!=CFTYPE_MANIFEST ) return 0;
1086 for(i=0; i<m.nFile; i++){
1087 if( strcmp(zFilename, m.aFile[i].zName)==0 ){
1088 return db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", m.aFile[i].zUuid);
 
 
1089 }
1090 }
1091 return 0;
1092 }
1093
@@ -1194,15 +1192,14 @@
1194 **
1195 ** Show the details of a ticket change control artifact.
1196 */
1197 void tinfo_page(void){
1198 int rid;
1199 Blob content;
1200 char *zDate;
1201 const char *zUuid;
1202 char zTktName[20];
1203 Manifest m;
1204
1205 login_check_credentials();
1206 if( !g.okRdTkt ){ login_needed(); return; }
1207 rid = name_to_rid_www("name");
1208 if( rid==0 ){ fossil_redirect_home(); }
@@ -1214,39 +1211,37 @@
1214 }else{
1215 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1216 g.zTop, zUuid);
1217 }
1218 }
1219 content_get(rid, &content);
1220 if( manifest_parse(&m, &content)==0 ){
1221 fossil_redirect_home();
1222 }
1223 if( m.type!=CFTYPE_TICKET ){
1224 fossil_redirect_home();
1225 }
1226 style_header("Ticket Change Details");
1227 zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
1228 memcpy(zTktName, m.zTicketUuid, 10);
1229 zTktName[10] = 0;
1230 if( g.okHistory ){
1231 @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2>
 
1232 @
1233 @ <p>By %h(m.zUser) on %s(zDate). See also:
1234 @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
1235 @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a>
1236 @ </p>
1237 }else{
1238 @ <h2>Changes to ticket %s(zTktName)</h2>
1239 @
1240 @ <p>By %h(m.zUser) on %s(zDate).
1241 @ </p>
1242 }
1243 @
1244 @ <ol>
1245 free(zDate);
1246 ticket_output_change_artifact(&m);
1247 manifest_clear(&m);
1248 style_footer_cmdref("info",0);
1249 }
1250
1251
1252 /*
1253
--- src/info.c
+++ src/info.c
@@ -545,24 +545,20 @@
545 rid = 0;
546 }
547 db_finalize(&q);
548 showTags(rid, "wiki-*");
549 if( rid ){
550 Manifest *pWiki;
551 pWiki = manifest_get(rid, CFTYPE_WIKI);
552 if( pWiki ){
 
 
 
 
553 Blob wiki;
554 blob_init(&wiki, pWiki->zWiki, -1);
555 @ <div class="section">Content</div>
556 wiki_convert(&wiki, 0, 0);
557 blob_reset(&wiki);
558 }
559 manifest_destroy(pWiki);
560 }
561 style_footer_cmdref("info",0);
562 }
563
564 /*
@@ -582,26 +578,23 @@
578
579 /*
580 ** Find an checkin based on query parameter zParam and parse its
581 ** manifest. Return the number of errors.
582 */
583 static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
584 int rid;
 
585
586 *pRid = rid = name_to_rid_www(zParam);
587 if( rid==0 ){
588 webpage_error("Missing \"%s\" query parameter.", zParam);
589 return 0;
590 }
591 if( !is_a_version(rid) ){
592 webpage_error("Artifact %s is not a checkin.", P(zParam));
593 return 0;
594 }
595 return manifest_get(rid, CFTYPE_MANIFEST);
 
 
596 }
597
598 /*
599 ** Output a description of a check-in
600 */
@@ -639,12 +632,12 @@
632 void vdiff_page(void){
633 int ridFrom, ridTo;
634 int showDetail = atoi(PD("detail","0"));
635 const char *zFrom = P("from");
636 const char *zTo = P("to");
637 Manifest *pFrom, *pTo;
638 ManifestFile *pFileFrom, *pFileTo;
639
640 login_check_credentials();
641 if( !g.okRead ){ login_needed(); return; }
642 login_anonymous_available();
643
@@ -665,55 +658,59 @@
658 @ checked="checked" value="1" /></td></tr>
659 @ <tr><td></td><td></td><td>
660 @ <input type="submit" name="diff" value="diff" /></td></tr></table>
661 @ </div></form>
662 style_footer_cmdref("diff",0);
 
 
 
 
663 return;
664 }
665 pFrom = vdiff_parse_manifest("from", &ridFrom);
666 if( pFrom==0 ) return;
667 pTo = vdiff_parse_manifest("to", &ridTo);
668 if( pTo==0 ) return;
669 showDetail = atoi(PD("detail","0"));
670 style_header("Check-in Differences");
671 @ <h2>Difference From:</h2><blockquote>
672 checkin_description(ridFrom);
673 @ </blockquote><h2>To:</h2><blockquote>
674 checkin_description(ridTo);
675 @ </blockquote><hr /><p>
676
677 manifest_file_rewind(pFrom);
678 pFileFrom = manifest_file_next(pFrom, 0);
679 manifest_file_rewind(pTo);
680 pFileTo = manifest_file_next(pTo, 0);
681 while( pFileFrom || pFileTo ){
682 int cmp;
683 if( pFileFrom==0 ){
684 cmp = +1;
685 }else if( pFileTo==0 ){
686 cmp = -1;
687 }else{
688 cmp = strcmp(pFileFrom->zName, pFileTo->zName);
689 }
690 if( cmp<0 ){
691 append_file_change_line(pFileFrom->zName,
692 pFileFrom->zUuid, 0, 0);
693 pFileFrom = manifest_file_next(pFrom, 0);
694 }else if( cmp>0 ){
695 append_file_change_line(pFileTo->zName,
696 0, pFileTo->zUuid, 0);
697 pFileTo = manifest_file_next(pTo, 0);
698 }else if( strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
699 /* No changes */
700 pFileFrom = manifest_file_next(pFrom, 0);
701 pFileTo = manifest_file_next(pTo, 0);
702 }else{
703 append_file_change_line(pFileFrom->zName,
704 pFileFrom->zUuid,
705 pFileTo->zUuid, showDetail);
706 pFileFrom = manifest_file_next(pFrom, 0);
707 pFileTo = manifest_file_next(pTo, 0);
708 }
709 }
710 manifest_destroy(pFrom);
711 manifest_destroy(pTo);
712
713 style_footer_cmdref("diff",0);
714 }
715
716 /*
@@ -1069,25 +1066,26 @@
1066 */
1067 int artifact_from_ci_and_filename(void){
1068 const char *zFilename;
1069 const char *zCI;
1070 int cirid;
1071 Manifest *pManifest;
1072 ManifestFile *pFile;
 
1073
1074 zCI = P("ci");
1075 if( zCI==0 ) return 0;
1076 zFilename = P("filename");
1077 if( zFilename==0 ) return 0;
1078 cirid = name_to_rid_www("ci");
1079 pManifest = manifest_get(cirid, CFTYPE_MANIFEST);
1080 if( pManifest==0 ) return 0;
1081 manifest_file_rewind(pManifest);
1082 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1083 if( strcmp(zFilename, pFile->zName)==0 ){
1084 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1085 manifest_destroy(pManifest);
1086 return rid;
1087 }
1088 }
1089 return 0;
1090 }
1091
@@ -1194,15 +1192,14 @@
1192 **
1193 ** Show the details of a ticket change control artifact.
1194 */
1195 void tinfo_page(void){
1196 int rid;
 
1197 char *zDate;
1198 const char *zUuid;
1199 char zTktName[20];
1200 Manifest *pTktChng;
1201
1202 login_check_credentials();
1203 if( !g.okRdTkt ){ login_needed(); return; }
1204 rid = name_to_rid_www("name");
1205 if( rid==0 ){ fossil_redirect_home(); }
@@ -1214,39 +1211,37 @@
1211 }else{
1212 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1213 g.zTop, zUuid);
1214 }
1215 }
1216 pTktChng = manifest_get(rid, CFTYPE_TICKET);
1217 if( pTktChng==0 ){
 
 
 
1218 fossil_redirect_home();
1219 }
1220 style_header("Ticket Change Details");
1221 zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
1222 memcpy(zTktName, pTktChng->zTicketUuid, 10);
1223 zTktName[10] = 0;
1224 if( g.okHistory ){
1225 @ <h2>Changes to ticket
1226 @ <a href="%s(pTktChng->zTicketUuid)">%s(zTktName)</a></h2>
1227 @
1228 @ <p>By %h(pTktChng->zUser) on %s(zDate). See also:
1229 @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
1230 @ <a href="%s(g.zTop)/tkthistory/%s(pTktChng->zTicketUuid)">ticket
1231 @ history</a></p>
1232 }else{
1233 @ <h2>Changes to ticket %s(zTktName)</h2>
1234 @
1235 @ <p>By %h(pTktChng->zUser) on %s(zDate).
1236 @ </p>
1237 }
1238 @
1239 @ <ol>
1240 free(zDate);
1241 ticket_output_change_artifact(pTktChng);
1242 manifest_destroy(pTktChng);
1243 style_footer_cmdref("info",0);
1244 }
1245
1246
1247 /*
1248
+612 -380
--- src/manifest.c
+++ src/manifest.c
@@ -26,24 +26,39 @@
2626
2727
#if INTERFACE
2828
/*
2929
** Types of control files
3030
*/
31
+#define CFTYPE_ANY 0
3132
#define CFTYPE_MANIFEST 1
3233
#define CFTYPE_CLUSTER 2
3334
#define CFTYPE_CONTROL 3
3435
#define CFTYPE_WIKI 4
3536
#define CFTYPE_TICKET 5
3637
#define CFTYPE_ATTACHMENT 6
3738
#define CFTYPE_EVENT 7
39
+
40
+/*
41
+** A single F-card within a manifest
42
+*/
43
+struct ManifestFile {
44
+ char *zName; /* Name of a file */
45
+ char *zUuid; /* UUID of the file */
46
+ char *zPerm; /* File permissions */
47
+ char *zPrior; /* Prior name if the name was changed */
48
+};
49
+
3850
3951
/*
4052
** A parsed manifest or cluster.
4153
*/
4254
struct Manifest {
4355
Blob content; /* The original content blob */
4456
int type; /* Type of artifact. One of CFTYPE_xxxxx */
57
+ int rid; /* The blob-id for this manifest */
58
+ char *zBaseline; /* Baseline manifest. The B card. */
59
+ Manifest *pBaseline; /* The actual baseline manifest */
4560
char *zComment; /* Decoded comment. The C card. */
4661
double rDate; /* Date and time from D card. 0.0 if no D card. */
4762
char *zUser; /* Name of the user from the U card. */
4863
char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
4964
char *zWiki; /* Text of the wiki page. W card. */
@@ -54,17 +69,12 @@
5469
char *zAttachName; /* Filename of an attachment. A card. */
5570
char *zAttachSrc; /* UUID of document being attached. A card. */
5671
char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
5772
int nFile; /* Number of F cards */
5873
int nFileAlloc; /* Slots allocated in aFile[] */
59
- struct ManifestFile {
60
- char *zName; /* Name of a file */
61
- char *zUuid; /* UUID of the file */
62
- char *zPerm; /* File permissions */
63
- char *zPrior; /* Prior name if the name was changed */
64
- int iRename; /* index of renamed name in prior/next manifest */
65
- } *aFile; /* One entry for each F card */
74
+ int iFile; /* Index of current file in iterator */
75
+ ManifestFile *aFile; /* One entry for each F-card */
6676
int nParent; /* Number of parents. */
6777
int nParentAlloc; /* Slots allocated in azParent[] */
6878
char **azParent; /* UUIDs of parents. One for each P card argument */
6979
int nCChild; /* Number of cluster children */
7080
int nCChildAlloc; /* Number of closts allocated in azCChild[] */
@@ -87,68 +97,75 @@
8797
8898
/*
8999
** A cache of parsed manifests. This reduces the number of
90100
** calls to manifest_parse() when doing a rebuild.
91101
*/
92
-#define MX_MANIFEST_CACHE 4
102
+#define MX_MANIFEST_CACHE 6
93103
static struct {
94104
int nxAge;
95
- int aRid[MX_MANIFEST_CACHE];
96105
int aAge[MX_MANIFEST_CACHE];
97
- Manifest aLine[MX_MANIFEST_CACHE];
106
+ Manifest *apManifest[MX_MANIFEST_CACHE];
98107
} manifestCache;
99108
100109
101110
/*
102111
** Clear the memory allocated in a manifest object
103112
*/
104
-void manifest_clear(Manifest *p){
105
- blob_reset(&p->content);
106
- free(p->aFile);
107
- free(p->azParent);
108
- free(p->azCChild);
109
- free(p->aTag);
110
- free(p->aField);
111
- memset(p, 0, sizeof(*p));
113
+void manifest_destroy(Manifest *p){
114
+ if( p ){
115
+ blob_reset(&p->content);
116
+ free(p->aFile);
117
+ free(p->azParent);
118
+ free(p->azCChild);
119
+ free(p->aTag);
120
+ free(p->aField);
121
+ if( p->pBaseline ) manifest_destroy(p->pBaseline);
122
+ fossil_free(p);
123
+ }
112124
}
113125
114126
/*
115127
** Add an element to the manifest cache using LRU replacement.
116128
*/
117
-void manifest_cache_insert(int rid, Manifest *p){
118
- int i;
119
- for(i=0; i<MX_MANIFEST_CACHE; i++){
120
- if( manifestCache.aRid[i]==0 ) break;
121
- }
122
- if( i>=MX_MANIFEST_CACHE ){
123
- int oldest = 0;
124
- int oldestAge = manifestCache.aAge[0];
125
- for(i=1; i<MX_MANIFEST_CACHE; i++){
126
- if( manifestCache.aAge[i]<oldestAge ){
127
- oldest = i;
128
- oldestAge = manifestCache.aAge[i];
129
- }
130
- }
131
- manifest_clear(&manifestCache.aLine[oldest]);
132
- i = oldest;
133
- }
134
- manifestCache.aAge[i] = ++manifestCache.nxAge;
135
- manifestCache.aRid[i] = rid;
136
- manifestCache.aLine[i] = *p;
129
+void manifest_cache_insert(Manifest *p){
130
+ while( p ){
131
+ int i;
132
+ Manifest *pBaseline = p->pBaseline;
133
+ p->pBaseline = 0;
134
+ for(i=0; i<MX_MANIFEST_CACHE; i++){
135
+ if( manifestCache.apManifest[i]==0 ) break;
136
+ }
137
+ if( i>=MX_MANIFEST_CACHE ){
138
+ int oldest = 0;
139
+ int oldestAge = manifestCache.aAge[0];
140
+ for(i=1; i<MX_MANIFEST_CACHE; i++){
141
+ if( manifestCache.aAge[i]<oldestAge ){
142
+ oldest = i;
143
+ oldestAge = manifestCache.aAge[i];
144
+ }
145
+ }
146
+ manifest_destroy(manifestCache.apManifest[oldest]);
147
+ i = oldest;
148
+ }
149
+ manifestCache.aAge[i] = ++manifestCache.nxAge;
150
+ manifestCache.apManifest[i] = p;
151
+ p = pBaseline;
152
+ }
137153
}
138154
139155
/*
140156
** Try to extract a line from the manifest cache. Return 1 if found.
141157
** Return 0 if not found.
142158
*/
143
-int manifest_cache_find(int rid, Manifest *p){
159
+static Manifest *manifest_cache_find(int rid){
144160
int i;
161
+ Manifest *p;
145162
for(i=0; i<MX_MANIFEST_CACHE; i++){
146
- if( manifestCache.aRid[i]==rid ){
147
- *p = manifestCache.aLine[i];
148
- manifestCache.aRid[i] = 0;
149
- return 1;
163
+ if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){
164
+ p = manifestCache.apManifest[i];
165
+ manifestCache.apManifest[i] = 0;
166
+ return p;
150167
}
151168
}
152169
return 0;
153170
}
154171
@@ -156,21 +173,113 @@
156173
** Clear the manifest cache.
157174
*/
158175
void manifest_cache_clear(void){
159176
int i;
160177
for(i=0; i<MX_MANIFEST_CACHE; i++){
161
- if( manifestCache.aRid[i]>0 ){
162
- manifest_clear(&manifestCache.aLine[i]);
178
+ if( manifestCache.apManifest[i] ){
179
+ manifest_destroy(manifestCache.apManifest[i]);
163180
}
164181
}
165182
memset(&manifestCache, 0, sizeof(manifestCache));
166183
}
167184
168185
#ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
169186
# define md5sum_init(X)
170187
# define md5sum_step_text(X,Y)
171188
#endif
189
+
190
+/*
191
+** Remove the PGP signature from the artifact, if there is one.
192
+*/
193
+static void remove_pgp_signature(char **pz, int *pn){
194
+ char *z = *pz;
195
+ int n = *pn;
196
+ int i;
197
+ if( memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return;
198
+ for(i=34; i<n && (z[i-1]!='\n' || z[i-2]!='\n'); i++){}
199
+ if( i>=n ) return;
200
+ z += i;
201
+ n -= i;
202
+ *pz = z;
203
+ for(i=n-1; i>=0; i--){
204
+ if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){
205
+ n = i+1;
206
+ break;
207
+ }
208
+ }
209
+ *pn = n;
210
+ return;
211
+}
212
+
213
+/*
214
+** Verify the Z-card checksum on the artifact, if there is such a
215
+** checksum. Return 0 if there is no Z-card. Return 1 if the Z-card
216
+** exists and is correct. Return 2 if the Z-card exists and has the wrong
217
+** value.
218
+**
219
+** 0123456789 123456789 123456789 123456789
220
+** Z aea84f4f863865a8d59d0384e4d2a41c
221
+*/
222
+static int verify_z_card(const char *z, int n){
223
+ if( n<35 ) return 0;
224
+ if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0;
225
+ md5sum_init();
226
+ md5sum_step_text(z, n-35);
227
+ if( memcmp(&z[n-33], md5sum_finish(0), 32)==0 ){
228
+ return 1;
229
+ }else{
230
+ return 2;
231
+ }
232
+}
233
+
234
+/*
235
+** A structure used for rapid parsing of the Manifest file
236
+*/
237
+typedef struct ManifestText ManifestText;
238
+struct ManifestText {
239
+ char *z; /* The first character of the next token */
240
+ char *zEnd; /* One character beyond the end of the manifest */
241
+ int atEol; /* True if z points to the start of a new line */
242
+};
243
+
244
+/*
245
+** Return a pointer to the next token. The token is zero-terminated.
246
+** Return NULL if there are no more tokens on the current line.
247
+*/
248
+static char *next_token(ManifestText *p, int *pLen){
249
+ char *z;
250
+ char *zStart;
251
+ int c;
252
+ if( p->atEol ) return 0;
253
+ zStart = z = p->z;
254
+ while( (c=(*z))!=' ' && c!='\n' ){ z++; }
255
+ *z = 0;
256
+ p->z = &z[1];
257
+ p->atEol = c=='\n';
258
+ if( pLen ) *pLen = z - zStart;
259
+ return zStart;
260
+}
261
+
262
+/*
263
+** Return the card-type for the next card. Or, return 0 if there are no
264
+** more cards or if we are not at the end of the current card.
265
+*/
266
+static char next_card(ManifestText *p){
267
+ char c;
268
+ if( !p->atEol || p->z>=p->zEnd ) return 0;
269
+ c = p->z[0];
270
+ if( p->z[1]==' ' ){
271
+ p->z += 2;
272
+ p->atEol = 0;
273
+ }else if( p->z[1]=='\n' ){
274
+ p->z += 2;
275
+ p->atEol = 1;
276
+ }else{
277
+ c = 0;
278
+ }
279
+ return c;
280
+}
172281
173282
/*
174283
** Parse a blob into a Manifest object. The Manifest object
175284
** takes over the input blob and will free it when the
176285
** Manifest object is freed. Zeros are inserted into the blob
@@ -195,56 +304,66 @@
195304
** Each card is divided into tokens by a single space character.
196305
** The first token is a single upper-case letter which is the card type.
197306
** The card type determines the other parameters to the card.
198307
** Cards must occur in lexicographical order.
199308
*/
200
-int manifest_parse(Manifest *p, Blob *pContent){
201
- int seenHeader = 0;
309
+static Manifest *manifest_parse(Blob *pContent, int rid){
310
+ Manifest *p;
202311
int seenZ = 0;
203312
int i, lineNo=0;
204
- Blob line, token, a1, a2, a3, a4;
313
+ ManifestText x;
205314
char cPrevType = 0;
315
+ char cType;
316
+ char *z;
317
+ int n;
318
+ char *zUuid;
319
+ int sz;
206320
207321
/* Every control artifact ends with a '\n' character. Exit early
208
- ** if that is not the case for this artifact. */
209
- i = blob_size(pContent);
210
- if( i<=0 || blob_buffer(pContent)[i-1]!='\n' ){
322
+ ** if that is not the case for this artifact.
323
+ */
324
+ z = blob_buffer(pContent);
325
+ n = blob_size(pContent);
326
+ if( n<=0 || z[n-1]!='\n' ){
327
+ blob_reset(pContent);
328
+ return 0;
329
+ }
330
+
331
+ /* Strip off the PGP signature if there is one. Then verify the
332
+ ** Z-card.
333
+ */
334
+ remove_pgp_signature(&z, &n);
335
+ if( verify_z_card(z, n)==0 ){
336
+ blob_reset(pContent);
337
+ return 0;
338
+ }
339
+
340
+ /* Verify that the first few characters of the artifact look like
341
+ ** a control artifact.
342
+ */
343
+ if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
211344
blob_reset(pContent);
212345
return 0;
213346
}
214347
348
+ /* Allocate a Manifest object to hold the parsed control artifact.
349
+ */
350
+ p = fossil_malloc( sizeof(*p) );
215351
memset(p, 0, sizeof(*p));
216352
memcpy(&p->content, pContent, sizeof(p->content));
353
+ p->rid = rid;
217354
blob_zero(pContent);
218355
pContent = &p->content;
219356
220
- blob_zero(&a1);
221
- blob_zero(&a2);
222
- blob_zero(&a3);
223
- md5sum_init();
224
- while( blob_line(pContent, &line) ){
225
- char *z = blob_buffer(&line);
357
+ /* Begin parsing, card by card.
358
+ */
359
+ x.z = z;
360
+ x.zEnd = &z[n];
361
+ x.atEol = 1;
362
+ while( (cType = next_card(&x))!=0 && cType>=cPrevType ){
226363
lineNo++;
227
- if( z[0]=='-' ){
228
- if( strncmp(z, "-----BEGIN PGP ", 15)!=0 ){
229
- goto manifest_syntax_error;
230
- }
231
- if( seenHeader ){
232
- break;
233
- }
234
- while( blob_line(pContent, &line)>2 ){}
235
- if( blob_line(pContent, &line)==0 ) break;
236
- z = blob_buffer(&line);
237
- }
238
- if( z[0]<cPrevType ){
239
- /* Lines of a manifest must occur in lexicographical order */
240
- goto manifest_syntax_error;
241
- }
242
- cPrevType = z[0];
243
- seenHeader = 1;
244
- if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
245
- switch( z[0] ){
364
+ switch( cType ){
246365
/*
247366
** A <filename> <target> ?<source>?
248367
**
249368
** Identifies an attachment to either a wiki page or a ticket.
250369
** <source> is the artifact that is the attachment. <source>
@@ -251,50 +370,60 @@
251370
** is omitted to delete an attachment. <target> is the name of
252371
** a wiki page or ticket to which that attachment is connected.
253372
*/
254373
case 'A': {
255374
char *zName, *zTarget, *zSrc;
256
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
257
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
258
- if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
375
+ int nTarget, nSrc;
376
+ zName = next_token(&x, 0);
377
+ zTarget = next_token(&x, &nTarget);
378
+ zSrc = next_token(&x, &nSrc);
379
+ if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
259380
if( p->zAttachName!=0 ) goto manifest_syntax_error;
260
- zName = blob_terminate(&a1);
261
- zTarget = blob_terminate(&a2);
262
- blob_token(&line, &a3);
263
- zSrc = blob_terminate(&a3);
264381
defossilize(zName);
265382
if( !file_is_simple_pathname(zName) ){
266383
goto manifest_syntax_error;
267384
}
268385
defossilize(zTarget);
269
- if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
386
+ if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
270387
&& !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
271388
goto manifest_syntax_error;
272389
}
273
- if( blob_size(&a3)>0
274
- && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
390
+ if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
275391
goto manifest_syntax_error;
276392
}
277393
p->zAttachName = (char*)file_tail(zName);
278394
p->zAttachSrc = zSrc;
279395
p->zAttachTarget = zTarget;
280396
break;
281397
}
398
+
399
+ /*
400
+ ** B <uuid>
401
+ **
402
+ ** A B-line gives the UUID for the baselinen of a delta-manifest.
403
+ */
404
+ case 'B': {
405
+ if( p->zBaseline ) goto manifest_syntax_error;
406
+ p->zBaseline = next_token(&x, &sz);
407
+ if( p->zBaseline==0 ) goto manifest_syntax_error;
408
+ if( sz!=UUID_SIZE ) goto manifest_syntax_error;
409
+ if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error;
410
+ break;
411
+ }
412
+
282413
283414
/*
284415
** C <comment>
285416
**
286417
** Comment text is fossil-encoded. There may be no more than
287418
** one C line. C lines are required for manifests and are
288419
** disallowed on all other control files.
289420
*/
290421
case 'C': {
291
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
292422
if( p->zComment!=0 ) goto manifest_syntax_error;
293
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
294
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
295
- p->zComment = blob_terminate(&a1);
423
+ p->zComment = next_token(&x, 0);
424
+ if( p->zComment==0 ) goto manifest_syntax_error;
296425
defossilize(p->zComment);
297426
break;
298427
}
299428
300429
/*
@@ -303,17 +432,13 @@
303432
** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS
304433
** There can be no more than 1 D line. D lines are required
305434
** for all control files except for clusters.
306435
*/
307436
case 'D': {
308
- char *zDate;
309
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
310
- if( p->rDate!=0.0 ) goto manifest_syntax_error;
311
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
312
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
313
- zDate = blob_terminate(&a1);
314
- p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
437
+ if( p->rDate>0.0 ) goto manifest_syntax_error;
438
+ p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0));
439
+ if( p->rDate<=0.0 ) goto manifest_syntax_error;
315440
break;
316441
}
317442
318443
/*
319444
** E <timestamp> <uuid>
@@ -323,56 +448,46 @@
323448
** The event timestamp is distinct from the D timestamp. The D
324449
** timestamp is when the artifact was created whereas the E timestamp
325450
** is when the specific event is said to occur.
326451
*/
327452
case 'E': {
328
- char *zEDate;
329
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
330
- if( p->rEventDate!=0.0 ) goto manifest_syntax_error;
331
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
332
- if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
333
- if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
334
- zEDate = blob_terminate(&a1);
335
- p->rEventDate = db_double(0.0, "SELECT julianday(%Q)", zEDate);
453
+ if( p->rEventDate>0.0 ) goto manifest_syntax_error;
454
+ p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
336455
if( p->rEventDate<=0.0 ) goto manifest_syntax_error;
337
- if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
338
- p->zEventId = blob_terminate(&a2);
456
+ p->zEventId = next_token(&x, &sz);
457
+ if( sz!=UUID_SIZE ) goto manifest_syntax_error;
339458
if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error;
340459
break;
341460
}
342461
343462
/*
344
- ** F <filename> <uuid> ?<permissions>? ?<old-name>?
463
+ ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
345464
**
346465
** Identifies a file in a manifest. Multiple F lines are
347466
** allowed in a manifest. F lines are not allowed in any
348467
** other control file. The filename and old-name are fossil-encoded.
349468
*/
350469
case 'F': {
351
- char *zName, *zUuid, *zPerm, *zPriorName;
352
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
353
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
354
- if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
355
- zName = blob_terminate(&a1);
356
- zUuid = blob_terminate(&a2);
357
- blob_token(&line, &a3);
358
- zPerm = blob_terminate(&a3);
359
- if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
360
- if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
470
+ char *zName, *zPerm, *zPriorName;
471
+ zName = next_token(&x,0);
472
+ if( zName==0 ) goto manifest_syntax_error;
361473
defossilize(zName);
362474
if( !file_is_simple_pathname(zName) ){
363475
goto manifest_syntax_error;
364476
}
365
- blob_token(&line, &a4);
366
- zPriorName = blob_terminate(&a4);
367
- if( zPriorName[0] ){
477
+ zUuid = next_token(&x, &sz);
478
+ if( p->zBaseline==0 || zUuid!=0 ){
479
+ if( sz!=UUID_SIZE ) goto manifest_syntax_error;
480
+ if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
481
+ }
482
+ zPerm = next_token(&x,0);
483
+ zPriorName = next_token(&x,0);
484
+ if( zPriorName ){
368485
defossilize(zPriorName);
369486
if( !file_is_simple_pathname(zPriorName) ){
370487
goto manifest_syntax_error;
371488
}
372
- }else{
373
- zPriorName = 0;
374489
}
375490
if( p->nFile>=p->nFileAlloc ){
376491
p->nFileAlloc = p->nFileAlloc*2 + 10;
377492
p->aFile = fossil_realloc(p->aFile,
378493
p->nFileAlloc*sizeof(p->aFile[0]) );
@@ -380,11 +495,10 @@
380495
i = p->nFile++;
381496
p->aFile[i].zName = zName;
382497
p->aFile[i].zUuid = zUuid;
383498
p->aFile[i].zPerm = zPerm;
384499
p->aFile[i].zPrior = zPriorName;
385
- p->aFile[i].iRename = -1;
386500
if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
387501
goto manifest_syntax_error;
388502
}
389503
break;
390504
}
@@ -397,16 +511,14 @@
397511
** value. If <value> is omitted then it is understood to be an
398512
** empty string.
399513
*/
400514
case 'J': {
401515
char *zName, *zValue;
402
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
403
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
404
- blob_token(&line, &a2);
405
- if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
406
- zName = blob_terminate(&a1);
407
- zValue = blob_terminate(&a2);
516
+ zName = next_token(&x,0);
517
+ zValue = next_token(&x,0);
518
+ if( zName==0 ) goto manifest_syntax_error;
519
+ if( zValue==0 ) zValue = "";
408520
defossilize(zValue);
409521
if( p->nField>=p->nFieldAlloc ){
410522
p->nFieldAlloc = p->nFieldAlloc*2 + 10;
411523
p->aField = fossil_realloc(p->aField,
412524
p->nFieldAlloc*sizeof(p->aField[0]) );
@@ -426,18 +538,14 @@
426538
**
427539
** A K-line gives the UUID for the ticket which this control file
428540
** is amending.
429541
*/
430542
case 'K': {
431
- char *zUuid;
432
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
433
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
434
- zUuid = blob_terminate(&a1);
435
- if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
436
- if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
437543
if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
438
- p->zTicketUuid = zUuid;
544
+ p->zTicketUuid = next_token(&x, &sz);
545
+ if( sz!=UUID_SIZE ) goto manifest_syntax_error;
546
+ if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error;
439547
break;
440548
}
441549
442550
/*
443551
** L <wikititle>
@@ -444,15 +552,13 @@
444552
**
445553
** The wiki page title is fossil-encoded. There may be no more than
446554
** one L line.
447555
*/
448556
case 'L': {
449
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
450557
if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
451
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
452
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
453
- p->zWikiTitle = blob_terminate(&a1);
558
+ p->zWikiTitle = next_token(&x,0);
559
+ if( p->zWikiTitle==0 ) goto manifest_syntax_error;
454560
defossilize(p->zWikiTitle);
455561
if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
456562
goto manifest_syntax_error;
457563
}
458564
break;
@@ -463,15 +569,13 @@
463569
**
464570
** An M-line identifies another artifact by its UUID. M-lines
465571
** occur in clusters only.
466572
*/
467573
case 'M': {
468
- char *zUuid;
469
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
470
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
471
- zUuid = blob_terminate(&a1);
472
- if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
574
+ zUuid = next_token(&x, &sz);
575
+ if( zUuid==0 ) goto manifest_syntax_error;
576
+ if( sz!=UUID_SIZE ) goto manifest_syntax_error;
473577
if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
474578
if( p->nCChild>=p->nCChildAlloc ){
475579
p->nCChildAlloc = p->nCChildAlloc*2 + 10;
476580
p->azCChild = fossil_realloc(p->azCChild
477581
, p->nCChildAlloc*sizeof(p->azCChild[0]) );
@@ -490,15 +594,12 @@
490594
** Specify one or more other artifacts where are the parents of
491595
** this artifact. The first parent is the primary parent. All
492596
** others are parents by merge.
493597
*/
494598
case 'P': {
495
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
496
- while( blob_token(&line, &a1) ){
497
- char *zUuid;
498
- if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
499
- zUuid = blob_terminate(&a1);
599
+ while( (zUuid = next_token(&x, &sz))!=0 ){
600
+ if( sz!=UUID_SIZE ) goto manifest_syntax_error;
500601
if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
501602
if( p->nParent>=p->nParentAlloc ){
502603
p->nParentAlloc = p->nParentAlloc*2 + 5;
503604
p->azParent = fossil_realloc(p->azParent,
504605
p->nParentAlloc*sizeof(char*));
@@ -514,16 +615,13 @@
514615
**
515616
** Specify the MD5 checksum over the name and content of all files
516617
** in the manifest.
517618
*/
518619
case 'R': {
519
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
520620
if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
521
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
522
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
523
- if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
524
- p->zRepoCksum = blob_terminate(&a1);
621
+ p->zRepoCksum = next_token(&x, &sz);
622
+ if( sz!=32 ) goto manifest_syntax_error;
525623
if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
526624
break;
527625
}
528626
529627
/*
@@ -540,29 +638,20 @@
540638
** the tag is really a property with the given value.
541639
**
542640
** Tags are not allowed in clusters. Multiple T lines are allowed.
543641
*/
544642
case 'T': {
545
- char *zName, *zUuid, *zValue;
546
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
547
- if( blob_token(&line, &a1)==0 ){
548
- goto manifest_syntax_error;
549
- }
550
- if( blob_token(&line, &a2)==0 ){
551
- goto manifest_syntax_error;
552
- }
553
- zName = blob_terminate(&a1);
554
- zUuid = blob_terminate(&a2);
555
- if( blob_token(&line, &a3)==0 ){
556
- zValue = 0;
557
- }else{
558
- zValue = blob_terminate(&a3);
559
- defossilize(zValue);
560
- }
561
- if( blob_size(&a2)==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
643
+ char *zName, *zValue;
644
+ zName = next_token(&x, 0);
645
+ if( zName==0 ) goto manifest_syntax_error;
646
+ zUuid = next_token(&x, &sz);
647
+ if( zUuid==0 ) goto manifest_syntax_error;
648
+ zValue = next_token(&x, 0);
649
+ if( zValue ) defossilize(zValue);
650
+ if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
562651
/* A valid uuid */
563
- }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){
652
+ }else if( sz==1 && zUuid[0]=='*' ){
564653
zUuid = 0;
565654
}else{
566655
goto manifest_syntax_error;
567656
}
568657
defossilize(zName);
@@ -593,19 +682,17 @@
593682
** Identify the user who created this control file by their
594683
** login. Only one U line is allowed. Prohibited in clusters.
595684
** If the user name is omitted, take that to be "anonymous".
596685
*/
597686
case 'U': {
598
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
599687
if( p->zUser!=0 ) goto manifest_syntax_error;
600
- if( blob_token(&line, &a1)==0 ){
688
+ p->zUser = next_token(&x, 0);
689
+ if( p->zUser==0 ){
601690
p->zUser = "anonymous";
602691
}else{
603
- p->zUser = blob_terminate(&a1);
604692
defossilize(p->zUser);
605693
}
606
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
607694
break;
608695
}
609696
610697
/*
611698
** W <size>
@@ -613,26 +700,28 @@
613700
** The next <size> bytes of the file contain the text of the wiki
614701
** page. There is always an extra \n before the start of the next
615702
** record.
616703
*/
617704
case 'W': {
618
- int size;
705
+ char *zSize;
706
+ int size, c;
619707
Blob wiki;
620
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
621
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
622
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
623
- if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error;
708
+ zSize = next_token(&x, 0);
709
+ if( zSize==0 ) goto manifest_syntax_error;
710
+ if( x.atEol==0 ) goto manifest_syntax_error;
711
+ for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
712
+ size = size*10 + c - '0';
713
+ }
624714
if( size<0 ) goto manifest_syntax_error;
625715
if( p->zWiki!=0 ) goto manifest_syntax_error;
626716
blob_zero(&wiki);
627
- if( blob_extract(pContent, size+1, &wiki)!=size+1 ){
628
- goto manifest_syntax_error;
629
- }
630
- p->zWiki = blob_buffer(&wiki);
631
- md5sum_step_text(p->zWiki, size+1);
632
- if( p->zWiki[size]!='\n' ) goto manifest_syntax_error;
633
- p->zWiki[size] = 0;
717
+ if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error;
718
+ p->zWiki = x.z;
719
+ x.z += size;
720
+ if( x.z[0]!='\n' ) goto manifest_syntax_error;
721
+ x.z[0] = 0;
722
+ x.z++;
634723
break;
635724
}
636725
637726
638727
/*
@@ -645,35 +734,24 @@
645734
** This card is required for all control file types except for
646735
** Manifest. It is not required for manifest only for historical
647736
** compatibility reasons.
648737
*/
649738
case 'Z': {
650
-#ifndef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
651
- int rc;
652
- Blob hash;
653
-#endif
654
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
655
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
656
- if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
657
- if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
658
-#ifndef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
659
- md5sum_finish(&hash);
660
- rc = blob_compare(&hash, &a1);
661
- blob_reset(&hash);
662
- if( rc!=0 ) goto manifest_syntax_error;
663
-#endif
739
+ zUuid = next_token(&x, &sz);
740
+ if( sz!=32 ) goto manifest_syntax_error;
741
+ if( !validate16(zUuid, 32) ) goto manifest_syntax_error;
664742
seenZ = 1;
665743
break;
666744
}
667745
default: {
668746
goto manifest_syntax_error;
669747
}
670748
}
671749
}
672
- if( !seenHeader ) goto manifest_syntax_error;
750
+ if( x.z<x.zEnd ) goto manifest_syntax_error;
673751
674
- if( p->nFile>0 || p->zRepoCksum!=0 ){
752
+ if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
675753
if( p->nCChild>0 ) goto manifest_syntax_error;
676754
if( p->rDate<=0.0 ) goto manifest_syntax_error;
677755
if( p->nField>0 ) goto manifest_syntax_error;
678756
if( p->zTicketUuid ) goto manifest_syntax_error;
679757
if( p->zWiki ) goto manifest_syntax_error;
@@ -754,43 +832,175 @@
754832
if( p->zWikiTitle ) goto manifest_syntax_error;
755833
if( p->zTicketUuid ) goto manifest_syntax_error;
756834
p->type = CFTYPE_MANIFEST;
757835
}
758836
md5sum_init();
759
- return 1;
837
+ return p;
760838
761839
manifest_syntax_error:
762840
/*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
763841
md5sum_init();
764
- manifest_clear(p);
842
+ manifest_destroy(p);
765843
return 0;
766844
}
845
+
846
+/*
847
+** Get a manifest given the rid for the control artifact. Return
848
+** a pointer to the manifest on success or NULL if there is a failure.
849
+*/
850
+Manifest *manifest_get(int rid, int cfType){
851
+ Blob content;
852
+ Manifest *p;
853
+ p = manifest_cache_find(rid);
854
+ if( p ){
855
+ if( cfType!=CFTYPE_ANY && cfType!=p->type ){
856
+ manifest_cache_insert(p);
857
+ p = 0;
858
+ }
859
+ return p;
860
+ }
861
+ content_get(rid, &content);
862
+ p = manifest_parse(&content, rid);
863
+ if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
864
+ manifest_destroy(p);
865
+ p = 0;
866
+ }
867
+ return p;
868
+}
869
+
870
+/*
871
+** Given a checkin name, load and parse the manifest for that checkin.
872
+** Throw a fatal error if anything goes wrong.
873
+*/
874
+Manifest *manifest_get_by_name(const char *zName, int *pRid){
875
+ int rid;
876
+ Manifest *p;
877
+
878
+ rid = name_to_rid(zName);
879
+ if( !is_a_version(rid) ){
880
+ fossil_fatal("no such checkin: %s", zName);
881
+ }
882
+ if( pRid ) *pRid = rid;
883
+ p = manifest_get(rid, CFTYPE_MANIFEST);
884
+ if( p==0 ){
885
+ fossil_fatal("cannot parse manifest for checkin: %s", zName);
886
+ }
887
+ return p;
888
+}
767889
768890
/*
769891
** COMMAND: test-parse-manifest
770892
**
771893
** Usage: %fossil test-parse-manifest FILENAME ?N?
772894
**
773895
** Parse the manifest and discarded. Use for testing only.
774896
*/
775897
void manifest_test_parse_cmd(void){
776
- Manifest m;
898
+ Manifest *p;
777899
Blob b;
778900
int i;
779901
int n = 1;
902
+ sqlite3_open(":memory:", &g.db);
780903
if( g.argc!=3 && g.argc!=4 ){
781904
usage("FILENAME");
782905
}
783
- db_must_be_within_tree();
784906
blob_read_from_file(&b, g.argv[2]);
785907
if( g.argc>3 ) n = atoi(g.argv[3]);
786908
for(i=0; i<n; i++){
787909
Blob b2;
788910
blob_copy(&b2, &b);
789
- manifest_parse(&m, &b2);
790
- manifest_clear(&m);
911
+ p = manifest_parse(&b2, 0);
912
+ manifest_destroy(p);
913
+ }
914
+}
915
+
916
+/*
917
+** Fetch the baseline associated with the delta-manifest p.
918
+** Print a fatal-error and quit if unable to load the baseline.
919
+*/
920
+static void fetch_baseline(Manifest *p){
921
+ if( p->zBaseline!=0 && p->pBaseline==0 ){
922
+ int rid = uuid_to_rid(p->zBaseline, 0);
923
+ p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST);
924
+ if( p->pBaseline==0 ){
925
+ fossil_fatal("cannot access baseline manifest %S", p->zBaseline);
926
+ }
927
+ }
928
+}
929
+
930
+/*
931
+** Rewind a manifest-file iterator back to the beginning of the manifest.
932
+*/
933
+void manifest_file_rewind(Manifest *p){
934
+ p->iFile = 0;
935
+ fetch_baseline(p);
936
+ if( p->pBaseline ){
937
+ p->pBaseline->iFile = 0;
938
+ }
939
+}
940
+
941
+/*
942
+** Advance to the next manifest-file.
943
+**
944
+** Return NULL for end-of-records or if there is an error. If an error
945
+** occurs and pErr!=0 then store 1 in *pErr.
946
+*/
947
+ManifestFile *manifest_file_next(
948
+ Manifest *p,
949
+ int *pErr
950
+){
951
+ ManifestFile *pOut = 0;
952
+ if( pErr ) *pErr = 0;
953
+ if( p->pBaseline==0 ){
954
+ /* Manifest p is a baseline-manifest. Just scan down the list
955
+ ** of files. */
956
+ if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
957
+ }else{
958
+ /* Manifest p is a delta-manifest. Scan the baseline but amend the
959
+ ** file list in the baseline with changes described by p.
960
+ */
961
+ Manifest *pB = p->pBaseline;
962
+ int cmp;
963
+ while(1){
964
+ if( pB->iFile>=pB->nFile ){
965
+ /* We have used all entries out of the baseline. Return the next
966
+ ** entry from the delta. */
967
+ if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
968
+ break;
969
+ }else if( p->iFile>=p->nFile ){
970
+ /* We have used all entries from the delta. Return the next
971
+ ** entry from the baseline. */
972
+ if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++];
973
+ break;
974
+ }else if( (cmp = strcmp(pB->aFile[pB->iFile].zName,
975
+ p->aFile[p->iFile].zName)) < 0 ){
976
+ /* The next baseline entry comes before the next delta entry.
977
+ ** So return the baseline entry. */
978
+ pOut = &pB->aFile[pB->iFile++];
979
+ break;
980
+ }else if( cmp>0 ){
981
+ /* The next delta entry comes before the next baseline
982
+ ** entry so return the delta entry */
983
+ pOut = &p->aFile[p->iFile++];
984
+ break;
985
+ }else if( p->aFile[p->iFile].zUuid ){
986
+ /* The next delta entry is a replacement for the next baseline
987
+ ** entry. Skip the baseline entry and return the delta entry */
988
+ pB->iFile++;
989
+ pOut = &p->aFile[p->iFile++];
990
+ break;
991
+ }else{
992
+ /* The next delta entry is a delete of the next baseline
993
+ ** entry. Skip them both. Repeat the loop to find the next
994
+ ** non-delete entry. */
995
+ pB->iFile++;
996
+ p->iFile++;
997
+ continue;
998
+ }
999
+ }
7911000
}
1001
+ return pOut;
7921002
}
7931003
7941004
/*
7951005
** Translate a filename into a filename-id (fnid). Create a new fnid
7961006
** if no previously exists.
@@ -818,14 +1028,14 @@
8181028
** Add a single entry to the mlink table. Also add the filename to
8191029
** the filename table if it is not there already.
8201030
*/
8211031
static void add_one_mlink(
8221032
int mid, /* The record ID of the manifest */
823
- const char *zFromUuid, /* UUID for the mlink.pid field */
824
- const char *zToUuid, /* UUID for the mlink.fid field */
1033
+ const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */
1034
+ const char *zToUuid, /* UUID for the mlink.fid. "" to delele */
8251035
const char *zFilename, /* Filename */
826
- const char *zPrior /* Previous filename. NULL if unchanged */
1036
+ const char *zPrior /* Previous filename. NULL if unchanged */
8271037
){
8281038
int fnid, pfnid, pid, fid;
8291039
static Stmt s1;
8301040
8311041
fnid = filename_to_fnid(zFilename);
@@ -832,16 +1042,16 @@
8321042
if( zPrior==0 ){
8331043
pfnid = 0;
8341044
}else{
8351045
pfnid = filename_to_fnid(zPrior);
8361046
}
837
- if( zFromUuid==0 ){
1047
+ if( zFromUuid==0 || zFromUuid[0]==0 ){
8381048
pid = 0;
8391049
}else{
8401050
pid = uuid_to_rid(zFromUuid, 1);
8411051
}
842
- if( zToUuid==0 ){
1052
+ if( zToUuid==0 || zToUuid[0]==0 ){
8431053
fid = 0;
8441054
}else{
8451055
fid = uuid_to_rid(zToUuid, 1);
8461056
}
8471057
db_static_prepare(&s1,
@@ -858,33 +1068,83 @@
8581068
content_deltify(pid, fid, 0);
8591069
}
8601070
}
8611071
8621072
/*
863
-** Locate a file named zName in the aFile[] array of the given
864
-** manifest. We assume that filenames are in sorted order.
865
-** Use a binary search. Return turn the index of the matching
866
-** entry. Or return -1 if not found.
1073
+** Do a binary search to find a file in the p->aFile[] array.
1074
+**
1075
+** As an optimization, guess that the file we seek is at index p->iFile.
1076
+** That will usually be the case. If it is not found there, then do the
1077
+** actual binary search.
1078
+**
1079
+** Update p->iFile to be the index of the file that is found.
8671080
*/
868
-static int find_file_in_manifest(Manifest *p, const char *zName){
1081
+static ManifestFile *manifest_file_seek_base(Manifest *p, const char *zName){
8691082
int lwr, upr;
8701083
int c;
8711084
int i;
8721085
lwr = 0;
8731086
upr = p->nFile - 1;
1087
+ if( p->iFile>=lwr && p->iFile<upr ){
1088
+ c = strcmp(p->aFile[p->iFile+1].zName, zName);
1089
+ if( c==0 ){
1090
+ return &p->aFile[++p->iFile];
1091
+ }else if( c>0 ){
1092
+ upr = p->iFile;
1093
+ }else{
1094
+ lwr = p->iFile+1;
1095
+ }
1096
+ }
8741097
while( lwr<=upr ){
8751098
i = (lwr+upr)/2;
8761099
c = strcmp(p->aFile[i].zName, zName);
8771100
if( c<0 ){
8781101
lwr = i+1;
8791102
}else if( c>0 ){
8801103
upr = i-1;
8811104
}else{
882
- return i;
1105
+ p->iFile = i;
1106
+ return &p->aFile[i];
8831107
}
8841108
}
885
- return -1;
1109
+ return 0;
1110
+}
1111
+
1112
+/*
1113
+** Locate a file named zName in the aFile[] array of the given manifest.
1114
+** Return a pointer to the appropriate ManifestFile object. Return NULL
1115
+** if not found.
1116
+**
1117
+** This routine works even if p is a delta-manifest. The pointer
1118
+** returned might be to the baseline.
1119
+**
1120
+** We assume that filenames are in sorted order and use a binary search.
1121
+*/
1122
+ManifestFile *manifest_file_seek(Manifest *p, const char *zName){
1123
+ ManifestFile *pFile;
1124
+
1125
+ pFile = manifest_file_seek_base(p, zName);
1126
+ if( pFile && pFile->zUuid==0 ) return 0;
1127
+ if( pFile==0 && p->zBaseline ){
1128
+ fetch_baseline(p);
1129
+ pFile = manifest_file_seek_base(p->pBaseline, zName);
1130
+ }
1131
+ return pFile;
1132
+}
1133
+
1134
+/*
1135
+** This strcmp() function handles NULL arguments. NULLs sort first.
1136
+*/
1137
+static int strcmp_null(const char *zOne, const char *zTwo){
1138
+ if( zOne==0 ){
1139
+ if( zTwo==0 ) return 0;
1140
+ return -1;
1141
+ }else if( zTwo==0 ){
1142
+ return +1;
1143
+ }else{
1144
+ return strcmp(zOne, zTwo);
1145
+ }
8861146
}
8871147
8881148
/*
8891149
** Add mlink table entries associated with manifest cid. The
8901150
** parent manifest is pid.
@@ -895,89 +1155,70 @@
8951155
** Deleted files have mlink.fid=0.
8961156
** Added files have mlink.pid=0.
8971157
** Edited files have both mlink.pid!=0 and mlink.fid!=0
8981158
*/
8991159
static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
900
- Manifest other;
9011160
Blob otherContent;
9021161
int otherRid;
903
- int i, j;
1162
+ int i, rc;
1163
+ ManifestFile *pChildFile, *pParentFile;
1164
+ Manifest **ppOther;
1165
+ static Stmt eq;
9041166
905
- if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", cid) ){
906
- return;
907
- }
1167
+ db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid");
1168
+ db_bind_int(&eq, ":mid", cid);
1169
+ rc = db_step(&eq);
1170
+ db_reset(&eq);
1171
+ if( rc==SQLITE_ROW ) return;
1172
+
9081173
assert( pParent==0 || pChild==0 );
9091174
if( pParent==0 ){
910
- pParent = &other;
1175
+ ppOther = &pParent;
9111176
otherRid = pid;
9121177
}else{
913
- pChild = &other;
1178
+ ppOther = &pChild;
9141179
otherRid = cid;
9151180
}
916
- if( manifest_cache_find(otherRid, &other)==0 ){
1181
+ if( (*ppOther = manifest_cache_find(otherRid))==0 ){
9171182
content_get(otherRid, &otherContent);
9181183
if( blob_size(&otherContent)==0 ) return;
919
- if( manifest_parse(&other, &otherContent)==0 ) return;
920
- }
921
- content_deltify(pid, cid, 0);
922
-
923
- /* Use the iRename fields to find the cross-linkage between
924
- ** renamed files. */
925
- for(j=0; j<pChild->nFile; j++){
926
- const char *zPrior = pChild->aFile[j].zPrior;
927
- if( zPrior && zPrior[0] ){
928
- i = find_file_in_manifest(pParent, zPrior);
929
- if( i>=0 ){
930
- pChild->aFile[j].iRename = i;
931
- pParent->aFile[i].iRename = j;
932
- }
933
- }
934
- }
935
-
936
- /* Construct the mlink entries */
937
- for(i=j=0; i<pParent->nFile && j<pChild->nFile; ){
938
- int c;
939
- if( pParent->aFile[i].iRename>=0 ){
940
- i++;
941
- }else if( (c = strcmp(pParent->aFile[i].zName, pChild->aFile[j].zName))<0 ){
942
- add_one_mlink(cid, pParent->aFile[i].zUuid,0,pParent->aFile[i].zName,0);
943
- i++;
944
- }else if( c>0 ){
945
- int rn = pChild->aFile[j].iRename;
946
- if( rn>=0 ){
947
- add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid,
948
- pChild->aFile[j].zName, pParent->aFile[rn].zName);
949
- }else{
950
- add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
951
- }
952
- j++;
953
- }else{
954
- if( strcmp(pParent->aFile[i].zUuid, pChild->aFile[j].zUuid)!=0 ){
955
- add_one_mlink(cid, pParent->aFile[i].zUuid, pChild->aFile[j].zUuid,
956
- pChild->aFile[j].zName, 0);
957
- }
958
- i++;
959
- j++;
960
- }
961
- }
962
- while( i<pParent->nFile ){
963
- if( pParent->aFile[i].iRename<0 ){
964
- add_one_mlink(cid, pParent->aFile[i].zUuid, 0, pParent->aFile[i].zName,0);
965
- }
966
- i++;
967
- }
968
- while( j<pChild->nFile ){
969
- int rn = pChild->aFile[j].iRename;
970
- if( rn>=0 ){
971
- add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid,
972
- pChild->aFile[j].zName, pParent->aFile[rn].zName);
973
- }else{
974
- add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
975
- }
976
- j++;
977
- }
978
- manifest_cache_insert(otherRid, &other);
1184
+ *ppOther = manifest_parse(&otherContent, otherRid);
1185
+ if( *ppOther==0 ) return;
1186
+ }
1187
+ if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){
1188
+ content_deltify(pid, cid, 0);
1189
+ }
1190
+
1191
+ for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){
1192
+ if( pChildFile->zPrior ){
1193
+ pParentFile = manifest_file_seek(pParent, pChildFile->zPrior);
1194
+ if( pParentFile ){
1195
+ add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1196
+ pChildFile->zName, pChildFile->zPrior);
1197
+ }
1198
+ }else{
1199
+ pParentFile = manifest_file_seek(pParent, pChildFile->zName);
1200
+ if( pParentFile==0 ){
1201
+ if( pChildFile->zUuid ){
1202
+ add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0);
1203
+ }
1204
+ }else if( strcmp_null(pChildFile->zUuid, pParentFile->zUuid)!=0 ){
1205
+ add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1206
+ pChildFile->zName, 0);
1207
+ }
1208
+ }
1209
+ }
1210
+ if( pParent->zBaseline && pChild->zBaseline ){
1211
+ for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){
1212
+ if( pParentFile->zUuid ) continue;
1213
+ pChildFile = manifest_file_seek(pChild, pParentFile->zName);
1214
+ if( pChildFile ){
1215
+ add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0);
1216
+ }
1217
+ }
1218
+ }
1219
+ manifest_cache_insert(*ppOther);
9791220
}
9801221
9811222
/*
9821223
** True if manifest_crosslink_begin() has been called but
9831224
** manifest_crosslink_end() is still pending.
@@ -1114,40 +1355,40 @@
11141355
** of the routine, "manifest_crosslink", and the name of this source
11151356
** file, is a legacy of its original use.
11161357
*/
11171358
int manifest_crosslink(int rid, Blob *pContent){
11181359
int i;
1119
- Manifest m;
1360
+ Manifest *p;
11201361
Stmt q;
11211362
int parentid = 0;
11221363
1123
- if( manifest_cache_find(rid, &m) ){
1364
+ if( (p = manifest_cache_find(rid))!=0 ){
11241365
blob_reset(pContent);
1125
- }else if( manifest_parse(&m, pContent)==0 ){
1366
+ }else if( (p = manifest_parse(pContent, rid))==0 ){
11261367
return 0;
11271368
}
1128
- if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){
1129
- manifest_clear(&m);
1369
+ if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
1370
+ manifest_destroy(p);
11301371
return 0;
11311372
}
11321373
db_begin_transaction();
1133
- if( m.type==CFTYPE_MANIFEST ){
1374
+ if( p->type==CFTYPE_MANIFEST ){
11341375
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
11351376
char *zCom;
1136
- for(i=0; i<m.nParent; i++){
1137
- int pid = uuid_to_rid(m.azParent[i], 1);
1377
+ for(i=0; i<p->nParent; i++){
1378
+ int pid = uuid_to_rid(p->azParent[i], 1);
11381379
db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
1139
- "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
1380
+ "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate);
11401381
if( i==0 ){
1141
- add_mlink(pid, 0, rid, &m);
1382
+ add_mlink(pid, 0, rid, p);
11421383
parentid = pid;
11431384
}
11441385
}
11451386
db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
11461387
while( db_step(&q)==SQLITE_ROW ){
11471388
int cid = db_column_int(&q, 0);
1148
- add_mlink(rid, &m, cid, 0);
1389
+ add_mlink(rid, p, cid, 0);
11491390
}
11501391
db_finalize(&q);
11511392
db_multi_exec(
11521393
"REPLACE INTO event(type,mtime,objid,user,comment,"
11531394
"bgcolor,euser,ecomment)"
@@ -1158,118 +1399,130 @@
11581399
" ),"
11591400
" %d,%Q,%Q,"
11601401
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0),"
11611402
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
11621403
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1163
- TAG_DATE, rid, m.rDate,
1164
- rid, m.zUser, m.zComment,
1404
+ TAG_DATE, rid, p->rDate,
1405
+ rid, p->zUser, p->zComment,
11651406
TAG_BGCOLOR, rid,
11661407
TAG_USER, rid,
11671408
TAG_COMMENT, rid
11681409
);
11691410
zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
11701411
" WHERE rowid=last_insert_rowid()");
1171
- wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE);
1412
+ wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE);
11721413
free(zCom);
1414
+
1415
+ /* If this is a delta-manifest, record the fact that this repository
1416
+ ** contains delta manifests, to free the "commit" logic to generate
1417
+ ** new delta manifests.
1418
+ */
1419
+ if( p->zBaseline!=0 ){
1420
+ static int once = 0;
1421
+ if( !once ){
1422
+ db_set_int("seen-delta-manifest", 1, 0);
1423
+ once = 0;
1424
+ }
1425
+ }
11731426
}
11741427
}
1175
- if( m.type==CFTYPE_CLUSTER ){
1176
- tag_insert("cluster", 1, 0, rid, m.rDate, rid);
1177
- for(i=0; i<m.nCChild; i++){
1428
+ if( p->type==CFTYPE_CLUSTER ){
1429
+ tag_insert("cluster", 1, 0, rid, p->rDate, rid);
1430
+ for(i=0; i<p->nCChild; i++){
11781431
int mid;
1179
- mid = uuid_to_rid(m.azCChild[i], 1);
1432
+ mid = uuid_to_rid(p->azCChild[i], 1);
11801433
if( mid>0 ){
11811434
db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid);
11821435
}
11831436
}
11841437
}
1185
- if( m.type==CFTYPE_CONTROL
1186
- || m.type==CFTYPE_MANIFEST
1187
- || m.type==CFTYPE_EVENT
1438
+ if( p->type==CFTYPE_CONTROL
1439
+ || p->type==CFTYPE_MANIFEST
1440
+ || p->type==CFTYPE_EVENT
11881441
){
1189
- for(i=0; i<m.nTag; i++){
1442
+ for(i=0; i<p->nTag; i++){
11901443
int tid;
11911444
int type;
1192
- if( m.aTag[i].zUuid ){
1193
- tid = uuid_to_rid(m.aTag[i].zUuid, 1);
1445
+ if( p->aTag[i].zUuid ){
1446
+ tid = uuid_to_rid(p->aTag[i].zUuid, 1);
11941447
}else{
11951448
tid = rid;
11961449
}
11971450
if( tid ){
1198
- switch( m.aTag[i].zName[0] ){
1451
+ switch( p->aTag[i].zName[0] ){
11991452
case '-': type = 0; break; /* Cancel prior occurances */
12001453
case '+': type = 1; break; /* Apply to target only */
12011454
case '*': type = 2; break; /* Propagate to descendants */
12021455
default:
1203
- fossil_fatal("unknown tag type in manifest: %s", m.aTag);
1456
+ fossil_fatal("unknown tag type in manifest: %s", p->aTag);
12041457
return 0;
12051458
}
1206
- tag_insert(&m.aTag[i].zName[1], type, m.aTag[i].zValue,
1207
- rid, m.rDate, tid);
1459
+ tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue,
1460
+ rid, p->rDate, tid);
12081461
}
12091462
}
12101463
if( parentid ){
12111464
tag_propagate_all(parentid);
12121465
}
12131466
}
1214
- if( m.type==CFTYPE_WIKI ){
1215
- char *zTag = mprintf("wiki-%s", m.zWikiTitle);
1467
+ if( p->type==CFTYPE_WIKI ){
1468
+ char *zTag = mprintf("wiki-%s", p->zWikiTitle);
12161469
int tagid = tag_findid(zTag, 1);
12171470
int prior;
12181471
char *zComment;
12191472
int nWiki;
12201473
char zLength[40];
1221
- while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
1222
- nWiki = strlen(m.zWiki);
1474
+ while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
1475
+ nWiki = strlen(p->zWiki);
12231476
sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1224
- tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
1477
+ tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
12251478
free(zTag);
12261479
prior = db_int(0,
12271480
"SELECT rid FROM tagxref"
12281481
" WHERE tagid=%d AND mtime<%.17g"
12291482
" ORDER BY mtime DESC",
1230
- tagid, m.rDate
1483
+ tagid, p->rDate
12311484
);
12321485
if( prior ){
12331486
content_deltify(prior, rid, 0);
12341487
}
12351488
if( nWiki>0 ){
1236
- zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1489
+ zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
12371490
}else{
1238
- zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
1491
+ zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
12391492
}
12401493
db_multi_exec(
12411494
"REPLACE INTO event(type,mtime,objid,user,comment,"
12421495
" bgcolor,euser,ecomment)"
12431496
"VALUES('w',%.17g,%d,%Q,%Q,"
12441497
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
12451498
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
12461499
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1247
- m.rDate, rid, m.zUser, zComment,
1500
+ p->rDate, rid, p->zUser, zComment,
12481501
TAG_BGCOLOR, rid,
12491502
TAG_BGCOLOR, rid,
12501503
TAG_USER, rid,
12511504
TAG_COMMENT, rid
12521505
);
12531506
free(zComment);
12541507
}
1255
- if( m.type==CFTYPE_EVENT ){
1256
- char *zTag = mprintf("event-%s", m.zEventId);
1508
+ if( p->type==CFTYPE_EVENT ){
1509
+ char *zTag = mprintf("event-%s", p->zEventId);
12571510
int tagid = tag_findid(zTag, 1);
12581511
int prior, subsequent;
12591512
int nWiki;
12601513
char zLength[40];
1261
- while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
1262
- nWiki = strlen(m.zWiki);
1514
+ while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
1515
+ nWiki = strlen(p->zWiki);
12631516
sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1264
- tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
1517
+ tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
12651518
free(zTag);
12661519
prior = db_int(0,
12671520
"SELECT rid FROM tagxref"
12681521
" WHERE tagid=%d AND mtime<%.17g"
12691522
" ORDER BY mtime DESC",
1270
- tagid, m.rDate
1523
+ tagid, p->rDate
12711524
);
12721525
if( prior ){
12731526
content_deltify(prior, rid, 0);
12741527
db_multi_exec(
12751528
"DELETE FROM event"
@@ -1281,108 +1534,87 @@
12811534
}
12821535
subsequent = db_int(0,
12831536
"SELECT rid FROM tagxref"
12841537
" WHERE tagid=%d AND mtime>%.17g"
12851538
" ORDER BY mtime",
1286
- tagid, m.rDate
1539
+ tagid, p->rDate
12871540
);
12881541
if( subsequent ){
12891542
content_deltify(rid, subsequent, 0);
12901543
}else{
12911544
db_multi_exec(
12921545
"REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
12931546
"VALUES('e',%.17g,%d,%d,%Q,%Q,"
12941547
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1295
- m.rEventDate, rid, tagid, m.zUser, m.zComment,
1548
+ p->rEventDate, rid, tagid, p->zUser, p->zComment,
12961549
TAG_BGCOLOR, rid
12971550
);
12981551
}
12991552
}
1300
- if( m.type==CFTYPE_TICKET ){
1553
+ if( p->type==CFTYPE_TICKET ){
13011554
char *zTag;
13021555
13031556
assert( manifest_crosslink_busy==1 );
1304
- zTag = mprintf("tkt-%s", m.zTicketUuid);
1305
- tag_insert(zTag, 1, 0, rid, m.rDate, rid);
1557
+ zTag = mprintf("tkt-%s", p->zTicketUuid);
1558
+ tag_insert(zTag, 1, 0, rid, p->rDate, rid);
13061559
free(zTag);
13071560
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
1308
- m.zTicketUuid);
1561
+ p->zTicketUuid);
13091562
}
1310
- if( m.type==CFTYPE_ATTACHMENT ){
1563
+ if( p->type==CFTYPE_ATTACHMENT ){
13111564
db_multi_exec(
13121565
"INSERT INTO attachment(attachid, mtime, src, target,"
13131566
"filename, comment, user)"
13141567
"VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
1315
- rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
1316
- (m.zComment ? m.zComment : ""), m.zUser
1568
+ rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
1569
+ (p->zComment ? p->zComment : ""), p->zUser
13171570
);
13181571
db_multi_exec(
13191572
"UPDATE attachment SET isLatest = (mtime=="
13201573
"(SELECT max(mtime) FROM attachment"
13211574
" WHERE target=%Q AND filename=%Q))"
13221575
" WHERE target=%Q AND filename=%Q",
1323
- m.zAttachTarget, m.zAttachName,
1324
- m.zAttachTarget, m.zAttachName
1576
+ p->zAttachTarget, p->zAttachName,
1577
+ p->zAttachTarget, p->zAttachName
13251578
);
1326
- if( strlen(m.zAttachTarget)!=UUID_SIZE
1327
- || !validate16(m.zAttachTarget, UUID_SIZE)
1579
+ if( strlen(p->zAttachTarget)!=UUID_SIZE
1580
+ || !validate16(p->zAttachTarget, UUID_SIZE)
13281581
){
13291582
char *zComment;
1330
- if( m.zAttachSrc && m.zAttachSrc[0] ){
1583
+ if( p->zAttachSrc && p->zAttachSrc[0] ){
13311584
zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
1332
- m.zAttachName, m.zAttachTarget);
1585
+ p->zAttachName, p->zAttachTarget);
13331586
}else{
13341587
zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
1335
- m.zAttachName, m.zAttachTarget);
1588
+ p->zAttachName, p->zAttachTarget);
13361589
}
13371590
db_multi_exec(
13381591
"REPLACE INTO event(type,mtime,objid,user,comment)"
13391592
"VALUES('w',%.17g,%d,%Q,%Q)",
1340
- m.rDate, rid, m.zUser, zComment
1593
+ p->rDate, rid, p->zUser, zComment
13411594
);
13421595
free(zComment);
13431596
}else{
13441597
char *zComment;
1345
- if( m.zAttachSrc && m.zAttachSrc[0] ){
1598
+ if( p->zAttachSrc && p->zAttachSrc[0] ){
13461599
zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
1347
- m.zAttachName, m.zAttachTarget);
1600
+ p->zAttachName, p->zAttachTarget);
13481601
}else{
13491602
zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
1350
- m.zAttachName, m.zAttachTarget);
1603
+ p->zAttachName, p->zAttachTarget);
13511604
}
13521605
db_multi_exec(
13531606
"REPLACE INTO event(type,mtime,objid,user,comment)"
13541607
"VALUES('t',%.17g,%d,%Q,%Q)",
1355
- m.rDate, rid, m.zUser, zComment
1608
+ p->rDate, rid, p->zUser, zComment
13561609
);
13571610
free(zComment);
13581611
}
13591612
}
13601613
db_end_transaction(0);
1361
- if( m.type==CFTYPE_MANIFEST ){
1362
- manifest_cache_insert(rid, &m);
1614
+ if( p->type==CFTYPE_MANIFEST ){
1615
+ manifest_cache_insert(p);
13631616
}else{
1364
- manifest_clear(&m);
1617
+ manifest_destroy(p);
13651618
}
13661619
return 1;
13671620
}
1368
-
1369
-/*
1370
-** Given a checkin name, load and parse the manifest for that checkin.
1371
-** Throw a fatal error if anything goes wrong.
1372
-*/
1373
-void manifest_from_name(
1374
- const char *zName,
1375
- Manifest *pM
1376
-){
1377
- int rid;
1378
- Blob content;
1379
-
1380
- rid = name_to_rid(zName);
1381
- if( !is_a_version(rid) ){
1382
- fossil_fatal("no such checkin: %s", zName);
1383
- }
1384
- content_get(rid, &content);
1385
- if( !manifest_parse(pM, &content) ){
1386
- fossil_fatal("cannot parse manifest for checkin: %s", zName);
1387
- }
1388
-}
13891621
--- src/manifest.c
+++ src/manifest.c
@@ -26,24 +26,39 @@
26
27 #if INTERFACE
28 /*
29 ** Types of control files
30 */
 
31 #define CFTYPE_MANIFEST 1
32 #define CFTYPE_CLUSTER 2
33 #define CFTYPE_CONTROL 3
34 #define CFTYPE_WIKI 4
35 #define CFTYPE_TICKET 5
36 #define CFTYPE_ATTACHMENT 6
37 #define CFTYPE_EVENT 7
 
 
 
 
 
 
 
 
 
 
 
38
39 /*
40 ** A parsed manifest or cluster.
41 */
42 struct Manifest {
43 Blob content; /* The original content blob */
44 int type; /* Type of artifact. One of CFTYPE_xxxxx */
 
 
 
45 char *zComment; /* Decoded comment. The C card. */
46 double rDate; /* Date and time from D card. 0.0 if no D card. */
47 char *zUser; /* Name of the user from the U card. */
48 char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
49 char *zWiki; /* Text of the wiki page. W card. */
@@ -54,17 +69,12 @@
54 char *zAttachName; /* Filename of an attachment. A card. */
55 char *zAttachSrc; /* UUID of document being attached. A card. */
56 char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
57 int nFile; /* Number of F cards */
58 int nFileAlloc; /* Slots allocated in aFile[] */
59 struct ManifestFile {
60 char *zName; /* Name of a file */
61 char *zUuid; /* UUID of the file */
62 char *zPerm; /* File permissions */
63 char *zPrior; /* Prior name if the name was changed */
64 int iRename; /* index of renamed name in prior/next manifest */
65 } *aFile; /* One entry for each F card */
66 int nParent; /* Number of parents. */
67 int nParentAlloc; /* Slots allocated in azParent[] */
68 char **azParent; /* UUIDs of parents. One for each P card argument */
69 int nCChild; /* Number of cluster children */
70 int nCChildAlloc; /* Number of closts allocated in azCChild[] */
@@ -87,68 +97,75 @@
87
88 /*
89 ** A cache of parsed manifests. This reduces the number of
90 ** calls to manifest_parse() when doing a rebuild.
91 */
92 #define MX_MANIFEST_CACHE 4
93 static struct {
94 int nxAge;
95 int aRid[MX_MANIFEST_CACHE];
96 int aAge[MX_MANIFEST_CACHE];
97 Manifest aLine[MX_MANIFEST_CACHE];
98 } manifestCache;
99
100
101 /*
102 ** Clear the memory allocated in a manifest object
103 */
104 void manifest_clear(Manifest *p){
105 blob_reset(&p->content);
106 free(p->aFile);
107 free(p->azParent);
108 free(p->azCChild);
109 free(p->aTag);
110 free(p->aField);
111 memset(p, 0, sizeof(*p));
 
 
 
112 }
113
114 /*
115 ** Add an element to the manifest cache using LRU replacement.
116 */
117 void manifest_cache_insert(int rid, Manifest *p){
118 int i;
119 for(i=0; i<MX_MANIFEST_CACHE; i++){
120 if( manifestCache.aRid[i]==0 ) break;
121 }
122 if( i>=MX_MANIFEST_CACHE ){
123 int oldest = 0;
124 int oldestAge = manifestCache.aAge[0];
125 for(i=1; i<MX_MANIFEST_CACHE; i++){
126 if( manifestCache.aAge[i]<oldestAge ){
127 oldest = i;
128 oldestAge = manifestCache.aAge[i];
129 }
130 }
131 manifest_clear(&manifestCache.aLine[oldest]);
132 i = oldest;
133 }
134 manifestCache.aAge[i] = ++manifestCache.nxAge;
135 manifestCache.aRid[i] = rid;
136 manifestCache.aLine[i] = *p;
 
 
 
 
137 }
138
139 /*
140 ** Try to extract a line from the manifest cache. Return 1 if found.
141 ** Return 0 if not found.
142 */
143 int manifest_cache_find(int rid, Manifest *p){
144 int i;
 
145 for(i=0; i<MX_MANIFEST_CACHE; i++){
146 if( manifestCache.aRid[i]==rid ){
147 *p = manifestCache.aLine[i];
148 manifestCache.aRid[i] = 0;
149 return 1;
150 }
151 }
152 return 0;
153 }
154
@@ -156,21 +173,113 @@
156 ** Clear the manifest cache.
157 */
158 void manifest_cache_clear(void){
159 int i;
160 for(i=0; i<MX_MANIFEST_CACHE; i++){
161 if( manifestCache.aRid[i]>0 ){
162 manifest_clear(&manifestCache.aLine[i]);
163 }
164 }
165 memset(&manifestCache, 0, sizeof(manifestCache));
166 }
167
168 #ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
169 # define md5sum_init(X)
170 # define md5sum_step_text(X,Y)
171 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
173 /*
174 ** Parse a blob into a Manifest object. The Manifest object
175 ** takes over the input blob and will free it when the
176 ** Manifest object is freed. Zeros are inserted into the blob
@@ -195,56 +304,66 @@
195 ** Each card is divided into tokens by a single space character.
196 ** The first token is a single upper-case letter which is the card type.
197 ** The card type determines the other parameters to the card.
198 ** Cards must occur in lexicographical order.
199 */
200 int manifest_parse(Manifest *p, Blob *pContent){
201 int seenHeader = 0;
202 int seenZ = 0;
203 int i, lineNo=0;
204 Blob line, token, a1, a2, a3, a4;
205 char cPrevType = 0;
 
 
 
 
 
206
207 /* Every control artifact ends with a '\n' character. Exit early
208 ** if that is not the case for this artifact. */
209 i = blob_size(pContent);
210 if( i<=0 || blob_buffer(pContent)[i-1]!='\n' ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211 blob_reset(pContent);
212 return 0;
213 }
214
 
 
 
215 memset(p, 0, sizeof(*p));
216 memcpy(&p->content, pContent, sizeof(p->content));
 
217 blob_zero(pContent);
218 pContent = &p->content;
219
220 blob_zero(&a1);
221 blob_zero(&a2);
222 blob_zero(&a3);
223 md5sum_init();
224 while( blob_line(pContent, &line) ){
225 char *z = blob_buffer(&line);
226 lineNo++;
227 if( z[0]=='-' ){
228 if( strncmp(z, "-----BEGIN PGP ", 15)!=0 ){
229 goto manifest_syntax_error;
230 }
231 if( seenHeader ){
232 break;
233 }
234 while( blob_line(pContent, &line)>2 ){}
235 if( blob_line(pContent, &line)==0 ) break;
236 z = blob_buffer(&line);
237 }
238 if( z[0]<cPrevType ){
239 /* Lines of a manifest must occur in lexicographical order */
240 goto manifest_syntax_error;
241 }
242 cPrevType = z[0];
243 seenHeader = 1;
244 if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
245 switch( z[0] ){
246 /*
247 ** A <filename> <target> ?<source>?
248 **
249 ** Identifies an attachment to either a wiki page or a ticket.
250 ** <source> is the artifact that is the attachment. <source>
@@ -251,50 +370,60 @@
251 ** is omitted to delete an attachment. <target> is the name of
252 ** a wiki page or ticket to which that attachment is connected.
253 */
254 case 'A': {
255 char *zName, *zTarget, *zSrc;
256 md5sum_step_text(blob_buffer(&line), blob_size(&line));
257 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
258 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
 
 
259 if( p->zAttachName!=0 ) goto manifest_syntax_error;
260 zName = blob_terminate(&a1);
261 zTarget = blob_terminate(&a2);
262 blob_token(&line, &a3);
263 zSrc = blob_terminate(&a3);
264 defossilize(zName);
265 if( !file_is_simple_pathname(zName) ){
266 goto manifest_syntax_error;
267 }
268 defossilize(zTarget);
269 if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
270 && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
271 goto manifest_syntax_error;
272 }
273 if( blob_size(&a3)>0
274 && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
275 goto manifest_syntax_error;
276 }
277 p->zAttachName = (char*)file_tail(zName);
278 p->zAttachSrc = zSrc;
279 p->zAttachTarget = zTarget;
280 break;
281 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
283 /*
284 ** C <comment>
285 **
286 ** Comment text is fossil-encoded. There may be no more than
287 ** one C line. C lines are required for manifests and are
288 ** disallowed on all other control files.
289 */
290 case 'C': {
291 md5sum_step_text(blob_buffer(&line), blob_size(&line));
292 if( p->zComment!=0 ) goto manifest_syntax_error;
293 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
294 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
295 p->zComment = blob_terminate(&a1);
296 defossilize(p->zComment);
297 break;
298 }
299
300 /*
@@ -303,17 +432,13 @@
303 ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS
304 ** There can be no more than 1 D line. D lines are required
305 ** for all control files except for clusters.
306 */
307 case 'D': {
308 char *zDate;
309 md5sum_step_text(blob_buffer(&line), blob_size(&line));
310 if( p->rDate!=0.0 ) goto manifest_syntax_error;
311 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
312 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
313 zDate = blob_terminate(&a1);
314 p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
315 break;
316 }
317
318 /*
319 ** E <timestamp> <uuid>
@@ -323,56 +448,46 @@
323 ** The event timestamp is distinct from the D timestamp. The D
324 ** timestamp is when the artifact was created whereas the E timestamp
325 ** is when the specific event is said to occur.
326 */
327 case 'E': {
328 char *zEDate;
329 md5sum_step_text(blob_buffer(&line), blob_size(&line));
330 if( p->rEventDate!=0.0 ) goto manifest_syntax_error;
331 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
332 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
333 if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
334 zEDate = blob_terminate(&a1);
335 p->rEventDate = db_double(0.0, "SELECT julianday(%Q)", zEDate);
336 if( p->rEventDate<=0.0 ) goto manifest_syntax_error;
337 if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
338 p->zEventId = blob_terminate(&a2);
339 if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error;
340 break;
341 }
342
343 /*
344 ** F <filename> <uuid> ?<permissions>? ?<old-name>?
345 **
346 ** Identifies a file in a manifest. Multiple F lines are
347 ** allowed in a manifest. F lines are not allowed in any
348 ** other control file. The filename and old-name are fossil-encoded.
349 */
350 case 'F': {
351 char *zName, *zUuid, *zPerm, *zPriorName;
352 md5sum_step_text(blob_buffer(&line), blob_size(&line));
353 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
354 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
355 zName = blob_terminate(&a1);
356 zUuid = blob_terminate(&a2);
357 blob_token(&line, &a3);
358 zPerm = blob_terminate(&a3);
359 if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
360 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
361 defossilize(zName);
362 if( !file_is_simple_pathname(zName) ){
363 goto manifest_syntax_error;
364 }
365 blob_token(&line, &a4);
366 zPriorName = blob_terminate(&a4);
367 if( zPriorName[0] ){
 
 
 
 
 
368 defossilize(zPriorName);
369 if( !file_is_simple_pathname(zPriorName) ){
370 goto manifest_syntax_error;
371 }
372 }else{
373 zPriorName = 0;
374 }
375 if( p->nFile>=p->nFileAlloc ){
376 p->nFileAlloc = p->nFileAlloc*2 + 10;
377 p->aFile = fossil_realloc(p->aFile,
378 p->nFileAlloc*sizeof(p->aFile[0]) );
@@ -380,11 +495,10 @@
380 i = p->nFile++;
381 p->aFile[i].zName = zName;
382 p->aFile[i].zUuid = zUuid;
383 p->aFile[i].zPerm = zPerm;
384 p->aFile[i].zPrior = zPriorName;
385 p->aFile[i].iRename = -1;
386 if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
387 goto manifest_syntax_error;
388 }
389 break;
390 }
@@ -397,16 +511,14 @@
397 ** value. If <value> is omitted then it is understood to be an
398 ** empty string.
399 */
400 case 'J': {
401 char *zName, *zValue;
402 md5sum_step_text(blob_buffer(&line), blob_size(&line));
403 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
404 blob_token(&line, &a2);
405 if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
406 zName = blob_terminate(&a1);
407 zValue = blob_terminate(&a2);
408 defossilize(zValue);
409 if( p->nField>=p->nFieldAlloc ){
410 p->nFieldAlloc = p->nFieldAlloc*2 + 10;
411 p->aField = fossil_realloc(p->aField,
412 p->nFieldAlloc*sizeof(p->aField[0]) );
@@ -426,18 +538,14 @@
426 **
427 ** A K-line gives the UUID for the ticket which this control file
428 ** is amending.
429 */
430 case 'K': {
431 char *zUuid;
432 md5sum_step_text(blob_buffer(&line), blob_size(&line));
433 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
434 zUuid = blob_terminate(&a1);
435 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
436 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
437 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
438 p->zTicketUuid = zUuid;
 
 
439 break;
440 }
441
442 /*
443 ** L <wikititle>
@@ -444,15 +552,13 @@
444 **
445 ** The wiki page title is fossil-encoded. There may be no more than
446 ** one L line.
447 */
448 case 'L': {
449 md5sum_step_text(blob_buffer(&line), blob_size(&line));
450 if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
451 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
452 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
453 p->zWikiTitle = blob_terminate(&a1);
454 defossilize(p->zWikiTitle);
455 if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
456 goto manifest_syntax_error;
457 }
458 break;
@@ -463,15 +569,13 @@
463 **
464 ** An M-line identifies another artifact by its UUID. M-lines
465 ** occur in clusters only.
466 */
467 case 'M': {
468 char *zUuid;
469 md5sum_step_text(blob_buffer(&line), blob_size(&line));
470 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
471 zUuid = blob_terminate(&a1);
472 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
473 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
474 if( p->nCChild>=p->nCChildAlloc ){
475 p->nCChildAlloc = p->nCChildAlloc*2 + 10;
476 p->azCChild = fossil_realloc(p->azCChild
477 , p->nCChildAlloc*sizeof(p->azCChild[0]) );
@@ -490,15 +594,12 @@
490 ** Specify one or more other artifacts where are the parents of
491 ** this artifact. The first parent is the primary parent. All
492 ** others are parents by merge.
493 */
494 case 'P': {
495 md5sum_step_text(blob_buffer(&line), blob_size(&line));
496 while( blob_token(&line, &a1) ){
497 char *zUuid;
498 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
499 zUuid = blob_terminate(&a1);
500 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
501 if( p->nParent>=p->nParentAlloc ){
502 p->nParentAlloc = p->nParentAlloc*2 + 5;
503 p->azParent = fossil_realloc(p->azParent,
504 p->nParentAlloc*sizeof(char*));
@@ -514,16 +615,13 @@
514 **
515 ** Specify the MD5 checksum over the name and content of all files
516 ** in the manifest.
517 */
518 case 'R': {
519 md5sum_step_text(blob_buffer(&line), blob_size(&line));
520 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
521 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
522 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
523 if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
524 p->zRepoCksum = blob_terminate(&a1);
525 if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
526 break;
527 }
528
529 /*
@@ -540,29 +638,20 @@
540 ** the tag is really a property with the given value.
541 **
542 ** Tags are not allowed in clusters. Multiple T lines are allowed.
543 */
544 case 'T': {
545 char *zName, *zUuid, *zValue;
546 md5sum_step_text(blob_buffer(&line), blob_size(&line));
547 if( blob_token(&line, &a1)==0 ){
548 goto manifest_syntax_error;
549 }
550 if( blob_token(&line, &a2)==0 ){
551 goto manifest_syntax_error;
552 }
553 zName = blob_terminate(&a1);
554 zUuid = blob_terminate(&a2);
555 if( blob_token(&line, &a3)==0 ){
556 zValue = 0;
557 }else{
558 zValue = blob_terminate(&a3);
559 defossilize(zValue);
560 }
561 if( blob_size(&a2)==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
562 /* A valid uuid */
563 }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){
564 zUuid = 0;
565 }else{
566 goto manifest_syntax_error;
567 }
568 defossilize(zName);
@@ -593,19 +682,17 @@
593 ** Identify the user who created this control file by their
594 ** login. Only one U line is allowed. Prohibited in clusters.
595 ** If the user name is omitted, take that to be "anonymous".
596 */
597 case 'U': {
598 md5sum_step_text(blob_buffer(&line), blob_size(&line));
599 if( p->zUser!=0 ) goto manifest_syntax_error;
600 if( blob_token(&line, &a1)==0 ){
 
601 p->zUser = "anonymous";
602 }else{
603 p->zUser = blob_terminate(&a1);
604 defossilize(p->zUser);
605 }
606 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
607 break;
608 }
609
610 /*
611 ** W <size>
@@ -613,26 +700,28 @@
613 ** The next <size> bytes of the file contain the text of the wiki
614 ** page. There is always an extra \n before the start of the next
615 ** record.
616 */
617 case 'W': {
618 int size;
 
619 Blob wiki;
620 md5sum_step_text(blob_buffer(&line), blob_size(&line));
621 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
622 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
623 if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error;
 
 
624 if( size<0 ) goto manifest_syntax_error;
625 if( p->zWiki!=0 ) goto manifest_syntax_error;
626 blob_zero(&wiki);
627 if( blob_extract(pContent, size+1, &wiki)!=size+1 ){
628 goto manifest_syntax_error;
629 }
630 p->zWiki = blob_buffer(&wiki);
631 md5sum_step_text(p->zWiki, size+1);
632 if( p->zWiki[size]!='\n' ) goto manifest_syntax_error;
633 p->zWiki[size] = 0;
634 break;
635 }
636
637
638 /*
@@ -645,35 +734,24 @@
645 ** This card is required for all control file types except for
646 ** Manifest. It is not required for manifest only for historical
647 ** compatibility reasons.
648 */
649 case 'Z': {
650 #ifndef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
651 int rc;
652 Blob hash;
653 #endif
654 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
655 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
656 if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
657 if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
658 #ifndef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
659 md5sum_finish(&hash);
660 rc = blob_compare(&hash, &a1);
661 blob_reset(&hash);
662 if( rc!=0 ) goto manifest_syntax_error;
663 #endif
664 seenZ = 1;
665 break;
666 }
667 default: {
668 goto manifest_syntax_error;
669 }
670 }
671 }
672 if( !seenHeader ) goto manifest_syntax_error;
673
674 if( p->nFile>0 || p->zRepoCksum!=0 ){
675 if( p->nCChild>0 ) goto manifest_syntax_error;
676 if( p->rDate<=0.0 ) goto manifest_syntax_error;
677 if( p->nField>0 ) goto manifest_syntax_error;
678 if( p->zTicketUuid ) goto manifest_syntax_error;
679 if( p->zWiki ) goto manifest_syntax_error;
@@ -754,43 +832,175 @@
754 if( p->zWikiTitle ) goto manifest_syntax_error;
755 if( p->zTicketUuid ) goto manifest_syntax_error;
756 p->type = CFTYPE_MANIFEST;
757 }
758 md5sum_init();
759 return 1;
760
761 manifest_syntax_error:
762 /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
763 md5sum_init();
764 manifest_clear(p);
765 return 0;
766 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
768 /*
769 ** COMMAND: test-parse-manifest
770 **
771 ** Usage: %fossil test-parse-manifest FILENAME ?N?
772 **
773 ** Parse the manifest and discarded. Use for testing only.
774 */
775 void manifest_test_parse_cmd(void){
776 Manifest m;
777 Blob b;
778 int i;
779 int n = 1;
 
780 if( g.argc!=3 && g.argc!=4 ){
781 usage("FILENAME");
782 }
783 db_must_be_within_tree();
784 blob_read_from_file(&b, g.argv[2]);
785 if( g.argc>3 ) n = atoi(g.argv[3]);
786 for(i=0; i<n; i++){
787 Blob b2;
788 blob_copy(&b2, &b);
789 manifest_parse(&m, &b2);
790 manifest_clear(&m);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791 }
 
792 }
793
794 /*
795 ** Translate a filename into a filename-id (fnid). Create a new fnid
796 ** if no previously exists.
@@ -818,14 +1028,14 @@
818 ** Add a single entry to the mlink table. Also add the filename to
819 ** the filename table if it is not there already.
820 */
821 static void add_one_mlink(
822 int mid, /* The record ID of the manifest */
823 const char *zFromUuid, /* UUID for the mlink.pid field */
824 const char *zToUuid, /* UUID for the mlink.fid field */
825 const char *zFilename, /* Filename */
826 const char *zPrior /* Previous filename. NULL if unchanged */
827 ){
828 int fnid, pfnid, pid, fid;
829 static Stmt s1;
830
831 fnid = filename_to_fnid(zFilename);
@@ -832,16 +1042,16 @@
832 if( zPrior==0 ){
833 pfnid = 0;
834 }else{
835 pfnid = filename_to_fnid(zPrior);
836 }
837 if( zFromUuid==0 ){
838 pid = 0;
839 }else{
840 pid = uuid_to_rid(zFromUuid, 1);
841 }
842 if( zToUuid==0 ){
843 fid = 0;
844 }else{
845 fid = uuid_to_rid(zToUuid, 1);
846 }
847 db_static_prepare(&s1,
@@ -858,33 +1068,83 @@
858 content_deltify(pid, fid, 0);
859 }
860 }
861
862 /*
863 ** Locate a file named zName in the aFile[] array of the given
864 ** manifest. We assume that filenames are in sorted order.
865 ** Use a binary search. Return turn the index of the matching
866 ** entry. Or return -1 if not found.
 
 
 
867 */
868 static int find_file_in_manifest(Manifest *p, const char *zName){
869 int lwr, upr;
870 int c;
871 int i;
872 lwr = 0;
873 upr = p->nFile - 1;
 
 
 
 
 
 
 
 
 
 
874 while( lwr<=upr ){
875 i = (lwr+upr)/2;
876 c = strcmp(p->aFile[i].zName, zName);
877 if( c<0 ){
878 lwr = i+1;
879 }else if( c>0 ){
880 upr = i-1;
881 }else{
882 return i;
 
883 }
884 }
885 return -1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
886 }
887
888 /*
889 ** Add mlink table entries associated with manifest cid. The
890 ** parent manifest is pid.
@@ -895,89 +1155,70 @@
895 ** Deleted files have mlink.fid=0.
896 ** Added files have mlink.pid=0.
897 ** Edited files have both mlink.pid!=0 and mlink.fid!=0
898 */
899 static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
900 Manifest other;
901 Blob otherContent;
902 int otherRid;
903 int i, j;
 
 
 
904
905 if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", cid) ){
906 return;
907 }
 
 
 
908 assert( pParent==0 || pChild==0 );
909 if( pParent==0 ){
910 pParent = &other;
911 otherRid = pid;
912 }else{
913 pChild = &other;
914 otherRid = cid;
915 }
916 if( manifest_cache_find(otherRid, &other)==0 ){
917 content_get(otherRid, &otherContent);
918 if( blob_size(&otherContent)==0 ) return;
919 if( manifest_parse(&other, &otherContent)==0 ) return;
920 }
921 content_deltify(pid, cid, 0);
922
923 /* Use the iRename fields to find the cross-linkage between
924 ** renamed files. */
925 for(j=0; j<pChild->nFile; j++){
926 const char *zPrior = pChild->aFile[j].zPrior;
927 if( zPrior && zPrior[0] ){
928 i = find_file_in_manifest(pParent, zPrior);
929 if( i>=0 ){
930 pChild->aFile[j].iRename = i;
931 pParent->aFile[i].iRename = j;
932 }
933 }
934 }
935
936 /* Construct the mlink entries */
937 for(i=j=0; i<pParent->nFile && j<pChild->nFile; ){
938 int c;
939 if( pParent->aFile[i].iRename>=0 ){
940 i++;
941 }else if( (c = strcmp(pParent->aFile[i].zName, pChild->aFile[j].zName))<0 ){
942 add_one_mlink(cid, pParent->aFile[i].zUuid,0,pParent->aFile[i].zName,0);
943 i++;
944 }else if( c>0 ){
945 int rn = pChild->aFile[j].iRename;
946 if( rn>=0 ){
947 add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid,
948 pChild->aFile[j].zName, pParent->aFile[rn].zName);
949 }else{
950 add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
951 }
952 j++;
953 }else{
954 if( strcmp(pParent->aFile[i].zUuid, pChild->aFile[j].zUuid)!=0 ){
955 add_one_mlink(cid, pParent->aFile[i].zUuid, pChild->aFile[j].zUuid,
956 pChild->aFile[j].zName, 0);
957 }
958 i++;
959 j++;
960 }
961 }
962 while( i<pParent->nFile ){
963 if( pParent->aFile[i].iRename<0 ){
964 add_one_mlink(cid, pParent->aFile[i].zUuid, 0, pParent->aFile[i].zName,0);
965 }
966 i++;
967 }
968 while( j<pChild->nFile ){
969 int rn = pChild->aFile[j].iRename;
970 if( rn>=0 ){
971 add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid,
972 pChild->aFile[j].zName, pParent->aFile[rn].zName);
973 }else{
974 add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
975 }
976 j++;
977 }
978 manifest_cache_insert(otherRid, &other);
979 }
980
981 /*
982 ** True if manifest_crosslink_begin() has been called but
983 ** manifest_crosslink_end() is still pending.
@@ -1114,40 +1355,40 @@
1114 ** of the routine, "manifest_crosslink", and the name of this source
1115 ** file, is a legacy of its original use.
1116 */
1117 int manifest_crosslink(int rid, Blob *pContent){
1118 int i;
1119 Manifest m;
1120 Stmt q;
1121 int parentid = 0;
1122
1123 if( manifest_cache_find(rid, &m) ){
1124 blob_reset(pContent);
1125 }else if( manifest_parse(&m, pContent)==0 ){
1126 return 0;
1127 }
1128 if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){
1129 manifest_clear(&m);
1130 return 0;
1131 }
1132 db_begin_transaction();
1133 if( m.type==CFTYPE_MANIFEST ){
1134 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1135 char *zCom;
1136 for(i=0; i<m.nParent; i++){
1137 int pid = uuid_to_rid(m.azParent[i], 1);
1138 db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
1139 "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
1140 if( i==0 ){
1141 add_mlink(pid, 0, rid, &m);
1142 parentid = pid;
1143 }
1144 }
1145 db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
1146 while( db_step(&q)==SQLITE_ROW ){
1147 int cid = db_column_int(&q, 0);
1148 add_mlink(rid, &m, cid, 0);
1149 }
1150 db_finalize(&q);
1151 db_multi_exec(
1152 "REPLACE INTO event(type,mtime,objid,user,comment,"
1153 "bgcolor,euser,ecomment)"
@@ -1158,118 +1399,130 @@
1158 " ),"
1159 " %d,%Q,%Q,"
1160 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0),"
1161 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
1162 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1163 TAG_DATE, rid, m.rDate,
1164 rid, m.zUser, m.zComment,
1165 TAG_BGCOLOR, rid,
1166 TAG_USER, rid,
1167 TAG_COMMENT, rid
1168 );
1169 zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
1170 " WHERE rowid=last_insert_rowid()");
1171 wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE);
1172 free(zCom);
 
 
 
 
 
 
 
 
 
 
 
 
1173 }
1174 }
1175 if( m.type==CFTYPE_CLUSTER ){
1176 tag_insert("cluster", 1, 0, rid, m.rDate, rid);
1177 for(i=0; i<m.nCChild; i++){
1178 int mid;
1179 mid = uuid_to_rid(m.azCChild[i], 1);
1180 if( mid>0 ){
1181 db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid);
1182 }
1183 }
1184 }
1185 if( m.type==CFTYPE_CONTROL
1186 || m.type==CFTYPE_MANIFEST
1187 || m.type==CFTYPE_EVENT
1188 ){
1189 for(i=0; i<m.nTag; i++){
1190 int tid;
1191 int type;
1192 if( m.aTag[i].zUuid ){
1193 tid = uuid_to_rid(m.aTag[i].zUuid, 1);
1194 }else{
1195 tid = rid;
1196 }
1197 if( tid ){
1198 switch( m.aTag[i].zName[0] ){
1199 case '-': type = 0; break; /* Cancel prior occurances */
1200 case '+': type = 1; break; /* Apply to target only */
1201 case '*': type = 2; break; /* Propagate to descendants */
1202 default:
1203 fossil_fatal("unknown tag type in manifest: %s", m.aTag);
1204 return 0;
1205 }
1206 tag_insert(&m.aTag[i].zName[1], type, m.aTag[i].zValue,
1207 rid, m.rDate, tid);
1208 }
1209 }
1210 if( parentid ){
1211 tag_propagate_all(parentid);
1212 }
1213 }
1214 if( m.type==CFTYPE_WIKI ){
1215 char *zTag = mprintf("wiki-%s", m.zWikiTitle);
1216 int tagid = tag_findid(zTag, 1);
1217 int prior;
1218 char *zComment;
1219 int nWiki;
1220 char zLength[40];
1221 while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
1222 nWiki = strlen(m.zWiki);
1223 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1224 tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
1225 free(zTag);
1226 prior = db_int(0,
1227 "SELECT rid FROM tagxref"
1228 " WHERE tagid=%d AND mtime<%.17g"
1229 " ORDER BY mtime DESC",
1230 tagid, m.rDate
1231 );
1232 if( prior ){
1233 content_deltify(prior, rid, 0);
1234 }
1235 if( nWiki>0 ){
1236 zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1237 }else{
1238 zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
1239 }
1240 db_multi_exec(
1241 "REPLACE INTO event(type,mtime,objid,user,comment,"
1242 " bgcolor,euser,ecomment)"
1243 "VALUES('w',%.17g,%d,%Q,%Q,"
1244 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
1245 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
1246 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1247 m.rDate, rid, m.zUser, zComment,
1248 TAG_BGCOLOR, rid,
1249 TAG_BGCOLOR, rid,
1250 TAG_USER, rid,
1251 TAG_COMMENT, rid
1252 );
1253 free(zComment);
1254 }
1255 if( m.type==CFTYPE_EVENT ){
1256 char *zTag = mprintf("event-%s", m.zEventId);
1257 int tagid = tag_findid(zTag, 1);
1258 int prior, subsequent;
1259 int nWiki;
1260 char zLength[40];
1261 while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
1262 nWiki = strlen(m.zWiki);
1263 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1264 tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
1265 free(zTag);
1266 prior = db_int(0,
1267 "SELECT rid FROM tagxref"
1268 " WHERE tagid=%d AND mtime<%.17g"
1269 " ORDER BY mtime DESC",
1270 tagid, m.rDate
1271 );
1272 if( prior ){
1273 content_deltify(prior, rid, 0);
1274 db_multi_exec(
1275 "DELETE FROM event"
@@ -1281,108 +1534,87 @@
1281 }
1282 subsequent = db_int(0,
1283 "SELECT rid FROM tagxref"
1284 " WHERE tagid=%d AND mtime>%.17g"
1285 " ORDER BY mtime",
1286 tagid, m.rDate
1287 );
1288 if( subsequent ){
1289 content_deltify(rid, subsequent, 0);
1290 }else{
1291 db_multi_exec(
1292 "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
1293 "VALUES('e',%.17g,%d,%d,%Q,%Q,"
1294 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1295 m.rEventDate, rid, tagid, m.zUser, m.zComment,
1296 TAG_BGCOLOR, rid
1297 );
1298 }
1299 }
1300 if( m.type==CFTYPE_TICKET ){
1301 char *zTag;
1302
1303 assert( manifest_crosslink_busy==1 );
1304 zTag = mprintf("tkt-%s", m.zTicketUuid);
1305 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
1306 free(zTag);
1307 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
1308 m.zTicketUuid);
1309 }
1310 if( m.type==CFTYPE_ATTACHMENT ){
1311 db_multi_exec(
1312 "INSERT INTO attachment(attachid, mtime, src, target,"
1313 "filename, comment, user)"
1314 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
1315 rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
1316 (m.zComment ? m.zComment : ""), m.zUser
1317 );
1318 db_multi_exec(
1319 "UPDATE attachment SET isLatest = (mtime=="
1320 "(SELECT max(mtime) FROM attachment"
1321 " WHERE target=%Q AND filename=%Q))"
1322 " WHERE target=%Q AND filename=%Q",
1323 m.zAttachTarget, m.zAttachName,
1324 m.zAttachTarget, m.zAttachName
1325 );
1326 if( strlen(m.zAttachTarget)!=UUID_SIZE
1327 || !validate16(m.zAttachTarget, UUID_SIZE)
1328 ){
1329 char *zComment;
1330 if( m.zAttachSrc && m.zAttachSrc[0] ){
1331 zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
1332 m.zAttachName, m.zAttachTarget);
1333 }else{
1334 zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
1335 m.zAttachName, m.zAttachTarget);
1336 }
1337 db_multi_exec(
1338 "REPLACE INTO event(type,mtime,objid,user,comment)"
1339 "VALUES('w',%.17g,%d,%Q,%Q)",
1340 m.rDate, rid, m.zUser, zComment
1341 );
1342 free(zComment);
1343 }else{
1344 char *zComment;
1345 if( m.zAttachSrc && m.zAttachSrc[0] ){
1346 zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
1347 m.zAttachName, m.zAttachTarget);
1348 }else{
1349 zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
1350 m.zAttachName, m.zAttachTarget);
1351 }
1352 db_multi_exec(
1353 "REPLACE INTO event(type,mtime,objid,user,comment)"
1354 "VALUES('t',%.17g,%d,%Q,%Q)",
1355 m.rDate, rid, m.zUser, zComment
1356 );
1357 free(zComment);
1358 }
1359 }
1360 db_end_transaction(0);
1361 if( m.type==CFTYPE_MANIFEST ){
1362 manifest_cache_insert(rid, &m);
1363 }else{
1364 manifest_clear(&m);
1365 }
1366 return 1;
1367 }
1368
1369 /*
1370 ** Given a checkin name, load and parse the manifest for that checkin.
1371 ** Throw a fatal error if anything goes wrong.
1372 */
1373 void manifest_from_name(
1374 const char *zName,
1375 Manifest *pM
1376 ){
1377 int rid;
1378 Blob content;
1379
1380 rid = name_to_rid(zName);
1381 if( !is_a_version(rid) ){
1382 fossil_fatal("no such checkin: %s", zName);
1383 }
1384 content_get(rid, &content);
1385 if( !manifest_parse(pM, &content) ){
1386 fossil_fatal("cannot parse manifest for checkin: %s", zName);
1387 }
1388 }
1389
--- src/manifest.c
+++ src/manifest.c
@@ -26,24 +26,39 @@
26
27 #if INTERFACE
28 /*
29 ** Types of control files
30 */
31 #define CFTYPE_ANY 0
32 #define CFTYPE_MANIFEST 1
33 #define CFTYPE_CLUSTER 2
34 #define CFTYPE_CONTROL 3
35 #define CFTYPE_WIKI 4
36 #define CFTYPE_TICKET 5
37 #define CFTYPE_ATTACHMENT 6
38 #define CFTYPE_EVENT 7
39
40 /*
41 ** A single F-card within a manifest
42 */
43 struct ManifestFile {
44 char *zName; /* Name of a file */
45 char *zUuid; /* UUID of the file */
46 char *zPerm; /* File permissions */
47 char *zPrior; /* Prior name if the name was changed */
48 };
49
50
51 /*
52 ** A parsed manifest or cluster.
53 */
54 struct Manifest {
55 Blob content; /* The original content blob */
56 int type; /* Type of artifact. One of CFTYPE_xxxxx */
57 int rid; /* The blob-id for this manifest */
58 char *zBaseline; /* Baseline manifest. The B card. */
59 Manifest *pBaseline; /* The actual baseline manifest */
60 char *zComment; /* Decoded comment. The C card. */
61 double rDate; /* Date and time from D card. 0.0 if no D card. */
62 char *zUser; /* Name of the user from the U card. */
63 char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
64 char *zWiki; /* Text of the wiki page. W card. */
@@ -54,17 +69,12 @@
69 char *zAttachName; /* Filename of an attachment. A card. */
70 char *zAttachSrc; /* UUID of document being attached. A card. */
71 char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
72 int nFile; /* Number of F cards */
73 int nFileAlloc; /* Slots allocated in aFile[] */
74 int iFile; /* Index of current file in iterator */
75 ManifestFile *aFile; /* One entry for each F-card */
 
 
 
 
 
76 int nParent; /* Number of parents. */
77 int nParentAlloc; /* Slots allocated in azParent[] */
78 char **azParent; /* UUIDs of parents. One for each P card argument */
79 int nCChild; /* Number of cluster children */
80 int nCChildAlloc; /* Number of closts allocated in azCChild[] */
@@ -87,68 +97,75 @@
97
98 /*
99 ** A cache of parsed manifests. This reduces the number of
100 ** calls to manifest_parse() when doing a rebuild.
101 */
102 #define MX_MANIFEST_CACHE 6
103 static struct {
104 int nxAge;
 
105 int aAge[MX_MANIFEST_CACHE];
106 Manifest *apManifest[MX_MANIFEST_CACHE];
107 } manifestCache;
108
109
110 /*
111 ** Clear the memory allocated in a manifest object
112 */
113 void manifest_destroy(Manifest *p){
114 if( p ){
115 blob_reset(&p->content);
116 free(p->aFile);
117 free(p->azParent);
118 free(p->azCChild);
119 free(p->aTag);
120 free(p->aField);
121 if( p->pBaseline ) manifest_destroy(p->pBaseline);
122 fossil_free(p);
123 }
124 }
125
126 /*
127 ** Add an element to the manifest cache using LRU replacement.
128 */
129 void manifest_cache_insert(Manifest *p){
130 while( p ){
131 int i;
132 Manifest *pBaseline = p->pBaseline;
133 p->pBaseline = 0;
134 for(i=0; i<MX_MANIFEST_CACHE; i++){
135 if( manifestCache.apManifest[i]==0 ) break;
136 }
137 if( i>=MX_MANIFEST_CACHE ){
138 int oldest = 0;
139 int oldestAge = manifestCache.aAge[0];
140 for(i=1; i<MX_MANIFEST_CACHE; i++){
141 if( manifestCache.aAge[i]<oldestAge ){
142 oldest = i;
143 oldestAge = manifestCache.aAge[i];
144 }
145 }
146 manifest_destroy(manifestCache.apManifest[oldest]);
147 i = oldest;
148 }
149 manifestCache.aAge[i] = ++manifestCache.nxAge;
150 manifestCache.apManifest[i] = p;
151 p = pBaseline;
152 }
153 }
154
155 /*
156 ** Try to extract a line from the manifest cache. Return 1 if found.
157 ** Return 0 if not found.
158 */
159 static Manifest *manifest_cache_find(int rid){
160 int i;
161 Manifest *p;
162 for(i=0; i<MX_MANIFEST_CACHE; i++){
163 if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){
164 p = manifestCache.apManifest[i];
165 manifestCache.apManifest[i] = 0;
166 return p;
167 }
168 }
169 return 0;
170 }
171
@@ -156,21 +173,113 @@
173 ** Clear the manifest cache.
174 */
175 void manifest_cache_clear(void){
176 int i;
177 for(i=0; i<MX_MANIFEST_CACHE; i++){
178 if( manifestCache.apManifest[i] ){
179 manifest_destroy(manifestCache.apManifest[i]);
180 }
181 }
182 memset(&manifestCache, 0, sizeof(manifestCache));
183 }
184
185 #ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
186 # define md5sum_init(X)
187 # define md5sum_step_text(X,Y)
188 #endif
189
190 /*
191 ** Remove the PGP signature from the artifact, if there is one.
192 */
193 static void remove_pgp_signature(char **pz, int *pn){
194 char *z = *pz;
195 int n = *pn;
196 int i;
197 if( memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return;
198 for(i=34; i<n && (z[i-1]!='\n' || z[i-2]!='\n'); i++){}
199 if( i>=n ) return;
200 z += i;
201 n -= i;
202 *pz = z;
203 for(i=n-1; i>=0; i--){
204 if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){
205 n = i+1;
206 break;
207 }
208 }
209 *pn = n;
210 return;
211 }
212
213 /*
214 ** Verify the Z-card checksum on the artifact, if there is such a
215 ** checksum. Return 0 if there is no Z-card. Return 1 if the Z-card
216 ** exists and is correct. Return 2 if the Z-card exists and has the wrong
217 ** value.
218 **
219 ** 0123456789 123456789 123456789 123456789
220 ** Z aea84f4f863865a8d59d0384e4d2a41c
221 */
222 static int verify_z_card(const char *z, int n){
223 if( n<35 ) return 0;
224 if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0;
225 md5sum_init();
226 md5sum_step_text(z, n-35);
227 if( memcmp(&z[n-33], md5sum_finish(0), 32)==0 ){
228 return 1;
229 }else{
230 return 2;
231 }
232 }
233
234 /*
235 ** A structure used for rapid parsing of the Manifest file
236 */
237 typedef struct ManifestText ManifestText;
238 struct ManifestText {
239 char *z; /* The first character of the next token */
240 char *zEnd; /* One character beyond the end of the manifest */
241 int atEol; /* True if z points to the start of a new line */
242 };
243
244 /*
245 ** Return a pointer to the next token. The token is zero-terminated.
246 ** Return NULL if there are no more tokens on the current line.
247 */
248 static char *next_token(ManifestText *p, int *pLen){
249 char *z;
250 char *zStart;
251 int c;
252 if( p->atEol ) return 0;
253 zStart = z = p->z;
254 while( (c=(*z))!=' ' && c!='\n' ){ z++; }
255 *z = 0;
256 p->z = &z[1];
257 p->atEol = c=='\n';
258 if( pLen ) *pLen = z - zStart;
259 return zStart;
260 }
261
262 /*
263 ** Return the card-type for the next card. Or, return 0 if there are no
264 ** more cards or if we are not at the end of the current card.
265 */
266 static char next_card(ManifestText *p){
267 char c;
268 if( !p->atEol || p->z>=p->zEnd ) return 0;
269 c = p->z[0];
270 if( p->z[1]==' ' ){
271 p->z += 2;
272 p->atEol = 0;
273 }else if( p->z[1]=='\n' ){
274 p->z += 2;
275 p->atEol = 1;
276 }else{
277 c = 0;
278 }
279 return c;
280 }
281
282 /*
283 ** Parse a blob into a Manifest object. The Manifest object
284 ** takes over the input blob and will free it when the
285 ** Manifest object is freed. Zeros are inserted into the blob
@@ -195,56 +304,66 @@
304 ** Each card is divided into tokens by a single space character.
305 ** The first token is a single upper-case letter which is the card type.
306 ** The card type determines the other parameters to the card.
307 ** Cards must occur in lexicographical order.
308 */
309 static Manifest *manifest_parse(Blob *pContent, int rid){
310 Manifest *p;
311 int seenZ = 0;
312 int i, lineNo=0;
313 ManifestText x;
314 char cPrevType = 0;
315 char cType;
316 char *z;
317 int n;
318 char *zUuid;
319 int sz;
320
321 /* Every control artifact ends with a '\n' character. Exit early
322 ** if that is not the case for this artifact.
323 */
324 z = blob_buffer(pContent);
325 n = blob_size(pContent);
326 if( n<=0 || z[n-1]!='\n' ){
327 blob_reset(pContent);
328 return 0;
329 }
330
331 /* Strip off the PGP signature if there is one. Then verify the
332 ** Z-card.
333 */
334 remove_pgp_signature(&z, &n);
335 if( verify_z_card(z, n)==0 ){
336 blob_reset(pContent);
337 return 0;
338 }
339
340 /* Verify that the first few characters of the artifact look like
341 ** a control artifact.
342 */
343 if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
344 blob_reset(pContent);
345 return 0;
346 }
347
348 /* Allocate a Manifest object to hold the parsed control artifact.
349 */
350 p = fossil_malloc( sizeof(*p) );
351 memset(p, 0, sizeof(*p));
352 memcpy(&p->content, pContent, sizeof(p->content));
353 p->rid = rid;
354 blob_zero(pContent);
355 pContent = &p->content;
356
357 /* Begin parsing, card by card.
358 */
359 x.z = z;
360 x.zEnd = &z[n];
361 x.atEol = 1;
362 while( (cType = next_card(&x))!=0 && cType>=cPrevType ){
363 lineNo++;
364 switch( cType ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365 /*
366 ** A <filename> <target> ?<source>?
367 **
368 ** Identifies an attachment to either a wiki page or a ticket.
369 ** <source> is the artifact that is the attachment. <source>
@@ -251,50 +370,60 @@
370 ** is omitted to delete an attachment. <target> is the name of
371 ** a wiki page or ticket to which that attachment is connected.
372 */
373 case 'A': {
374 char *zName, *zTarget, *zSrc;
375 int nTarget, nSrc;
376 zName = next_token(&x, 0);
377 zTarget = next_token(&x, &nTarget);
378 zSrc = next_token(&x, &nSrc);
379 if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
380 if( p->zAttachName!=0 ) goto manifest_syntax_error;
 
 
 
 
381 defossilize(zName);
382 if( !file_is_simple_pathname(zName) ){
383 goto manifest_syntax_error;
384 }
385 defossilize(zTarget);
386 if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
387 && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
388 goto manifest_syntax_error;
389 }
390 if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
 
391 goto manifest_syntax_error;
392 }
393 p->zAttachName = (char*)file_tail(zName);
394 p->zAttachSrc = zSrc;
395 p->zAttachTarget = zTarget;
396 break;
397 }
398
399 /*
400 ** B <uuid>
401 **
402 ** A B-line gives the UUID for the baselinen of a delta-manifest.
403 */
404 case 'B': {
405 if( p->zBaseline ) goto manifest_syntax_error;
406 p->zBaseline = next_token(&x, &sz);
407 if( p->zBaseline==0 ) goto manifest_syntax_error;
408 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
409 if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error;
410 break;
411 }
412
413
414 /*
415 ** C <comment>
416 **
417 ** Comment text is fossil-encoded. There may be no more than
418 ** one C line. C lines are required for manifests and are
419 ** disallowed on all other control files.
420 */
421 case 'C': {
 
422 if( p->zComment!=0 ) goto manifest_syntax_error;
423 p->zComment = next_token(&x, 0);
424 if( p->zComment==0 ) goto manifest_syntax_error;
 
425 defossilize(p->zComment);
426 break;
427 }
428
429 /*
@@ -303,17 +432,13 @@
432 ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS
433 ** There can be no more than 1 D line. D lines are required
434 ** for all control files except for clusters.
435 */
436 case 'D': {
437 if( p->rDate>0.0 ) goto manifest_syntax_error;
438 p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0));
439 if( p->rDate<=0.0 ) goto manifest_syntax_error;
 
 
 
 
440 break;
441 }
442
443 /*
444 ** E <timestamp> <uuid>
@@ -323,56 +448,46 @@
448 ** The event timestamp is distinct from the D timestamp. The D
449 ** timestamp is when the artifact was created whereas the E timestamp
450 ** is when the specific event is said to occur.
451 */
452 case 'E': {
453 if( p->rEventDate>0.0 ) goto manifest_syntax_error;
454 p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
 
 
 
 
 
 
455 if( p->rEventDate<=0.0 ) goto manifest_syntax_error;
456 p->zEventId = next_token(&x, &sz);
457 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
458 if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error;
459 break;
460 }
461
462 /*
463 ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
464 **
465 ** Identifies a file in a manifest. Multiple F lines are
466 ** allowed in a manifest. F lines are not allowed in any
467 ** other control file. The filename and old-name are fossil-encoded.
468 */
469 case 'F': {
470 char *zName, *zPerm, *zPriorName;
471 zName = next_token(&x,0);
472 if( zName==0 ) goto manifest_syntax_error;
 
 
 
 
 
 
 
473 defossilize(zName);
474 if( !file_is_simple_pathname(zName) ){
475 goto manifest_syntax_error;
476 }
477 zUuid = next_token(&x, &sz);
478 if( p->zBaseline==0 || zUuid!=0 ){
479 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
480 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
481 }
482 zPerm = next_token(&x,0);
483 zPriorName = next_token(&x,0);
484 if( zPriorName ){
485 defossilize(zPriorName);
486 if( !file_is_simple_pathname(zPriorName) ){
487 goto manifest_syntax_error;
488 }
 
 
489 }
490 if( p->nFile>=p->nFileAlloc ){
491 p->nFileAlloc = p->nFileAlloc*2 + 10;
492 p->aFile = fossil_realloc(p->aFile,
493 p->nFileAlloc*sizeof(p->aFile[0]) );
@@ -380,11 +495,10 @@
495 i = p->nFile++;
496 p->aFile[i].zName = zName;
497 p->aFile[i].zUuid = zUuid;
498 p->aFile[i].zPerm = zPerm;
499 p->aFile[i].zPrior = zPriorName;
 
500 if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
501 goto manifest_syntax_error;
502 }
503 break;
504 }
@@ -397,16 +511,14 @@
511 ** value. If <value> is omitted then it is understood to be an
512 ** empty string.
513 */
514 case 'J': {
515 char *zName, *zValue;
516 zName = next_token(&x,0);
517 zValue = next_token(&x,0);
518 if( zName==0 ) goto manifest_syntax_error;
519 if( zValue==0 ) zValue = "";
 
 
520 defossilize(zValue);
521 if( p->nField>=p->nFieldAlloc ){
522 p->nFieldAlloc = p->nFieldAlloc*2 + 10;
523 p->aField = fossil_realloc(p->aField,
524 p->nFieldAlloc*sizeof(p->aField[0]) );
@@ -426,18 +538,14 @@
538 **
539 ** A K-line gives the UUID for the ticket which this control file
540 ** is amending.
541 */
542 case 'K': {
 
 
 
 
 
 
543 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
544 p->zTicketUuid = next_token(&x, &sz);
545 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
546 if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error;
547 break;
548 }
549
550 /*
551 ** L <wikititle>
@@ -444,15 +552,13 @@
552 **
553 ** The wiki page title is fossil-encoded. There may be no more than
554 ** one L line.
555 */
556 case 'L': {
 
557 if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
558 p->zWikiTitle = next_token(&x,0);
559 if( p->zWikiTitle==0 ) goto manifest_syntax_error;
 
560 defossilize(p->zWikiTitle);
561 if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
562 goto manifest_syntax_error;
563 }
564 break;
@@ -463,15 +569,13 @@
569 **
570 ** An M-line identifies another artifact by its UUID. M-lines
571 ** occur in clusters only.
572 */
573 case 'M': {
574 zUuid = next_token(&x, &sz);
575 if( zUuid==0 ) goto manifest_syntax_error;
576 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
 
 
577 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
578 if( p->nCChild>=p->nCChildAlloc ){
579 p->nCChildAlloc = p->nCChildAlloc*2 + 10;
580 p->azCChild = fossil_realloc(p->azCChild
581 , p->nCChildAlloc*sizeof(p->azCChild[0]) );
@@ -490,15 +594,12 @@
594 ** Specify one or more other artifacts where are the parents of
595 ** this artifact. The first parent is the primary parent. All
596 ** others are parents by merge.
597 */
598 case 'P': {
599 while( (zUuid = next_token(&x, &sz))!=0 ){
600 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
 
 
 
601 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
602 if( p->nParent>=p->nParentAlloc ){
603 p->nParentAlloc = p->nParentAlloc*2 + 5;
604 p->azParent = fossil_realloc(p->azParent,
605 p->nParentAlloc*sizeof(char*));
@@ -514,16 +615,13 @@
615 **
616 ** Specify the MD5 checksum over the name and content of all files
617 ** in the manifest.
618 */
619 case 'R': {
 
620 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
621 p->zRepoCksum = next_token(&x, &sz);
622 if( sz!=32 ) goto manifest_syntax_error;
 
 
623 if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
624 break;
625 }
626
627 /*
@@ -540,29 +638,20 @@
638 ** the tag is really a property with the given value.
639 **
640 ** Tags are not allowed in clusters. Multiple T lines are allowed.
641 */
642 case 'T': {
643 char *zName, *zValue;
644 zName = next_token(&x, 0);
645 if( zName==0 ) goto manifest_syntax_error;
646 zUuid = next_token(&x, &sz);
647 if( zUuid==0 ) goto manifest_syntax_error;
648 zValue = next_token(&x, 0);
649 if( zValue ) defossilize(zValue);
650 if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
 
 
 
 
 
 
 
 
 
651 /* A valid uuid */
652 }else if( sz==1 && zUuid[0]=='*' ){
653 zUuid = 0;
654 }else{
655 goto manifest_syntax_error;
656 }
657 defossilize(zName);
@@ -593,19 +682,17 @@
682 ** Identify the user who created this control file by their
683 ** login. Only one U line is allowed. Prohibited in clusters.
684 ** If the user name is omitted, take that to be "anonymous".
685 */
686 case 'U': {
 
687 if( p->zUser!=0 ) goto manifest_syntax_error;
688 p->zUser = next_token(&x, 0);
689 if( p->zUser==0 ){
690 p->zUser = "anonymous";
691 }else{
 
692 defossilize(p->zUser);
693 }
 
694 break;
695 }
696
697 /*
698 ** W <size>
@@ -613,26 +700,28 @@
700 ** The next <size> bytes of the file contain the text of the wiki
701 ** page. There is always an extra \n before the start of the next
702 ** record.
703 */
704 case 'W': {
705 char *zSize;
706 int size, c;
707 Blob wiki;
708 zSize = next_token(&x, 0);
709 if( zSize==0 ) goto manifest_syntax_error;
710 if( x.atEol==0 ) goto manifest_syntax_error;
711 for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
712 size = size*10 + c - '0';
713 }
714 if( size<0 ) goto manifest_syntax_error;
715 if( p->zWiki!=0 ) goto manifest_syntax_error;
716 blob_zero(&wiki);
717 if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error;
718 p->zWiki = x.z;
719 x.z += size;
720 if( x.z[0]!='\n' ) goto manifest_syntax_error;
721 x.z[0] = 0;
722 x.z++;
 
723 break;
724 }
725
726
727 /*
@@ -645,35 +734,24 @@
734 ** This card is required for all control file types except for
735 ** Manifest. It is not required for manifest only for historical
736 ** compatibility reasons.
737 */
738 case 'Z': {
739 zUuid = next_token(&x, &sz);
740 if( sz!=32 ) goto manifest_syntax_error;
741 if( !validate16(zUuid, 32) ) goto manifest_syntax_error;
 
 
 
 
 
 
 
 
 
 
 
742 seenZ = 1;
743 break;
744 }
745 default: {
746 goto manifest_syntax_error;
747 }
748 }
749 }
750 if( x.z<x.zEnd ) goto manifest_syntax_error;
751
752 if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
753 if( p->nCChild>0 ) goto manifest_syntax_error;
754 if( p->rDate<=0.0 ) goto manifest_syntax_error;
755 if( p->nField>0 ) goto manifest_syntax_error;
756 if( p->zTicketUuid ) goto manifest_syntax_error;
757 if( p->zWiki ) goto manifest_syntax_error;
@@ -754,43 +832,175 @@
832 if( p->zWikiTitle ) goto manifest_syntax_error;
833 if( p->zTicketUuid ) goto manifest_syntax_error;
834 p->type = CFTYPE_MANIFEST;
835 }
836 md5sum_init();
837 return p;
838
839 manifest_syntax_error:
840 /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
841 md5sum_init();
842 manifest_destroy(p);
843 return 0;
844 }
845
846 /*
847 ** Get a manifest given the rid for the control artifact. Return
848 ** a pointer to the manifest on success or NULL if there is a failure.
849 */
850 Manifest *manifest_get(int rid, int cfType){
851 Blob content;
852 Manifest *p;
853 p = manifest_cache_find(rid);
854 if( p ){
855 if( cfType!=CFTYPE_ANY && cfType!=p->type ){
856 manifest_cache_insert(p);
857 p = 0;
858 }
859 return p;
860 }
861 content_get(rid, &content);
862 p = manifest_parse(&content, rid);
863 if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
864 manifest_destroy(p);
865 p = 0;
866 }
867 return p;
868 }
869
870 /*
871 ** Given a checkin name, load and parse the manifest for that checkin.
872 ** Throw a fatal error if anything goes wrong.
873 */
874 Manifest *manifest_get_by_name(const char *zName, int *pRid){
875 int rid;
876 Manifest *p;
877
878 rid = name_to_rid(zName);
879 if( !is_a_version(rid) ){
880 fossil_fatal("no such checkin: %s", zName);
881 }
882 if( pRid ) *pRid = rid;
883 p = manifest_get(rid, CFTYPE_MANIFEST);
884 if( p==0 ){
885 fossil_fatal("cannot parse manifest for checkin: %s", zName);
886 }
887 return p;
888 }
889
890 /*
891 ** COMMAND: test-parse-manifest
892 **
893 ** Usage: %fossil test-parse-manifest FILENAME ?N?
894 **
895 ** Parse the manifest and discarded. Use for testing only.
896 */
897 void manifest_test_parse_cmd(void){
898 Manifest *p;
899 Blob b;
900 int i;
901 int n = 1;
902 sqlite3_open(":memory:", &g.db);
903 if( g.argc!=3 && g.argc!=4 ){
904 usage("FILENAME");
905 }
 
906 blob_read_from_file(&b, g.argv[2]);
907 if( g.argc>3 ) n = atoi(g.argv[3]);
908 for(i=0; i<n; i++){
909 Blob b2;
910 blob_copy(&b2, &b);
911 p = manifest_parse(&b2, 0);
912 manifest_destroy(p);
913 }
914 }
915
916 /*
917 ** Fetch the baseline associated with the delta-manifest p.
918 ** Print a fatal-error and quit if unable to load the baseline.
919 */
920 static void fetch_baseline(Manifest *p){
921 if( p->zBaseline!=0 && p->pBaseline==0 ){
922 int rid = uuid_to_rid(p->zBaseline, 0);
923 p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST);
924 if( p->pBaseline==0 ){
925 fossil_fatal("cannot access baseline manifest %S", p->zBaseline);
926 }
927 }
928 }
929
930 /*
931 ** Rewind a manifest-file iterator back to the beginning of the manifest.
932 */
933 void manifest_file_rewind(Manifest *p){
934 p->iFile = 0;
935 fetch_baseline(p);
936 if( p->pBaseline ){
937 p->pBaseline->iFile = 0;
938 }
939 }
940
941 /*
942 ** Advance to the next manifest-file.
943 **
944 ** Return NULL for end-of-records or if there is an error. If an error
945 ** occurs and pErr!=0 then store 1 in *pErr.
946 */
947 ManifestFile *manifest_file_next(
948 Manifest *p,
949 int *pErr
950 ){
951 ManifestFile *pOut = 0;
952 if( pErr ) *pErr = 0;
953 if( p->pBaseline==0 ){
954 /* Manifest p is a baseline-manifest. Just scan down the list
955 ** of files. */
956 if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
957 }else{
958 /* Manifest p is a delta-manifest. Scan the baseline but amend the
959 ** file list in the baseline with changes described by p.
960 */
961 Manifest *pB = p->pBaseline;
962 int cmp;
963 while(1){
964 if( pB->iFile>=pB->nFile ){
965 /* We have used all entries out of the baseline. Return the next
966 ** entry from the delta. */
967 if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
968 break;
969 }else if( p->iFile>=p->nFile ){
970 /* We have used all entries from the delta. Return the next
971 ** entry from the baseline. */
972 if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++];
973 break;
974 }else if( (cmp = strcmp(pB->aFile[pB->iFile].zName,
975 p->aFile[p->iFile].zName)) < 0 ){
976 /* The next baseline entry comes before the next delta entry.
977 ** So return the baseline entry. */
978 pOut = &pB->aFile[pB->iFile++];
979 break;
980 }else if( cmp>0 ){
981 /* The next delta entry comes before the next baseline
982 ** entry so return the delta entry */
983 pOut = &p->aFile[p->iFile++];
984 break;
985 }else if( p->aFile[p->iFile].zUuid ){
986 /* The next delta entry is a replacement for the next baseline
987 ** entry. Skip the baseline entry and return the delta entry */
988 pB->iFile++;
989 pOut = &p->aFile[p->iFile++];
990 break;
991 }else{
992 /* The next delta entry is a delete of the next baseline
993 ** entry. Skip them both. Repeat the loop to find the next
994 ** non-delete entry. */
995 pB->iFile++;
996 p->iFile++;
997 continue;
998 }
999 }
1000 }
1001 return pOut;
1002 }
1003
1004 /*
1005 ** Translate a filename into a filename-id (fnid). Create a new fnid
1006 ** if no previously exists.
@@ -818,14 +1028,14 @@
1028 ** Add a single entry to the mlink table. Also add the filename to
1029 ** the filename table if it is not there already.
1030 */
1031 static void add_one_mlink(
1032 int mid, /* The record ID of the manifest */
1033 const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */
1034 const char *zToUuid, /* UUID for the mlink.fid. "" to delele */
1035 const char *zFilename, /* Filename */
1036 const char *zPrior /* Previous filename. NULL if unchanged */
1037 ){
1038 int fnid, pfnid, pid, fid;
1039 static Stmt s1;
1040
1041 fnid = filename_to_fnid(zFilename);
@@ -832,16 +1042,16 @@
1042 if( zPrior==0 ){
1043 pfnid = 0;
1044 }else{
1045 pfnid = filename_to_fnid(zPrior);
1046 }
1047 if( zFromUuid==0 || zFromUuid[0]==0 ){
1048 pid = 0;
1049 }else{
1050 pid = uuid_to_rid(zFromUuid, 1);
1051 }
1052 if( zToUuid==0 || zToUuid[0]==0 ){
1053 fid = 0;
1054 }else{
1055 fid = uuid_to_rid(zToUuid, 1);
1056 }
1057 db_static_prepare(&s1,
@@ -858,33 +1068,83 @@
1068 content_deltify(pid, fid, 0);
1069 }
1070 }
1071
1072 /*
1073 ** Do a binary search to find a file in the p->aFile[] array.
1074 **
1075 ** As an optimization, guess that the file we seek is at index p->iFile.
1076 ** That will usually be the case. If it is not found there, then do the
1077 ** actual binary search.
1078 **
1079 ** Update p->iFile to be the index of the file that is found.
1080 */
1081 static ManifestFile *manifest_file_seek_base(Manifest *p, const char *zName){
1082 int lwr, upr;
1083 int c;
1084 int i;
1085 lwr = 0;
1086 upr = p->nFile - 1;
1087 if( p->iFile>=lwr && p->iFile<upr ){
1088 c = strcmp(p->aFile[p->iFile+1].zName, zName);
1089 if( c==0 ){
1090 return &p->aFile[++p->iFile];
1091 }else if( c>0 ){
1092 upr = p->iFile;
1093 }else{
1094 lwr = p->iFile+1;
1095 }
1096 }
1097 while( lwr<=upr ){
1098 i = (lwr+upr)/2;
1099 c = strcmp(p->aFile[i].zName, zName);
1100 if( c<0 ){
1101 lwr = i+1;
1102 }else if( c>0 ){
1103 upr = i-1;
1104 }else{
1105 p->iFile = i;
1106 return &p->aFile[i];
1107 }
1108 }
1109 return 0;
1110 }
1111
1112 /*
1113 ** Locate a file named zName in the aFile[] array of the given manifest.
1114 ** Return a pointer to the appropriate ManifestFile object. Return NULL
1115 ** if not found.
1116 **
1117 ** This routine works even if p is a delta-manifest. The pointer
1118 ** returned might be to the baseline.
1119 **
1120 ** We assume that filenames are in sorted order and use a binary search.
1121 */
1122 ManifestFile *manifest_file_seek(Manifest *p, const char *zName){
1123 ManifestFile *pFile;
1124
1125 pFile = manifest_file_seek_base(p, zName);
1126 if( pFile && pFile->zUuid==0 ) return 0;
1127 if( pFile==0 && p->zBaseline ){
1128 fetch_baseline(p);
1129 pFile = manifest_file_seek_base(p->pBaseline, zName);
1130 }
1131 return pFile;
1132 }
1133
1134 /*
1135 ** This strcmp() function handles NULL arguments. NULLs sort first.
1136 */
1137 static int strcmp_null(const char *zOne, const char *zTwo){
1138 if( zOne==0 ){
1139 if( zTwo==0 ) return 0;
1140 return -1;
1141 }else if( zTwo==0 ){
1142 return +1;
1143 }else{
1144 return strcmp(zOne, zTwo);
1145 }
1146 }
1147
1148 /*
1149 ** Add mlink table entries associated with manifest cid. The
1150 ** parent manifest is pid.
@@ -895,89 +1155,70 @@
1155 ** Deleted files have mlink.fid=0.
1156 ** Added files have mlink.pid=0.
1157 ** Edited files have both mlink.pid!=0 and mlink.fid!=0
1158 */
1159 static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
 
1160 Blob otherContent;
1161 int otherRid;
1162 int i, rc;
1163 ManifestFile *pChildFile, *pParentFile;
1164 Manifest **ppOther;
1165 static Stmt eq;
1166
1167 db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid");
1168 db_bind_int(&eq, ":mid", cid);
1169 rc = db_step(&eq);
1170 db_reset(&eq);
1171 if( rc==SQLITE_ROW ) return;
1172
1173 assert( pParent==0 || pChild==0 );
1174 if( pParent==0 ){
1175 ppOther = &pParent;
1176 otherRid = pid;
1177 }else{
1178 ppOther = &pChild;
1179 otherRid = cid;
1180 }
1181 if( (*ppOther = manifest_cache_find(otherRid))==0 ){
1182 content_get(otherRid, &otherContent);
1183 if( blob_size(&otherContent)==0 ) return;
1184 *ppOther = manifest_parse(&otherContent, otherRid);
1185 if( *ppOther==0 ) return;
1186 }
1187 if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){
1188 content_deltify(pid, cid, 0);
1189 }
1190
1191 for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){
1192 if( pChildFile->zPrior ){
1193 pParentFile = manifest_file_seek(pParent, pChildFile->zPrior);
1194 if( pParentFile ){
1195 add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1196 pChildFile->zName, pChildFile->zPrior);
1197 }
1198 }else{
1199 pParentFile = manifest_file_seek(pParent, pChildFile->zName);
1200 if( pParentFile==0 ){
1201 if( pChildFile->zUuid ){
1202 add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0);
1203 }
1204 }else if( strcmp_null(pChildFile->zUuid, pParentFile->zUuid)!=0 ){
1205 add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1206 pChildFile->zName, 0);
1207 }
1208 }
1209 }
1210 if( pParent->zBaseline && pChild->zBaseline ){
1211 for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){
1212 if( pParentFile->zUuid ) continue;
1213 pChildFile = manifest_file_seek(pChild, pParentFile->zName);
1214 if( pChildFile ){
1215 add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0);
1216 }
1217 }
1218 }
1219 manifest_cache_insert(*ppOther);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1220 }
1221
1222 /*
1223 ** True if manifest_crosslink_begin() has been called but
1224 ** manifest_crosslink_end() is still pending.
@@ -1114,40 +1355,40 @@
1355 ** of the routine, "manifest_crosslink", and the name of this source
1356 ** file, is a legacy of its original use.
1357 */
1358 int manifest_crosslink(int rid, Blob *pContent){
1359 int i;
1360 Manifest *p;
1361 Stmt q;
1362 int parentid = 0;
1363
1364 if( (p = manifest_cache_find(rid))!=0 ){
1365 blob_reset(pContent);
1366 }else if( (p = manifest_parse(pContent, rid))==0 ){
1367 return 0;
1368 }
1369 if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
1370 manifest_destroy(p);
1371 return 0;
1372 }
1373 db_begin_transaction();
1374 if( p->type==CFTYPE_MANIFEST ){
1375 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1376 char *zCom;
1377 for(i=0; i<p->nParent; i++){
1378 int pid = uuid_to_rid(p->azParent[i], 1);
1379 db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
1380 "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate);
1381 if( i==0 ){
1382 add_mlink(pid, 0, rid, p);
1383 parentid = pid;
1384 }
1385 }
1386 db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
1387 while( db_step(&q)==SQLITE_ROW ){
1388 int cid = db_column_int(&q, 0);
1389 add_mlink(rid, p, cid, 0);
1390 }
1391 db_finalize(&q);
1392 db_multi_exec(
1393 "REPLACE INTO event(type,mtime,objid,user,comment,"
1394 "bgcolor,euser,ecomment)"
@@ -1158,118 +1399,130 @@
1399 " ),"
1400 " %d,%Q,%Q,"
1401 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0),"
1402 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
1403 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1404 TAG_DATE, rid, p->rDate,
1405 rid, p->zUser, p->zComment,
1406 TAG_BGCOLOR, rid,
1407 TAG_USER, rid,
1408 TAG_COMMENT, rid
1409 );
1410 zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
1411 " WHERE rowid=last_insert_rowid()");
1412 wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE);
1413 free(zCom);
1414
1415 /* If this is a delta-manifest, record the fact that this repository
1416 ** contains delta manifests, to free the "commit" logic to generate
1417 ** new delta manifests.
1418 */
1419 if( p->zBaseline!=0 ){
1420 static int once = 0;
1421 if( !once ){
1422 db_set_int("seen-delta-manifest", 1, 0);
1423 once = 0;
1424 }
1425 }
1426 }
1427 }
1428 if( p->type==CFTYPE_CLUSTER ){
1429 tag_insert("cluster", 1, 0, rid, p->rDate, rid);
1430 for(i=0; i<p->nCChild; i++){
1431 int mid;
1432 mid = uuid_to_rid(p->azCChild[i], 1);
1433 if( mid>0 ){
1434 db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid);
1435 }
1436 }
1437 }
1438 if( p->type==CFTYPE_CONTROL
1439 || p->type==CFTYPE_MANIFEST
1440 || p->type==CFTYPE_EVENT
1441 ){
1442 for(i=0; i<p->nTag; i++){
1443 int tid;
1444 int type;
1445 if( p->aTag[i].zUuid ){
1446 tid = uuid_to_rid(p->aTag[i].zUuid, 1);
1447 }else{
1448 tid = rid;
1449 }
1450 if( tid ){
1451 switch( p->aTag[i].zName[0] ){
1452 case '-': type = 0; break; /* Cancel prior occurances */
1453 case '+': type = 1; break; /* Apply to target only */
1454 case '*': type = 2; break; /* Propagate to descendants */
1455 default:
1456 fossil_fatal("unknown tag type in manifest: %s", p->aTag);
1457 return 0;
1458 }
1459 tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue,
1460 rid, p->rDate, tid);
1461 }
1462 }
1463 if( parentid ){
1464 tag_propagate_all(parentid);
1465 }
1466 }
1467 if( p->type==CFTYPE_WIKI ){
1468 char *zTag = mprintf("wiki-%s", p->zWikiTitle);
1469 int tagid = tag_findid(zTag, 1);
1470 int prior;
1471 char *zComment;
1472 int nWiki;
1473 char zLength[40];
1474 while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
1475 nWiki = strlen(p->zWiki);
1476 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1477 tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
1478 free(zTag);
1479 prior = db_int(0,
1480 "SELECT rid FROM tagxref"
1481 " WHERE tagid=%d AND mtime<%.17g"
1482 " ORDER BY mtime DESC",
1483 tagid, p->rDate
1484 );
1485 if( prior ){
1486 content_deltify(prior, rid, 0);
1487 }
1488 if( nWiki>0 ){
1489 zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
1490 }else{
1491 zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
1492 }
1493 db_multi_exec(
1494 "REPLACE INTO event(type,mtime,objid,user,comment,"
1495 " bgcolor,euser,ecomment)"
1496 "VALUES('w',%.17g,%d,%Q,%Q,"
1497 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
1498 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
1499 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1500 p->rDate, rid, p->zUser, zComment,
1501 TAG_BGCOLOR, rid,
1502 TAG_BGCOLOR, rid,
1503 TAG_USER, rid,
1504 TAG_COMMENT, rid
1505 );
1506 free(zComment);
1507 }
1508 if( p->type==CFTYPE_EVENT ){
1509 char *zTag = mprintf("event-%s", p->zEventId);
1510 int tagid = tag_findid(zTag, 1);
1511 int prior, subsequent;
1512 int nWiki;
1513 char zLength[40];
1514 while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
1515 nWiki = strlen(p->zWiki);
1516 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1517 tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
1518 free(zTag);
1519 prior = db_int(0,
1520 "SELECT rid FROM tagxref"
1521 " WHERE tagid=%d AND mtime<%.17g"
1522 " ORDER BY mtime DESC",
1523 tagid, p->rDate
1524 );
1525 if( prior ){
1526 content_deltify(prior, rid, 0);
1527 db_multi_exec(
1528 "DELETE FROM event"
@@ -1281,108 +1534,87 @@
1534 }
1535 subsequent = db_int(0,
1536 "SELECT rid FROM tagxref"
1537 " WHERE tagid=%d AND mtime>%.17g"
1538 " ORDER BY mtime",
1539 tagid, p->rDate
1540 );
1541 if( subsequent ){
1542 content_deltify(rid, subsequent, 0);
1543 }else{
1544 db_multi_exec(
1545 "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
1546 "VALUES('e',%.17g,%d,%d,%Q,%Q,"
1547 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1548 p->rEventDate, rid, tagid, p->zUser, p->zComment,
1549 TAG_BGCOLOR, rid
1550 );
1551 }
1552 }
1553 if( p->type==CFTYPE_TICKET ){
1554 char *zTag;
1555
1556 assert( manifest_crosslink_busy==1 );
1557 zTag = mprintf("tkt-%s", p->zTicketUuid);
1558 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1559 free(zTag);
1560 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
1561 p->zTicketUuid);
1562 }
1563 if( p->type==CFTYPE_ATTACHMENT ){
1564 db_multi_exec(
1565 "INSERT INTO attachment(attachid, mtime, src, target,"
1566 "filename, comment, user)"
1567 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
1568 rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
1569 (p->zComment ? p->zComment : ""), p->zUser
1570 );
1571 db_multi_exec(
1572 "UPDATE attachment SET isLatest = (mtime=="
1573 "(SELECT max(mtime) FROM attachment"
1574 " WHERE target=%Q AND filename=%Q))"
1575 " WHERE target=%Q AND filename=%Q",
1576 p->zAttachTarget, p->zAttachName,
1577 p->zAttachTarget, p->zAttachName
1578 );
1579 if( strlen(p->zAttachTarget)!=UUID_SIZE
1580 || !validate16(p->zAttachTarget, UUID_SIZE)
1581 ){
1582 char *zComment;
1583 if( p->zAttachSrc && p->zAttachSrc[0] ){
1584 zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
1585 p->zAttachName, p->zAttachTarget);
1586 }else{
1587 zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
1588 p->zAttachName, p->zAttachTarget);
1589 }
1590 db_multi_exec(
1591 "REPLACE INTO event(type,mtime,objid,user,comment)"
1592 "VALUES('w',%.17g,%d,%Q,%Q)",
1593 p->rDate, rid, p->zUser, zComment
1594 );
1595 free(zComment);
1596 }else{
1597 char *zComment;
1598 if( p->zAttachSrc && p->zAttachSrc[0] ){
1599 zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
1600 p->zAttachName, p->zAttachTarget);
1601 }else{
1602 zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
1603 p->zAttachName, p->zAttachTarget);
1604 }
1605 db_multi_exec(
1606 "REPLACE INTO event(type,mtime,objid,user,comment)"
1607 "VALUES('t',%.17g,%d,%Q,%Q)",
1608 p->rDate, rid, p->zUser, zComment
1609 );
1610 free(zComment);
1611 }
1612 }
1613 db_end_transaction(0);
1614 if( p->type==CFTYPE_MANIFEST ){
1615 manifest_cache_insert(p);
1616 }else{
1617 manifest_destroy(p);
1618 }
1619 return 1;
1620 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1621
+27 -1
--- src/md5.c
+++ src/md5.c
@@ -41,12 +41,16 @@
4141
uint32 bits[2];
4242
unsigned char in[64];
4343
};
4444
typedef struct Context MD5Context;
4545
46
+#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32)
47
+# define byteReverse(A,B)
48
+#else
4649
/*
47
- * Note: this code is harmless on little-endian machines.
50
+ * Convert an array of integers to little-endian.
51
+ * Note: this code is a no-op on little-endian machines.
4852
*/
4953
static void byteReverse (unsigned char *buf, unsigned longs){
5054
uint32 t;
5155
do {
5256
t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
@@ -53,10 +57,12 @@
5357
((unsigned)buf[1]<<8 | buf[0]);
5458
*(uint32 *)buf = t;
5559
buf += 4;
5660
} while (--longs);
5761
}
62
+#endif
63
+
5864
/* The four core functions - F1 is optimized somewhat */
5965
6066
/* #define F1(x, y, z) (x & y | ~x & z) */
6167
#define F1(x, y, z) (z ^ (x & (y ^ z)))
6268
#define F2(x, y, z) F1(z, x, y)
@@ -314,10 +320,30 @@
314320
** Add the content of a blob to the incremental MD5 checksum.
315321
*/
316322
void md5sum_step_blob(Blob *p){
317323
md5sum_step_text(blob_buffer(p), blob_size(p));
318324
}
325
+
326
+/*
327
+** For trouble-shooting only:
328
+**
329
+** Report the current state of the incremental checksum.
330
+*/
331
+const char *md5sum_current_state(void){
332
+ unsigned int cksum = 0;
333
+ unsigned int *pFirst, *pLast;
334
+ static char zResult[12];
335
+
336
+ pFirst = (unsigned int*)&incrCtx;
337
+ pLast = (unsigned int*)((&incrCtx)+1);
338
+ while( pFirst<pLast ){
339
+ cksum += *pFirst;
340
+ pFirst++;
341
+ }
342
+ sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum);
343
+ return zResult;
344
+}
319345
320346
/*
321347
** Finish the incremental MD5 checksum. Store the result in blob pOut
322348
** if pOut!=0. Also return a pointer to the result.
323349
**
324350
--- src/md5.c
+++ src/md5.c
@@ -41,12 +41,16 @@
41 uint32 bits[2];
42 unsigned char in[64];
43 };
44 typedef struct Context MD5Context;
45
 
 
 
46 /*
47 * Note: this code is harmless on little-endian machines.
 
48 */
49 static void byteReverse (unsigned char *buf, unsigned longs){
50 uint32 t;
51 do {
52 t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
@@ -53,10 +57,12 @@
53 ((unsigned)buf[1]<<8 | buf[0]);
54 *(uint32 *)buf = t;
55 buf += 4;
56 } while (--longs);
57 }
 
 
58 /* The four core functions - F1 is optimized somewhat */
59
60 /* #define F1(x, y, z) (x & y | ~x & z) */
61 #define F1(x, y, z) (z ^ (x & (y ^ z)))
62 #define F2(x, y, z) F1(z, x, y)
@@ -314,10 +320,30 @@
314 ** Add the content of a blob to the incremental MD5 checksum.
315 */
316 void md5sum_step_blob(Blob *p){
317 md5sum_step_text(blob_buffer(p), blob_size(p));
318 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
320 /*
321 ** Finish the incremental MD5 checksum. Store the result in blob pOut
322 ** if pOut!=0. Also return a pointer to the result.
323 **
324
--- src/md5.c
+++ src/md5.c
@@ -41,12 +41,16 @@
41 uint32 bits[2];
42 unsigned char in[64];
43 };
44 typedef struct Context MD5Context;
45
46 #if defined(__i386__) || defined(__x86_64__) || defined(_WIN32)
47 # define byteReverse(A,B)
48 #else
49 /*
50 * Convert an array of integers to little-endian.
51 * Note: this code is a no-op on little-endian machines.
52 */
53 static void byteReverse (unsigned char *buf, unsigned longs){
54 uint32 t;
55 do {
56 t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
@@ -53,10 +57,12 @@
57 ((unsigned)buf[1]<<8 | buf[0]);
58 *(uint32 *)buf = t;
59 buf += 4;
60 } while (--longs);
61 }
62 #endif
63
64 /* The four core functions - F1 is optimized somewhat */
65
66 /* #define F1(x, y, z) (x & y | ~x & z) */
67 #define F1(x, y, z) (z ^ (x & (y ^ z)))
68 #define F2(x, y, z) F1(z, x, y)
@@ -314,10 +320,30 @@
320 ** Add the content of a blob to the incremental MD5 checksum.
321 */
322 void md5sum_step_blob(Blob *p){
323 md5sum_step_text(blob_buffer(p), blob_size(p));
324 }
325
326 /*
327 ** For trouble-shooting only:
328 **
329 ** Report the current state of the incremental checksum.
330 */
331 const char *md5sum_current_state(void){
332 unsigned int cksum = 0;
333 unsigned int *pFirst, *pLast;
334 static char zResult[12];
335
336 pFirst = (unsigned int*)&incrCtx;
337 pLast = (unsigned int*)((&incrCtx)+1);
338 while( pFirst<pLast ){
339 cksum += *pFirst;
340 pFirst++;
341 }
342 sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum);
343 return zResult;
344 }
345
346 /*
347 ** Finish the incremental MD5 checksum. Store the result in blob pOut
348 ** if pOut!=0. Also return a pointer to the result.
349 **
350
+22 -8
--- src/rebuild.c
+++ src/rebuild.c
@@ -82,10 +82,25 @@
8282
static int ttyOutput; /* Do progress output */
8383
static Bag bagDone; /* Bag of records rebuilt */
8484
8585
static char *zFNameFormat; /* Format string for filenames on deconstruct */
8686
static int prefixLength; /* Length of directory prefix for deconstruct */
87
+
88
+
89
+/*
90
+** Draw the percent-complete message.
91
+** The input is actually the permill complete.
92
+*/
93
+static void percent_complete(int permill){
94
+ static int lastOutput = -1;
95
+ if( permill>lastOutput ){
96
+ printf(" %d.%d%% complete...\r", permill/10, permill%10);
97
+ fflush(stdout);
98
+ lastOutput = permill;
99
+ }
100
+}
101
+
87102
88103
/*
89104
** Called after each artifact is processed
90105
*/
91106
static void rebuild_step_done(rid){
@@ -92,12 +107,11 @@
92107
/* assert( bag_find(&bagDone, rid)==0 ); */
93108
bag_insert(&bagDone, rid);
94109
if( ttyOutput ){
95110
processCnt++;
96111
if (!g.fQuiet) {
97
- printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize));
98
- fflush(stdout);
112
+ percent_complete((processCnt*1000)/totalSize);
99113
}
100114
}
101115
}
102116
103117
/*
@@ -167,20 +181,21 @@
167181
rebuild_step_done(rid);
168182
169183
/* Call all children recursively */
170184
rid = 0;
171185
for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
172
- Stmt q2;
186
+ static Stmt q2;
173187
int sz;
174
- db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
188
+ db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid");
189
+ db_bind_int(&q2, ":rid", cid);
175190
if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
176191
Blob delta, next;
177192
db_ephemeral_blob(&q2, 0, &delta);
178193
blob_uncompress(&delta, &delta);
179194
blob_delta_apply(pBase, &delta, &next);
180195
blob_reset(&delta);
181
- db_finalize(&q2);
196
+ db_reset(&q2);
182197
if( i<nChild ){
183198
rebuild_step(cid, sz, &next);
184199
}else{
185200
/* Tail recursion */
186201
rid = cid;
@@ -187,11 +202,11 @@
187202
size = sz;
188203
blob_reset(pBase);
189204
*pBase = next;
190205
}
191206
}else{
192
- db_finalize(&q2);
207
+ db_reset(&q2);
193208
blob_reset(pBase);
194209
}
195210
}
196211
bag_clear(&children);
197212
}
@@ -237,12 +252,11 @@
237252
238253
bag_init(&bagDone);
239254
ttyOutput = doOut;
240255
processCnt = 0;
241256
if (!g.fQuiet) {
242
- printf("0 (0%%)...\r");
243
- fflush(stdout);
257
+ percent_complete(0);
244258
}
245259
db_multi_exec(zSchemaUpdates);
246260
for(;;){
247261
zTable = db_text(0,
248262
"SELECT name FROM sqlite_master /*scan*/"
249263
--- src/rebuild.c
+++ src/rebuild.c
@@ -82,10 +82,25 @@
82 static int ttyOutput; /* Do progress output */
83 static Bag bagDone; /* Bag of records rebuilt */
84
85 static char *zFNameFormat; /* Format string for filenames on deconstruct */
86 static int prefixLength; /* Length of directory prefix for deconstruct */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
88 /*
89 ** Called after each artifact is processed
90 */
91 static void rebuild_step_done(rid){
@@ -92,12 +107,11 @@
92 /* assert( bag_find(&bagDone, rid)==0 ); */
93 bag_insert(&bagDone, rid);
94 if( ttyOutput ){
95 processCnt++;
96 if (!g.fQuiet) {
97 printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize));
98 fflush(stdout);
99 }
100 }
101 }
102
103 /*
@@ -167,20 +181,21 @@
167 rebuild_step_done(rid);
168
169 /* Call all children recursively */
170 rid = 0;
171 for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
172 Stmt q2;
173 int sz;
174 db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
 
175 if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
176 Blob delta, next;
177 db_ephemeral_blob(&q2, 0, &delta);
178 blob_uncompress(&delta, &delta);
179 blob_delta_apply(pBase, &delta, &next);
180 blob_reset(&delta);
181 db_finalize(&q2);
182 if( i<nChild ){
183 rebuild_step(cid, sz, &next);
184 }else{
185 /* Tail recursion */
186 rid = cid;
@@ -187,11 +202,11 @@
187 size = sz;
188 blob_reset(pBase);
189 *pBase = next;
190 }
191 }else{
192 db_finalize(&q2);
193 blob_reset(pBase);
194 }
195 }
196 bag_clear(&children);
197 }
@@ -237,12 +252,11 @@
237
238 bag_init(&bagDone);
239 ttyOutput = doOut;
240 processCnt = 0;
241 if (!g.fQuiet) {
242 printf("0 (0%%)...\r");
243 fflush(stdout);
244 }
245 db_multi_exec(zSchemaUpdates);
246 for(;;){
247 zTable = db_text(0,
248 "SELECT name FROM sqlite_master /*scan*/"
249
--- src/rebuild.c
+++ src/rebuild.c
@@ -82,10 +82,25 @@
82 static int ttyOutput; /* Do progress output */
83 static Bag bagDone; /* Bag of records rebuilt */
84
85 static char *zFNameFormat; /* Format string for filenames on deconstruct */
86 static int prefixLength; /* Length of directory prefix for deconstruct */
87
88
89 /*
90 ** Draw the percent-complete message.
91 ** The input is actually the permill complete.
92 */
93 static void percent_complete(int permill){
94 static int lastOutput = -1;
95 if( permill>lastOutput ){
96 printf(" %d.%d%% complete...\r", permill/10, permill%10);
97 fflush(stdout);
98 lastOutput = permill;
99 }
100 }
101
102
103 /*
104 ** Called after each artifact is processed
105 */
106 static void rebuild_step_done(rid){
@@ -92,12 +107,11 @@
107 /* assert( bag_find(&bagDone, rid)==0 ); */
108 bag_insert(&bagDone, rid);
109 if( ttyOutput ){
110 processCnt++;
111 if (!g.fQuiet) {
112 percent_complete((processCnt*1000)/totalSize);
 
113 }
114 }
115 }
116
117 /*
@@ -167,20 +181,21 @@
181 rebuild_step_done(rid);
182
183 /* Call all children recursively */
184 rid = 0;
185 for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
186 static Stmt q2;
187 int sz;
188 db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid");
189 db_bind_int(&q2, ":rid", cid);
190 if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
191 Blob delta, next;
192 db_ephemeral_blob(&q2, 0, &delta);
193 blob_uncompress(&delta, &delta);
194 blob_delta_apply(pBase, &delta, &next);
195 blob_reset(&delta);
196 db_reset(&q2);
197 if( i<nChild ){
198 rebuild_step(cid, sz, &next);
199 }else{
200 /* Tail recursion */
201 rid = cid;
@@ -187,11 +202,11 @@
202 size = sz;
203 blob_reset(pBase);
204 *pBase = next;
205 }
206 }else{
207 db_reset(&q2);
208 blob_reset(pBase);
209 }
210 }
211 bag_clear(&children);
212 }
@@ -237,12 +252,11 @@
252
253 bag_init(&bagDone);
254 ttyOutput = doOut;
255 processCnt = 0;
256 if (!g.fQuiet) {
257 percent_complete(0);
 
258 }
259 db_multi_exec(zSchemaUpdates);
260 for(;;){
261 zTable = db_text(0,
262 "SELECT name FROM sqlite_master /*scan*/"
263
+22 -8
--- src/rebuild.c
+++ src/rebuild.c
@@ -82,10 +82,25 @@
8282
static int ttyOutput; /* Do progress output */
8383
static Bag bagDone; /* Bag of records rebuilt */
8484
8585
static char *zFNameFormat; /* Format string for filenames on deconstruct */
8686
static int prefixLength; /* Length of directory prefix for deconstruct */
87
+
88
+
89
+/*
90
+** Draw the percent-complete message.
91
+** The input is actually the permill complete.
92
+*/
93
+static void percent_complete(int permill){
94
+ static int lastOutput = -1;
95
+ if( permill>lastOutput ){
96
+ printf(" %d.%d%% complete...\r", permill/10, permill%10);
97
+ fflush(stdout);
98
+ lastOutput = permill;
99
+ }
100
+}
101
+
87102
88103
/*
89104
** Called after each artifact is processed
90105
*/
91106
static void rebuild_step_done(rid){
@@ -92,12 +107,11 @@
92107
/* assert( bag_find(&bagDone, rid)==0 ); */
93108
bag_insert(&bagDone, rid);
94109
if( ttyOutput ){
95110
processCnt++;
96111
if (!g.fQuiet) {
97
- printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize));
98
- fflush(stdout);
112
+ percent_complete((processCnt*1000)/totalSize);
99113
}
100114
}
101115
}
102116
103117
/*
@@ -167,20 +181,21 @@
167181
rebuild_step_done(rid);
168182
169183
/* Call all children recursively */
170184
rid = 0;
171185
for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
172
- Stmt q2;
186
+ static Stmt q2;
173187
int sz;
174
- db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
188
+ db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid");
189
+ db_bind_int(&q2, ":rid", cid);
175190
if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
176191
Blob delta, next;
177192
db_ephemeral_blob(&q2, 0, &delta);
178193
blob_uncompress(&delta, &delta);
179194
blob_delta_apply(pBase, &delta, &next);
180195
blob_reset(&delta);
181
- db_finalize(&q2);
196
+ db_reset(&q2);
182197
if( i<nChild ){
183198
rebuild_step(cid, sz, &next);
184199
}else{
185200
/* Tail recursion */
186201
rid = cid;
@@ -187,11 +202,11 @@
187202
size = sz;
188203
blob_reset(pBase);
189204
*pBase = next;
190205
}
191206
}else{
192
- db_finalize(&q2);
207
+ db_reset(&q2);
193208
blob_reset(pBase);
194209
}
195210
}
196211
bag_clear(&children);
197212
}
@@ -237,12 +252,11 @@
237252
238253
bag_init(&bagDone);
239254
ttyOutput = doOut;
240255
processCnt = 0;
241256
if (!g.fQuiet) {
242
- printf("0 (0%%)...\r");
243
- fflush(stdout);
257
+ percent_complete(0);
244258
}
245259
db_multi_exec(zSchemaUpdates);
246260
for(;;){
247261
zTable = db_text(0,
248262
"SELECT name FROM sqlite_master /*scan*/"
249263
--- src/rebuild.c
+++ src/rebuild.c
@@ -82,10 +82,25 @@
82 static int ttyOutput; /* Do progress output */
83 static Bag bagDone; /* Bag of records rebuilt */
84
85 static char *zFNameFormat; /* Format string for filenames on deconstruct */
86 static int prefixLength; /* Length of directory prefix for deconstruct */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
88 /*
89 ** Called after each artifact is processed
90 */
91 static void rebuild_step_done(rid){
@@ -92,12 +107,11 @@
92 /* assert( bag_find(&bagDone, rid)==0 ); */
93 bag_insert(&bagDone, rid);
94 if( ttyOutput ){
95 processCnt++;
96 if (!g.fQuiet) {
97 printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize));
98 fflush(stdout);
99 }
100 }
101 }
102
103 /*
@@ -167,20 +181,21 @@
167 rebuild_step_done(rid);
168
169 /* Call all children recursively */
170 rid = 0;
171 for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
172 Stmt q2;
173 int sz;
174 db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
 
175 if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
176 Blob delta, next;
177 db_ephemeral_blob(&q2, 0, &delta);
178 blob_uncompress(&delta, &delta);
179 blob_delta_apply(pBase, &delta, &next);
180 blob_reset(&delta);
181 db_finalize(&q2);
182 if( i<nChild ){
183 rebuild_step(cid, sz, &next);
184 }else{
185 /* Tail recursion */
186 rid = cid;
@@ -187,11 +202,11 @@
187 size = sz;
188 blob_reset(pBase);
189 *pBase = next;
190 }
191 }else{
192 db_finalize(&q2);
193 blob_reset(pBase);
194 }
195 }
196 bag_clear(&children);
197 }
@@ -237,12 +252,11 @@
237
238 bag_init(&bagDone);
239 ttyOutput = doOut;
240 processCnt = 0;
241 if (!g.fQuiet) {
242 printf("0 (0%%)...\r");
243 fflush(stdout);
244 }
245 db_multi_exec(zSchemaUpdates);
246 for(;;){
247 zTable = db_text(0,
248 "SELECT name FROM sqlite_master /*scan*/"
249
--- src/rebuild.c
+++ src/rebuild.c
@@ -82,10 +82,25 @@
82 static int ttyOutput; /* Do progress output */
83 static Bag bagDone; /* Bag of records rebuilt */
84
85 static char *zFNameFormat; /* Format string for filenames on deconstruct */
86 static int prefixLength; /* Length of directory prefix for deconstruct */
87
88
89 /*
90 ** Draw the percent-complete message.
91 ** The input is actually the permill complete.
92 */
93 static void percent_complete(int permill){
94 static int lastOutput = -1;
95 if( permill>lastOutput ){
96 printf(" %d.%d%% complete...\r", permill/10, permill%10);
97 fflush(stdout);
98 lastOutput = permill;
99 }
100 }
101
102
103 /*
104 ** Called after each artifact is processed
105 */
106 static void rebuild_step_done(rid){
@@ -92,12 +107,11 @@
107 /* assert( bag_find(&bagDone, rid)==0 ); */
108 bag_insert(&bagDone, rid);
109 if( ttyOutput ){
110 processCnt++;
111 if (!g.fQuiet) {
112 percent_complete((processCnt*1000)/totalSize);
 
113 }
114 }
115 }
116
117 /*
@@ -167,20 +181,21 @@
181 rebuild_step_done(rid);
182
183 /* Call all children recursively */
184 rid = 0;
185 for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
186 static Stmt q2;
187 int sz;
188 db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid");
189 db_bind_int(&q2, ":rid", cid);
190 if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
191 Blob delta, next;
192 db_ephemeral_blob(&q2, 0, &delta);
193 blob_uncompress(&delta, &delta);
194 blob_delta_apply(pBase, &delta, &next);
195 blob_reset(&delta);
196 db_reset(&q2);
197 if( i<nChild ){
198 rebuild_step(cid, sz, &next);
199 }else{
200 /* Tail recursion */
201 rid = cid;
@@ -187,11 +202,11 @@
202 size = sz;
203 blob_reset(pBase);
204 *pBase = next;
205 }
206 }else{
207 db_reset(&q2);
208 blob_reset(pBase);
209 }
210 }
211 bag_clear(&children);
212 }
@@ -237,12 +252,11 @@
252
253 bag_init(&bagDone);
254 ttyOutput = doOut;
255 processCnt = 0;
256 if (!g.fQuiet) {
257 percent_complete(0);
 
258 }
259 db_multi_exec(zSchemaUpdates);
260 for(;;){
261 zTable = db_text(0,
262 "SELECT name FROM sqlite_master /*scan*/"
263
+198 -425
--- src/sha1.c
+++ src/sha1.c
@@ -1,417 +1,190 @@
11
/*
2
-** This implementation of SHA1 is adapted from the example implementation
3
-** contained in RFC-3174.
2
+** This implementation of SHA1.
43
*/
5
-/*
6
- * If you do not have the ISO standard stdint.h header file, then you
7
- * must typdef the following:
8
- * name meaning
9
- * */
10
-#if defined(__DMC__) || defined(_MSC_VER)
11
- typedef unsigned long uint32_t; //unsigned 32 bit integer
12
- typedef unsigned char uint8_t; //unsigned 8 bit integer (i.e., unsigned char)
13
-#else
14
-# include <stdint.h>
15
-#endif
164
#include <sys/types.h>
175
#include "config.h"
186
#include "sha1.h"
197
20
-#define SHA1HashSize 20
21
-#define shaSuccess 0
22
-#define shaInputTooLong 1
23
-#define shaStateError 2
24
-
25
-/*
26
- * This structure will hold context information for the SHA-1
27
- * hashing operation
28
- */
29
-typedef struct SHA1Context SHA1Context;
30
-struct SHA1Context {
31
- uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
32
-
33
- uint32_t Length_Low; /* Message length in bits */
34
- uint32_t Length_High; /* Message length in bits */
35
-
36
- int Message_Block_Index; /* Index into message block array */
37
- uint8_t Message_Block[64]; /* 512-bit message blocks */
38
-
39
- int Computed; /* Is the digest computed? */
40
- int Corrupted; /* Is the message digest corrupted? */
41
-};
42
-
43
-/*
44
- * sha1.c
45
- *
46
- * Description:
47
- * This file implements the Secure Hashing Algorithm 1 as
48
- * defined in FIPS PUB 180-1 published April 17, 1995.
49
- *
50
- * The SHA-1, produces a 160-bit message digest for a given
51
- * data stream. It should take about 2**n steps to find a
52
- * message with the same digest as a given message and
53
- * 2**(n/2) to find any two messages with the same digest,
54
- * when n is the digest size in bits. Therefore, this
55
- * algorithm can serve as a means of providing a
56
- * "fingerprint" for a message.
57
- *
58
- * Portability Issues:
59
- * SHA-1 is defined in terms of 32-bit "words". This code
60
- * uses <stdint.h> (included via "sha1.h" to define 32 and 8
61
- * bit unsigned integer types. If your C compiler does not
62
- * support 32 bit unsigned integers, this code is not
63
- * appropriate.
64
- *
65
- * Caveats:
66
- * SHA-1 is designed to work with messages less than 2^64 bits
67
- * long. Although SHA-1 allows a message digest to be generated
68
- * for messages of any number of bits less than 2^64, this
69
- * implementation only works with messages with a length that is
70
- * a multiple of the size of an 8-bit character.
71
- *
72
- */
73
-
74
-/*
75
- * Define the SHA1 circular left shift macro
76
- */
77
-#define SHA1CircularShift(bits,word) \
78
- (((word) << (bits)) | ((word) >> (32-(bits))))
79
-
80
-/* Local Function Prototyptes */
81
-static void SHA1PadMessage(SHA1Context *);
82
-static void SHA1ProcessMessageBlock(SHA1Context *);
83
-
84
-/*
85
- * SHA1Reset
86
- *
87
- * Description:
88
- * This function will initialize the SHA1Context in preparation
89
- * for computing a new SHA1 message digest.
90
- *
91
- * Parameters:
92
- * context: [in/out]
93
- * The context to reset.
94
- *
95
- * Returns:
96
- * sha Error Code.
97
- *
98
- */
99
-static int SHA1Reset(SHA1Context *context)
100
-{
101
- context->Length_Low = 0;
102
- context->Length_High = 0;
103
- context->Message_Block_Index = 0;
104
-
105
- context->Intermediate_Hash[0] = 0x67452301;
106
- context->Intermediate_Hash[1] = 0xEFCDAB89;
107
- context->Intermediate_Hash[2] = 0x98BADCFE;
108
- context->Intermediate_Hash[3] = 0x10325476;
109
- context->Intermediate_Hash[4] = 0xC3D2E1F0;
110
-
111
- context->Computed = 0;
112
- context->Corrupted = 0;
113
-
114
- return shaSuccess;
115
-}
116
-
117
-/*
118
- * SHA1Result
119
- *
120
- * Description:
121
- * This function will return the 160-bit message digest into the
122
- * Message_Digest array provided by the caller.
123
- * NOTE: The first octet of hash is stored in the 0th element,
124
- * the last octet of hash in the 19th element.
125
- *
126
- * Parameters:
127
- * context: [in/out]
128
- * The context to use to calculate the SHA-1 hash.
129
- * Message_Digest: [out]
130
- * Where the digest is returned.
131
- *
132
- * Returns:
133
- * sha Error Code.
134
- *
135
- */
136
-static int SHA1Result( SHA1Context *context,
137
- uint8_t Message_Digest[SHA1HashSize])
138
-{
139
- int i;
140
-
141
- if (context->Corrupted)
142
- {
143
- return context->Corrupted;
144
- }
145
-
146
- if (!context->Computed)
147
- {
148
- SHA1PadMessage(context);
149
- for(i=0; i<64; ++i)
150
- {
151
- /* message may be sensitive, clear it out */
152
- context->Message_Block[i] = 0;
153
- }
154
- context->Length_Low = 0; /* and clear length */
155
- context->Length_High = 0;
156
- context->Computed = 1;
157
-
158
- }
159
-
160
- for(i = 0; i < SHA1HashSize; ++i)
161
- {
162
- Message_Digest[i] = context->Intermediate_Hash[i>>2]
163
- >> 8 * ( 3 - ( i & 0x03 ) );
164
- }
165
-
166
- return shaSuccess;
167
-}
168
-
169
-/*
170
- * SHA1Input
171
- *
172
- * Description:
173
- * This function accepts an array of octets as the next portion
174
- * of the message.
175
- *
176
- * Parameters:
177
- * context: [in/out]
178
- * The SHA context to update
179
- * message_array: [in]
180
- * An array of characters representing the next portion of
181
- * the message.
182
- * length: [in]
183
- * The length of the message in message_array
184
- *
185
- * Returns:
186
- * sha Error Code.
187
- *
188
- */
189
-static
190
-int SHA1Input( SHA1Context *context,
191
- const uint8_t *message_array,
192
- unsigned length)
193
-{
194
- if (!length)
195
- {
196
- return shaSuccess;
197
- }
198
-
199
- if (context->Computed)
200
- {
201
- context->Corrupted = shaStateError;
202
-
203
- return shaStateError;
204
- }
205
-
206
- if (context->Corrupted)
207
- {
208
- return context->Corrupted;
209
- }
210
- while(length-- && !context->Corrupted)
211
- {
212
- context->Message_Block[context->Message_Block_Index++] =
213
- (*message_array & 0xFF);
214
-
215
- context->Length_Low += 8;
216
- if (context->Length_Low == 0)
217
- {
218
- context->Length_High++;
219
- if (context->Length_High == 0)
220
- {
221
- /* Message is too long */
222
- context->Corrupted = 1;
223
- }
224
- }
225
-
226
- if (context->Message_Block_Index == 64)
227
- {
228
- SHA1ProcessMessageBlock(context);
229
- }
230
-
231
- message_array++;
232
- }
233
-
234
- return shaSuccess;
235
-}
236
-
237
-/*
238
- * SHA1ProcessMessageBlock
239
- *
240
- * Description:
241
- * This function will process the next 512 bits of the message
242
- * stored in the Message_Block array.
243
- *
244
- * Parameters:
245
- * None.
246
- *
247
- * Returns:
248
- * Nothing.
249
- *
250
- * Comments:
251
- * Many of the variable names in this code, especially the
252
- * single character names, were used because those were the
253
- * names used in the publication.
254
- *
255
- *
256
- */
257
-static void SHA1ProcessMessageBlock(SHA1Context *context)
258
-{
259
- const uint32_t K[] = { /* Constants defined in SHA-1 */
260
- 0x5A827999,
261
- 0x6ED9EBA1,
262
- 0x8F1BBCDC,
263
- 0xCA62C1D6
264
- };
265
- int t; /* Loop counter */
266
- uint32_t temp; /* Temporary word value */
267
- uint32_t W[80]; /* Word sequence */
268
- uint32_t A, B, C, D, E; /* Word buffers */
269
-
270
- /*
271
- * Initialize the first 16 words in the array W
272
- */
273
- for(t = 0; t < 16; t++)
274
- {
275
- W[t] = context->Message_Block[t * 4] << 24;
276
- W[t] |= context->Message_Block[t * 4 + 1] << 16;
277
- W[t] |= context->Message_Block[t * 4 + 2] << 8;
278
- W[t] |= context->Message_Block[t * 4 + 3];
279
- }
280
-
281
- for(t = 16; t < 80; t++)
282
- {
283
- W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
284
- }
285
-
286
- A = context->Intermediate_Hash[0];
287
- B = context->Intermediate_Hash[1];
288
- C = context->Intermediate_Hash[2];
289
- D = context->Intermediate_Hash[3];
290
- E = context->Intermediate_Hash[4];
291
-
292
- for(t = 0; t < 20; t++)
293
- {
294
- temp = SHA1CircularShift(5,A) +
295
- ((B & C) | ((~B) & D)) + E + W[t] + K[0];
296
- E = D;
297
- D = C;
298
- C = SHA1CircularShift(30,B);
299
-
300
- B = A;
301
- A = temp;
302
- }
303
-
304
- for(t = 20; t < 40; t++)
305
- {
306
- temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
307
- E = D;
308
- D = C;
309
- C = SHA1CircularShift(30,B);
310
- B = A;
311
- A = temp;
312
- }
313
-
314
- for(t = 40; t < 60; t++)
315
- {
316
- temp = SHA1CircularShift(5,A) +
317
- ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
318
- E = D;
319
- D = C;
320
- C = SHA1CircularShift(30,B);
321
- B = A;
322
- A = temp;
323
- }
324
-
325
- for(t = 60; t < 80; t++)
326
- {
327
- temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
328
- E = D;
329
- D = C;
330
- C = SHA1CircularShift(30,B);
331
- B = A;
332
- A = temp;
333
- }
334
-
335
- context->Intermediate_Hash[0] += A;
336
- context->Intermediate_Hash[1] += B;
337
- context->Intermediate_Hash[2] += C;
338
- context->Intermediate_Hash[3] += D;
339
- context->Intermediate_Hash[4] += E;
340
-
341
- context->Message_Block_Index = 0;
342
-}
343
-
344
-/*
345
- * SHA1PadMessage
346
- *
347
-
348
- * Description:
349
- * According to the standard, the message must be padded to an even
350
- * 512 bits. The first padding bit must be a '1'. The last 64
351
- * bits represent the length of the original message. All bits in
352
- * between should be 0. This function will pad the message
353
- * according to those rules by filling the Message_Block array
354
- * accordingly. It will also call the ProcessMessageBlock function
355
- * provided appropriately. When it returns, it can be assumed that
356
- * the message digest has been computed.
357
- *
358
- * Parameters:
359
- * context: [in/out]
360
- * The context to pad
361
- * ProcessMessageBlock: [in]
362
- * The appropriate SHA*ProcessMessageBlock function
363
- * Returns:
364
- * Nothing.
365
- *
366
- */
367
-static void SHA1PadMessage(SHA1Context *context)
368
-{
369
- /*
370
- * Check to see if the current message block is too small to hold
371
- * the initial padding bits and length. If so, we will pad the
372
- * block, process it, and then continue padding into a second
373
- * block.
374
- */
375
- if (context->Message_Block_Index > 55)
376
- {
377
- context->Message_Block[context->Message_Block_Index++] = 0x80;
378
- while(context->Message_Block_Index < 64)
379
- {
380
- context->Message_Block[context->Message_Block_Index++] = 0;
381
- }
382
-
383
- SHA1ProcessMessageBlock(context);
384
-
385
- while(context->Message_Block_Index < 56)
386
- {
387
- context->Message_Block[context->Message_Block_Index++] = 0;
388
- }
389
- }
390
- else
391
- {
392
- context->Message_Block[context->Message_Block_Index++] = 0x80;
393
- while(context->Message_Block_Index < 56)
394
- {
395
-
396
- context->Message_Block[context->Message_Block_Index++] = 0;
397
- }
398
- }
399
-
400
- /*
401
- * Store the message length as the last 8 octets
402
- */
403
- context->Message_Block[56] = context->Length_High >> 24;
404
- context->Message_Block[57] = context->Length_High >> 16;
405
- context->Message_Block[58] = context->Length_High >> 8;
406
- context->Message_Block[59] = context->Length_High;
407
- context->Message_Block[60] = context->Length_Low >> 24;
408
- context->Message_Block[61] = context->Length_Low >> 16;
409
- context->Message_Block[62] = context->Length_Low >> 8;
410
- context->Message_Block[63] = context->Length_Low;
411
-
412
- SHA1ProcessMessageBlock(context);
8
+
9
+/*
10
+** The SHA1 implementation below is adapted from:
11
+**
12
+** $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $
13
+** $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $
14
+**
15
+** SHA-1 in C
16
+** By Steve Reid <[email protected]>
17
+** 100% Public Domain
18
+*/
19
+typedef struct SHA1Context SHA1Context;
20
+struct SHA1Context {
21
+ unsigned int state[5];
22
+ unsigned int count[2];
23
+ unsigned char buffer[64];
24
+};
25
+
26
+/*
27
+ * blk0() and blk() perform the initial expand.
28
+ * I got the idea of expanding during the round function from SSLeay
29
+ *
30
+ * blk0le() for little-endian and blk0be() for big-endian.
31
+ */
32
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
33
+#define blk0le(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
34
+ |(rol(block->l[i],8)&0x00FF00FF))
35
+#define blk0be(i) block->l[i]
36
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
37
+ ^block->l[(i+2)&15]^block->l[i&15],1))
38
+
39
+/*
40
+ * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
41
+ *
42
+ * Rl0() for little-endian and Rb0() for big-endian. Endianness is
43
+ * determined at run-time.
44
+ */
45
+#define Rl0(v,w,x,y,z,i) \
46
+ z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=rol(w,30);
47
+#define Rb0(v,w,x,y,z,i) \
48
+ z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=rol(w,30);
49
+#define R1(v,w,x,y,z,i) \
50
+ z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
51
+#define R2(v,w,x,y,z,i) \
52
+ z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
53
+#define R3(v,w,x,y,z,i) \
54
+ z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
55
+#define R4(v,w,x,y,z,i) \
56
+ z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
57
+
58
+typedef union {
59
+ unsigned char c[64];
60
+ unsigned int l[16];
61
+} CHAR64LONG16;
62
+
63
+/*
64
+ * Hash a single 512-bit block. This is the core of the algorithm.
65
+ */
66
+void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
67
+{
68
+ unsigned int a, b, c, d, e;
69
+ CHAR64LONG16 *block;
70
+ static int one = 1;
71
+ CHAR64LONG16 workspace;
72
+
73
+ block = &workspace;
74
+ (void)memcpy(block, buffer, 64);
75
+
76
+ /* Copy context->state[] to working vars */
77
+ a = state[0];
78
+ b = state[1];
79
+ c = state[2];
80
+ d = state[3];
81
+ e = state[4];
82
+
83
+ /* 4 rounds of 20 operations each. Loop unrolled. */
84
+ if( 1 == *(unsigned char*)&one ){
85
+ 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);
86
+ 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);
87
+ 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);
88
+ 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);
89
+ }else{
90
+ 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);
91
+ 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);
92
+ 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);
93
+ 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);
94
+ }
95
+ 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);
96
+ 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);
97
+ 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);
98
+ 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);
99
+ 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);
100
+ 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);
101
+ 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);
102
+ 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);
103
+ 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);
104
+ 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);
105
+ 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);
106
+ 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);
107
+ 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);
108
+ 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);
109
+ 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);
110
+ 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);
111
+
112
+ /* Add the working vars back into context.state[] */
113
+ state[0] += a;
114
+ state[1] += b;
115
+ state[2] += c;
116
+ state[3] += d;
117
+ state[4] += e;
118
+
119
+ /* Wipe variables */
120
+ a = b = c = d = e = 0;
121
+}
122
+
123
+
124
+/*
125
+ * SHA1Init - Initialize new context
126
+ */
127
+static void SHA1Init(SHA1Context *context){
128
+ /* SHA1 initialization constants */
129
+ context->state[0] = 0x67452301;
130
+ context->state[1] = 0xEFCDAB89;
131
+ context->state[2] = 0x98BADCFE;
132
+ context->state[3] = 0x10325476;
133
+ context->state[4] = 0xC3D2E1F0;
134
+ context->count[0] = context->count[1] = 0;
135
+}
136
+
137
+
138
+/*
139
+ * Run your data through this.
140
+ */
141
+static void SHA1Update(
142
+ SHA1Context *context,
143
+ const unsigned char *data,
144
+ unsigned int len
145
+){
146
+ unsigned int i, j;
147
+
148
+ j = context->count[0];
149
+ if ((context->count[0] += len << 3) < j)
150
+ context->count[1] += (len>>29)+1;
151
+ j = (j >> 3) & 63;
152
+ if ((j + len) > 63) {
153
+ (void)memcpy(&context->buffer[j], data, (i = 64-j));
154
+ SHA1Transform(context->state, context->buffer);
155
+ for ( ; i + 63 < len; i += 64)
156
+ SHA1Transform(context->state, &data[i]);
157
+ j = 0;
158
+ } else {
159
+ i = 0;
160
+ }
161
+ (void)memcpy(&context->buffer[j], &data[i], len - i);
162
+}
163
+
164
+
165
+/*
166
+ * Add padding and return the message digest.
167
+ */
168
+static void SHA1Final(SHA1Context *context, unsigned char digest[20]){
169
+ unsigned int i;
170
+ unsigned char finalcount[8];
171
+
172
+ for (i = 0; i < 8; i++) {
173
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
174
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
175
+ }
176
+ SHA1Update(context, (const unsigned char *)"\200", 1);
177
+ while ((context->count[0] & 504) != 448)
178
+ SHA1Update(context, (const unsigned char *)"\0", 1);
179
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
180
+
181
+ if (digest) {
182
+ for (i = 0; i < 20; i++)
183
+ digest[i] = (unsigned char)
184
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
185
+ }
413186
}
414187
415188
416189
/*
417190
** Convert a digest into base-16. digest should be declared as
@@ -441,18 +214,18 @@
441214
/*
442215
** Add more text to the incremental SHA1 checksum.
443216
*/
444217
void sha1sum_step_text(const char *zText, int nBytes){
445218
if( !incrInit ){
446
- SHA1Reset(&incrCtx);
219
+ SHA1Init(&incrCtx);
447220
incrInit = 1;
448221
}
449222
if( nBytes<=0 ){
450223
if( nBytes==0 ) return;
451224
nBytes = strlen(zText);
452225
}
453
- SHA1Input(&incrCtx, (unsigned char*)zText, nBytes);
226
+ SHA1Update(&incrCtx, (unsigned char*)zText, nBytes);
454227
}
455228
456229
/*
457230
** Add the content of a blob to the incremental SHA1 checksum.
458231
*/
@@ -470,11 +243,11 @@
470243
*/
471244
char *sha1sum_finish(Blob *pOut){
472245
unsigned char zResult[20];
473246
static char zOut[41];
474247
sha1sum_step_text(0,0);
475
- SHA1Result(&incrCtx, zResult);
248
+ SHA1Final(&incrCtx, zResult);
476249
incrInit = 0;
477250
DigestToBase16(zResult, zOut);
478251
if( pOut ){
479252
blob_zero(pOut);
480253
blob_append(pOut, zOut, 40);
@@ -497,21 +270,21 @@
497270
498271
in = fopen(zFilename,"rb");
499272
if( in==0 ){
500273
return 1;
501274
}
502
- SHA1Reset(&ctx);
275
+ SHA1Init(&ctx);
503276
for(;;){
504277
int n;
505278
n = fread(zBuf, 1, sizeof(zBuf), in);
506279
if( n<=0 ) break;
507
- SHA1Input(&ctx, (unsigned char*)zBuf, (unsigned)n);
280
+ SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
508281
}
509282
fclose(in);
510283
blob_zero(pCksum);
511284
blob_resize(pCksum, 40);
512
- SHA1Result(&ctx, zResult);
285
+ SHA1Final(&ctx, zResult);
513286
DigestToBase16(zResult, blob_buffer(pCksum));
514287
return 0;
515288
}
516289
517290
/*
@@ -523,19 +296,19 @@
523296
*/
524297
int sha1sum_blob(const Blob *pIn, Blob *pCksum){
525298
SHA1Context ctx;
526299
unsigned char zResult[20];
527300
528
- SHA1Reset(&ctx);
529
- SHA1Input(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn));
301
+ SHA1Init(&ctx);
302
+ SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn));
530303
if( pIn==pCksum ){
531304
blob_reset(pCksum);
532305
}else{
533306
blob_zero(pCksum);
534307
}
535308
blob_resize(pCksum, 40);
536
- SHA1Result(&ctx, zResult);
309
+ SHA1Final(&ctx, zResult);
537310
DigestToBase16(zResult, blob_buffer(pCksum));
538311
return 0;
539312
}
540313
541314
/*
@@ -545,13 +318,13 @@
545318
char *sha1sum(const char *zIn){
546319
SHA1Context ctx;
547320
unsigned char zResult[20];
548321
char zDigest[41];
549322
550
- SHA1Reset(&ctx);
551
- SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn));
552
- SHA1Result(&ctx, zResult);
323
+ SHA1Init(&ctx);
324
+ SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
325
+ SHA1Final(&ctx, zResult);
553326
DigestToBase16(zResult, zDigest);
554327
return mprintf("%s", zDigest);
555328
}
556329
557330
/*
@@ -575,11 +348,11 @@
575348
static char *zProjectId = 0;
576349
SHA1Context ctx;
577350
unsigned char zResult[20];
578351
char zDigest[41];
579352
580
- SHA1Reset(&ctx);
353
+ SHA1Init(&ctx);
581354
if( zProjectId==0 ){
582355
zProjectId = db_get("project-code", 0);
583356
584357
/* On the first xfer request of a clone, the project-code is not yet
585358
** known. Use the cleartext password, since that is all we have.
@@ -586,16 +359,16 @@
586359
*/
587360
if( zProjectId==0 ){
588361
return mprintf("%s", zPw);
589362
}
590363
}
591
- SHA1Input(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
592
- SHA1Input(&ctx, (unsigned char*)"/", 1);
593
- SHA1Input(&ctx, (unsigned char*)zLogin, strlen(zLogin));
594
- SHA1Input(&ctx, (unsigned char*)"/", 1);
595
- SHA1Input(&ctx, (unsigned const char*)zPw, strlen(zPw));
596
- SHA1Result(&ctx, zResult);
364
+ SHA1Update(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
365
+ SHA1Update(&ctx, (unsigned char*)"/", 1);
366
+ SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
367
+ SHA1Update(&ctx, (unsigned char*)"/", 1);
368
+ SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
369
+ SHA1Final(&ctx, zResult);
597370
DigestToBase16(zResult, zDigest);
598371
return mprintf("%s", zDigest);
599372
}
600373
601374
/*
602375
--- src/sha1.c
+++ src/sha1.c
@@ -1,417 +1,190 @@
1 /*
2 ** This implementation of SHA1 is adapted from the example implementation
3 ** contained in RFC-3174.
4 */
5 /*
6 * If you do not have the ISO standard stdint.h header file, then you
7 * must typdef the following:
8 * name meaning
9 * */
10 #if defined(__DMC__) || defined(_MSC_VER)
11 typedef unsigned long uint32_t; //unsigned 32 bit integer
12 typedef unsigned char uint8_t; //unsigned 8 bit integer (i.e., unsigned char)
13 #else
14 # include <stdint.h>
15 #endif
16 #include <sys/types.h>
17 #include "config.h"
18 #include "sha1.h"
19
20 #define SHA1HashSize 20
21 #define shaSuccess 0
22 #define shaInputTooLong 1
23 #define shaStateError 2
24
25 /*
26 * This structure will hold context information for the SHA-1
27 * hashing operation
28 */
29 typedef struct SHA1Context SHA1Context;
30 struct SHA1Context {
31 uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
32
33 uint32_t Length_Low; /* Message length in bits */
34 uint32_t Length_High; /* Message length in bits */
35
36 int Message_Block_Index; /* Index into message block array */
37 uint8_t Message_Block[64]; /* 512-bit message blocks */
38
39 int Computed; /* Is the digest computed? */
40 int Corrupted; /* Is the message digest corrupted? */
41 };
42
43 /*
44 * sha1.c
45 *
46 * Description:
47 * This file implements the Secure Hashing Algorithm 1 as
48 * defined in FIPS PUB 180-1 published April 17, 1995.
49 *
50 * The SHA-1, produces a 160-bit message digest for a given
51 * data stream. It should take about 2**n steps to find a
52 * message with the same digest as a given message and
53 * 2**(n/2) to find any two messages with the same digest,
54 * when n is the digest size in bits. Therefore, this
55 * algorithm can serve as a means of providing a
56 * "fingerprint" for a message.
57 *
58 * Portability Issues:
59 * SHA-1 is defined in terms of 32-bit "words". This code
60 * uses <stdint.h> (included via "sha1.h" to define 32 and 8
61 * bit unsigned integer types. If your C compiler does not
62 * support 32 bit unsigned integers, this code is not
63 * appropriate.
64 *
65 * Caveats:
66 * SHA-1 is designed to work with messages less than 2^64 bits
67 * long. Although SHA-1 allows a message digest to be generated
68 * for messages of any number of bits less than 2^64, this
69 * implementation only works with messages with a length that is
70 * a multiple of the size of an 8-bit character.
71 *
72 */
73
74 /*
75 * Define the SHA1 circular left shift macro
76 */
77 #define SHA1CircularShift(bits,word) \
78 (((word) << (bits)) | ((word) >> (32-(bits))))
79
80 /* Local Function Prototyptes */
81 static void SHA1PadMessage(SHA1Context *);
82 static void SHA1ProcessMessageBlock(SHA1Context *);
83
84 /*
85 * SHA1Reset
86 *
87 * Description:
88 * This function will initialize the SHA1Context in preparation
89 * for computing a new SHA1 message digest.
90 *
91 * Parameters:
92 * context: [in/out]
93 * The context to reset.
94 *
95 * Returns:
96 * sha Error Code.
97 *
98 */
99 static int SHA1Reset(SHA1Context *context)
100 {
101 context->Length_Low = 0;
102 context->Length_High = 0;
103 context->Message_Block_Index = 0;
104
105 context->Intermediate_Hash[0] = 0x67452301;
106 context->Intermediate_Hash[1] = 0xEFCDAB89;
107 context->Intermediate_Hash[2] = 0x98BADCFE;
108 context->Intermediate_Hash[3] = 0x10325476;
109 context->Intermediate_Hash[4] = 0xC3D2E1F0;
110
111 context->Computed = 0;
112 context->Corrupted = 0;
113
114 return shaSuccess;
115 }
116
117 /*
118 * SHA1Result
119 *
120 * Description:
121 * This function will return the 160-bit message digest into the
122 * Message_Digest array provided by the caller.
123 * NOTE: The first octet of hash is stored in the 0th element,
124 * the last octet of hash in the 19th element.
125 *
126 * Parameters:
127 * context: [in/out]
128 * The context to use to calculate the SHA-1 hash.
129 * Message_Digest: [out]
130 * Where the digest is returned.
131 *
132 * Returns:
133 * sha Error Code.
134 *
135 */
136 static int SHA1Result( SHA1Context *context,
137 uint8_t Message_Digest[SHA1HashSize])
138 {
139 int i;
140
141 if (context->Corrupted)
142 {
143 return context->Corrupted;
144 }
145
146 if (!context->Computed)
147 {
148 SHA1PadMessage(context);
149 for(i=0; i<64; ++i)
150 {
151 /* message may be sensitive, clear it out */
152 context->Message_Block[i] = 0;
153 }
154 context->Length_Low = 0; /* and clear length */
155 context->Length_High = 0;
156 context->Computed = 1;
157
158 }
159
160 for(i = 0; i < SHA1HashSize; ++i)
161 {
162 Message_Digest[i] = context->Intermediate_Hash[i>>2]
163 >> 8 * ( 3 - ( i & 0x03 ) );
164 }
165
166 return shaSuccess;
167 }
168
169 /*
170 * SHA1Input
171 *
172 * Description:
173 * This function accepts an array of octets as the next portion
174 * of the message.
175 *
176 * Parameters:
177 * context: [in/out]
178 * The SHA context to update
179 * message_array: [in]
180 * An array of characters representing the next portion of
181 * the message.
182 * length: [in]
183 * The length of the message in message_array
184 *
185 * Returns:
186 * sha Error Code.
187 *
188 */
189 static
190 int SHA1Input( SHA1Context *context,
191 const uint8_t *message_array,
192 unsigned length)
193 {
194 if (!length)
195 {
196 return shaSuccess;
197 }
198
199 if (context->Computed)
200 {
201 context->Corrupted = shaStateError;
202
203 return shaStateError;
204 }
205
206 if (context->Corrupted)
207 {
208 return context->Corrupted;
209 }
210 while(length-- && !context->Corrupted)
211 {
212 context->Message_Block[context->Message_Block_Index++] =
213 (*message_array & 0xFF);
214
215 context->Length_Low += 8;
216 if (context->Length_Low == 0)
217 {
218 context->Length_High++;
219 if (context->Length_High == 0)
220 {
221 /* Message is too long */
222 context->Corrupted = 1;
223 }
224 }
225
226 if (context->Message_Block_Index == 64)
227 {
228 SHA1ProcessMessageBlock(context);
229 }
230
231 message_array++;
232 }
233
234 return shaSuccess;
235 }
236
237 /*
238 * SHA1ProcessMessageBlock
239 *
240 * Description:
241 * This function will process the next 512 bits of the message
242 * stored in the Message_Block array.
243 *
244 * Parameters:
245 * None.
246 *
247 * Returns:
248 * Nothing.
249 *
250 * Comments:
251 * Many of the variable names in this code, especially the
252 * single character names, were used because those were the
253 * names used in the publication.
254 *
255 *
256 */
257 static void SHA1ProcessMessageBlock(SHA1Context *context)
258 {
259 const uint32_t K[] = { /* Constants defined in SHA-1 */
260 0x5A827999,
261 0x6ED9EBA1,
262 0x8F1BBCDC,
263 0xCA62C1D6
264 };
265 int t; /* Loop counter */
266 uint32_t temp; /* Temporary word value */
267 uint32_t W[80]; /* Word sequence */
268 uint32_t A, B, C, D, E; /* Word buffers */
269
270 /*
271 * Initialize the first 16 words in the array W
272 */
273 for(t = 0; t < 16; t++)
274 {
275 W[t] = context->Message_Block[t * 4] << 24;
276 W[t] |= context->Message_Block[t * 4 + 1] << 16;
277 W[t] |= context->Message_Block[t * 4 + 2] << 8;
278 W[t] |= context->Message_Block[t * 4 + 3];
279 }
280
281 for(t = 16; t < 80; t++)
282 {
283 W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
284 }
285
286 A = context->Intermediate_Hash[0];
287 B = context->Intermediate_Hash[1];
288 C = context->Intermediate_Hash[2];
289 D = context->Intermediate_Hash[3];
290 E = context->Intermediate_Hash[4];
291
292 for(t = 0; t < 20; t++)
293 {
294 temp = SHA1CircularShift(5,A) +
295 ((B & C) | ((~B) & D)) + E + W[t] + K[0];
296 E = D;
297 D = C;
298 C = SHA1CircularShift(30,B);
299
300 B = A;
301 A = temp;
302 }
303
304 for(t = 20; t < 40; t++)
305 {
306 temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
307 E = D;
308 D = C;
309 C = SHA1CircularShift(30,B);
310 B = A;
311 A = temp;
312 }
313
314 for(t = 40; t < 60; t++)
315 {
316 temp = SHA1CircularShift(5,A) +
317 ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
318 E = D;
319 D = C;
320 C = SHA1CircularShift(30,B);
321 B = A;
322 A = temp;
323 }
324
325 for(t = 60; t < 80; t++)
326 {
327 temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
328 E = D;
329 D = C;
330 C = SHA1CircularShift(30,B);
331 B = A;
332 A = temp;
333 }
334
335 context->Intermediate_Hash[0] += A;
336 context->Intermediate_Hash[1] += B;
337 context->Intermediate_Hash[2] += C;
338 context->Intermediate_Hash[3] += D;
339 context->Intermediate_Hash[4] += E;
340
341 context->Message_Block_Index = 0;
342 }
343
344 /*
345 * SHA1PadMessage
346 *
347
348 * Description:
349 * According to the standard, the message must be padded to an even
350 * 512 bits. The first padding bit must be a '1'. The last 64
351 * bits represent the length of the original message. All bits in
352 * between should be 0. This function will pad the message
353 * according to those rules by filling the Message_Block array
354 * accordingly. It will also call the ProcessMessageBlock function
355 * provided appropriately. When it returns, it can be assumed that
356 * the message digest has been computed.
357 *
358 * Parameters:
359 * context: [in/out]
360 * The context to pad
361 * ProcessMessageBlock: [in]
362 * The appropriate SHA*ProcessMessageBlock function
363 * Returns:
364 * Nothing.
365 *
366 */
367 static void SHA1PadMessage(SHA1Context *context)
368 {
369 /*
370 * Check to see if the current message block is too small to hold
371 * the initial padding bits and length. If so, we will pad the
372 * block, process it, and then continue padding into a second
373 * block.
374 */
375 if (context->Message_Block_Index > 55)
376 {
377 context->Message_Block[context->Message_Block_Index++] = 0x80;
378 while(context->Message_Block_Index < 64)
379 {
380 context->Message_Block[context->Message_Block_Index++] = 0;
381 }
382
383 SHA1ProcessMessageBlock(context);
384
385 while(context->Message_Block_Index < 56)
386 {
387 context->Message_Block[context->Message_Block_Index++] = 0;
388 }
389 }
390 else
391 {
392 context->Message_Block[context->Message_Block_Index++] = 0x80;
393 while(context->Message_Block_Index < 56)
394 {
395
396 context->Message_Block[context->Message_Block_Index++] = 0;
397 }
398 }
399
400 /*
401 * Store the message length as the last 8 octets
402 */
403 context->Message_Block[56] = context->Length_High >> 24;
404 context->Message_Block[57] = context->Length_High >> 16;
405 context->Message_Block[58] = context->Length_High >> 8;
406 context->Message_Block[59] = context->Length_High;
407 context->Message_Block[60] = context->Length_Low >> 24;
408 context->Message_Block[61] = context->Length_Low >> 16;
409 context->Message_Block[62] = context->Length_Low >> 8;
410 context->Message_Block[63] = context->Length_Low;
411
412 SHA1ProcessMessageBlock(context);
413 }
414
415
416 /*
417 ** Convert a digest into base-16. digest should be declared as
@@ -441,18 +214,18 @@
441 /*
442 ** Add more text to the incremental SHA1 checksum.
443 */
444 void sha1sum_step_text(const char *zText, int nBytes){
445 if( !incrInit ){
446 SHA1Reset(&incrCtx);
447 incrInit = 1;
448 }
449 if( nBytes<=0 ){
450 if( nBytes==0 ) return;
451 nBytes = strlen(zText);
452 }
453 SHA1Input(&incrCtx, (unsigned char*)zText, nBytes);
454 }
455
456 /*
457 ** Add the content of a blob to the incremental SHA1 checksum.
458 */
@@ -470,11 +243,11 @@
470 */
471 char *sha1sum_finish(Blob *pOut){
472 unsigned char zResult[20];
473 static char zOut[41];
474 sha1sum_step_text(0,0);
475 SHA1Result(&incrCtx, zResult);
476 incrInit = 0;
477 DigestToBase16(zResult, zOut);
478 if( pOut ){
479 blob_zero(pOut);
480 blob_append(pOut, zOut, 40);
@@ -497,21 +270,21 @@
497
498 in = fopen(zFilename,"rb");
499 if( in==0 ){
500 return 1;
501 }
502 SHA1Reset(&ctx);
503 for(;;){
504 int n;
505 n = fread(zBuf, 1, sizeof(zBuf), in);
506 if( n<=0 ) break;
507 SHA1Input(&ctx, (unsigned char*)zBuf, (unsigned)n);
508 }
509 fclose(in);
510 blob_zero(pCksum);
511 blob_resize(pCksum, 40);
512 SHA1Result(&ctx, zResult);
513 DigestToBase16(zResult, blob_buffer(pCksum));
514 return 0;
515 }
516
517 /*
@@ -523,19 +296,19 @@
523 */
524 int sha1sum_blob(const Blob *pIn, Blob *pCksum){
525 SHA1Context ctx;
526 unsigned char zResult[20];
527
528 SHA1Reset(&ctx);
529 SHA1Input(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn));
530 if( pIn==pCksum ){
531 blob_reset(pCksum);
532 }else{
533 blob_zero(pCksum);
534 }
535 blob_resize(pCksum, 40);
536 SHA1Result(&ctx, zResult);
537 DigestToBase16(zResult, blob_buffer(pCksum));
538 return 0;
539 }
540
541 /*
@@ -545,13 +318,13 @@
545 char *sha1sum(const char *zIn){
546 SHA1Context ctx;
547 unsigned char zResult[20];
548 char zDigest[41];
549
550 SHA1Reset(&ctx);
551 SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn));
552 SHA1Result(&ctx, zResult);
553 DigestToBase16(zResult, zDigest);
554 return mprintf("%s", zDigest);
555 }
556
557 /*
@@ -575,11 +348,11 @@
575 static char *zProjectId = 0;
576 SHA1Context ctx;
577 unsigned char zResult[20];
578 char zDigest[41];
579
580 SHA1Reset(&ctx);
581 if( zProjectId==0 ){
582 zProjectId = db_get("project-code", 0);
583
584 /* On the first xfer request of a clone, the project-code is not yet
585 ** known. Use the cleartext password, since that is all we have.
@@ -586,16 +359,16 @@
586 */
587 if( zProjectId==0 ){
588 return mprintf("%s", zPw);
589 }
590 }
591 SHA1Input(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
592 SHA1Input(&ctx, (unsigned char*)"/", 1);
593 SHA1Input(&ctx, (unsigned char*)zLogin, strlen(zLogin));
594 SHA1Input(&ctx, (unsigned char*)"/", 1);
595 SHA1Input(&ctx, (unsigned const char*)zPw, strlen(zPw));
596 SHA1Result(&ctx, zResult);
597 DigestToBase16(zResult, zDigest);
598 return mprintf("%s", zDigest);
599 }
600
601 /*
602
--- src/sha1.c
+++ src/sha1.c
@@ -1,417 +1,190 @@
1 /*
2 ** This implementation of SHA1.
 
3 */
 
 
 
 
 
 
 
 
 
 
 
4 #include <sys/types.h>
5 #include "config.h"
6 #include "sha1.h"
7
8
9 /*
10 ** The SHA1 implementation below is adapted from:
11 **
12 ** $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $
13 ** $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $
14 **
15 ** SHA-1 in C
16 ** By Steve Reid <[email protected]>
17 ** 100% Public Domain
18 */
19 typedef struct SHA1Context SHA1Context;
20 struct SHA1Context {
21 unsigned int state[5];
22 unsigned int count[2];
23 unsigned char buffer[64];
24 };
25
26 /*
27 * blk0() and blk() perform the initial expand.
28 * I got the idea of expanding during the round function from SSLeay
29 *
30 * blk0le() for little-endian and blk0be() for big-endian.
31 */
32 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
33 #define blk0le(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
34 |(rol(block->l[i],8)&0x00FF00FF))
35 #define blk0be(i) block->l[i]
36 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
37 ^block->l[(i+2)&15]^block->l[i&15],1))
38
39 /*
40 * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
41 *
42 * Rl0() for little-endian and Rb0() for big-endian. Endianness is
43 * determined at run-time.
44 */
45 #define Rl0(v,w,x,y,z,i) \
46 z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=rol(w,30);
47 #define Rb0(v,w,x,y,z,i) \
48 z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=rol(w,30);
49 #define R1(v,w,x,y,z,i) \
50 z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
51 #define R2(v,w,x,y,z,i) \
52 z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
53 #define R3(v,w,x,y,z,i) \
54 z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
55 #define R4(v,w,x,y,z,i) \
56 z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
57
58 typedef union {
59 unsigned char c[64];
60 unsigned int l[16];
61 } CHAR64LONG16;
62
63 /*
64 * Hash a single 512-bit block. This is the core of the algorithm.
65 */
66 void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
67 {
68 unsigned int a, b, c, d, e;
69 CHAR64LONG16 *block;
70 static int one = 1;
71 CHAR64LONG16 workspace;
72
73 block = &workspace;
74 (void)memcpy(block, buffer, 64);
75
76 /* Copy context->state[] to working vars */
77 a = state[0];
78 b = state[1];
79 c = state[2];
80 d = state[3];
81 e = state[4];
82
83 /* 4 rounds of 20 operations each. Loop unrolled. */
84 if( 1 == *(unsigned char*)&one ){
85 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);
86 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);
87 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);
88 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);
89 }else{
90 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);
91 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);
92 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);
93 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);
94 }
95 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);
96 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);
97 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);
98 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);
99 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);
100 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);
101 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);
102 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);
103 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);
104 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);
105 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);
106 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);
107 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);
108 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);
109 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);
110 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);
111
112 /* Add the working vars back into context.state[] */
113 state[0] += a;
114 state[1] += b;
115 state[2] += c;
116 state[3] += d;
117 state[4] += e;
118
119 /* Wipe variables */
120 a = b = c = d = e = 0;
121 }
122
123
124 /*
125 * SHA1Init - Initialize new context
126 */
127 static void SHA1Init(SHA1Context *context){
128 /* SHA1 initialization constants */
129 context->state[0] = 0x67452301;
130 context->state[1] = 0xEFCDAB89;
131 context->state[2] = 0x98BADCFE;
132 context->state[3] = 0x10325476;
133 context->state[4] = 0xC3D2E1F0;
134 context->count[0] = context->count[1] = 0;
135 }
136
137
138 /*
139 * Run your data through this.
140 */
141 static void SHA1Update(
142 SHA1Context *context,
143 const unsigned char *data,
144 unsigned int len
145 ){
146 unsigned int i, j;
147
148 j = context->count[0];
149 if ((context->count[0] += len << 3) < j)
150 context->count[1] += (len>>29)+1;
151 j = (j >> 3) & 63;
152 if ((j + len) > 63) {
153 (void)memcpy(&context->buffer[j], data, (i = 64-j));
154 SHA1Transform(context->state, context->buffer);
155 for ( ; i + 63 < len; i += 64)
156 SHA1Transform(context->state, &data[i]);
157 j = 0;
158 } else {
159 i = 0;
160 }
161 (void)memcpy(&context->buffer[j], &data[i], len - i);
162 }
163
164
165 /*
166 * Add padding and return the message digest.
167 */
168 static void SHA1Final(SHA1Context *context, unsigned char digest[20]){
169 unsigned int i;
170 unsigned char finalcount[8];
171
172 for (i = 0; i < 8; i++) {
173 finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
174 >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
175 }
176 SHA1Update(context, (const unsigned char *)"\200", 1);
177 while ((context->count[0] & 504) != 448)
178 SHA1Update(context, (const unsigned char *)"\0", 1);
179 SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
180
181 if (digest) {
182 for (i = 0; i < 20; i++)
183 digest[i] = (unsigned char)
184 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
185 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186 }
187
188
189 /*
190 ** Convert a digest into base-16. digest should be declared as
@@ -441,18 +214,18 @@
214 /*
215 ** Add more text to the incremental SHA1 checksum.
216 */
217 void sha1sum_step_text(const char *zText, int nBytes){
218 if( !incrInit ){
219 SHA1Init(&incrCtx);
220 incrInit = 1;
221 }
222 if( nBytes<=0 ){
223 if( nBytes==0 ) return;
224 nBytes = strlen(zText);
225 }
226 SHA1Update(&incrCtx, (unsigned char*)zText, nBytes);
227 }
228
229 /*
230 ** Add the content of a blob to the incremental SHA1 checksum.
231 */
@@ -470,11 +243,11 @@
243 */
244 char *sha1sum_finish(Blob *pOut){
245 unsigned char zResult[20];
246 static char zOut[41];
247 sha1sum_step_text(0,0);
248 SHA1Final(&incrCtx, zResult);
249 incrInit = 0;
250 DigestToBase16(zResult, zOut);
251 if( pOut ){
252 blob_zero(pOut);
253 blob_append(pOut, zOut, 40);
@@ -497,21 +270,21 @@
270
271 in = fopen(zFilename,"rb");
272 if( in==0 ){
273 return 1;
274 }
275 SHA1Init(&ctx);
276 for(;;){
277 int n;
278 n = fread(zBuf, 1, sizeof(zBuf), in);
279 if( n<=0 ) break;
280 SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
281 }
282 fclose(in);
283 blob_zero(pCksum);
284 blob_resize(pCksum, 40);
285 SHA1Final(&ctx, zResult);
286 DigestToBase16(zResult, blob_buffer(pCksum));
287 return 0;
288 }
289
290 /*
@@ -523,19 +296,19 @@
296 */
297 int sha1sum_blob(const Blob *pIn, Blob *pCksum){
298 SHA1Context ctx;
299 unsigned char zResult[20];
300
301 SHA1Init(&ctx);
302 SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn));
303 if( pIn==pCksum ){
304 blob_reset(pCksum);
305 }else{
306 blob_zero(pCksum);
307 }
308 blob_resize(pCksum, 40);
309 SHA1Final(&ctx, zResult);
310 DigestToBase16(zResult, blob_buffer(pCksum));
311 return 0;
312 }
313
314 /*
@@ -545,13 +318,13 @@
318 char *sha1sum(const char *zIn){
319 SHA1Context ctx;
320 unsigned char zResult[20];
321 char zDigest[41];
322
323 SHA1Init(&ctx);
324 SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
325 SHA1Final(&ctx, zResult);
326 DigestToBase16(zResult, zDigest);
327 return mprintf("%s", zDigest);
328 }
329
330 /*
@@ -575,11 +348,11 @@
348 static char *zProjectId = 0;
349 SHA1Context ctx;
350 unsigned char zResult[20];
351 char zDigest[41];
352
353 SHA1Init(&ctx);
354 if( zProjectId==0 ){
355 zProjectId = db_get("project-code", 0);
356
357 /* On the first xfer request of a clone, the project-code is not yet
358 ** known. Use the cleartext password, since that is all we have.
@@ -586,16 +359,16 @@
359 */
360 if( zProjectId==0 ){
361 return mprintf("%s", zPw);
362 }
363 }
364 SHA1Update(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
365 SHA1Update(&ctx, (unsigned char*)"/", 1);
366 SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
367 SHA1Update(&ctx, (unsigned char*)"/", 1);
368 SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
369 SHA1Final(&ctx, zResult);
370 DigestToBase16(zResult, zDigest);
371 return mprintf("%s", zDigest);
372 }
373
374 /*
375
+13 -14
--- src/tkt.c
+++ src/tkt.c
@@ -214,25 +214,25 @@
214214
*/
215215
void ticket_rebuild_entry(const char *zTktUuid){
216216
char *zTag = mprintf("tkt-%s", zTktUuid);
217217
int tagid = tag_findid(zTag, 1);
218218
Stmt q;
219
- Manifest manifest;
220
- Blob content;
219
+ Manifest *pTicket;
221220
int createFlag = 1;
222221
223222
db_multi_exec(
224223
"DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
225224
);
226225
db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
227226
while( db_step(&q)==SQLITE_ROW ){
228227
int rid = db_column_int(&q, 0);
229
- content_get(rid, &content);
230
- manifest_parse(&manifest, &content);
231
- ticket_insert(&manifest, createFlag, rid);
232
- manifest_ticket_event(rid, &manifest, createFlag, tagid);
233
- manifest_clear(&manifest);
228
+ pTicket = manifest_get(rid, CFTYPE_TICKET);
229
+ if( pTicket ){
230
+ ticket_insert(pTicket, createFlag, rid);
231
+ manifest_ticket_event(rid, pTicket, createFlag, tagid);
232
+ manifest_destroy(pTicket);
233
+ }
234234
createFlag = 0;
235235
}
236236
db_finalize(&q);
237237
}
238238
@@ -753,12 +753,11 @@
753753
" AND blob.rid=attachid"
754754
" ORDER BY 1 DESC",
755755
tagid, tagid
756756
);
757757
while( db_step(&q)==SQLITE_ROW ){
758
- Blob content;
759
- Manifest m;
758
+ Manifest *pTicket;
760759
char zShort[12];
761760
const char *zDate = db_column_text(&q, 0);
762761
int rid = db_column_int(&q, 1);
763762
const char *zChngUuid = db_column_text(&q, 2);
764763
const char *zFile = db_column_text(&q, 4);
@@ -777,22 +776,22 @@
777776
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
778777
@ (rid %d(rid)) by
779778
hyperlink_to_user(zUser,zDate," on");
780779
hyperlink_to_date(zDate, ".</p>");
781780
}else{
782
- content_get(rid, &content);
783
- if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
781
+ pTicket = manifest_get(rid, CFTYPE_TICKET);
782
+ if( pTicket ){
784783
@
785784
@ <p>Ticket change
786785
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
787786
@ (rid %d(rid)) by
788
- hyperlink_to_user(m.zUser,zDate," on");
787
+ hyperlink_to_user(pTicket->zUser,zDate," on");
789788
hyperlink_to_date(zDate, ":");
790789
@ </p>
791
- ticket_output_change_artifact(&m);
790
+ ticket_output_change_artifact(pTicket);
792791
}
793
- manifest_clear(&m);
792
+ manifest_destroy(pTicket);
794793
}
795794
}
796795
db_finalize(&q);
797796
style_footer_cmdref("timeline",0);
798797
}
799798
--- src/tkt.c
+++ src/tkt.c
@@ -214,25 +214,25 @@
214 */
215 void ticket_rebuild_entry(const char *zTktUuid){
216 char *zTag = mprintf("tkt-%s", zTktUuid);
217 int tagid = tag_findid(zTag, 1);
218 Stmt q;
219 Manifest manifest;
220 Blob content;
221 int createFlag = 1;
222
223 db_multi_exec(
224 "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
225 );
226 db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
227 while( db_step(&q)==SQLITE_ROW ){
228 int rid = db_column_int(&q, 0);
229 content_get(rid, &content);
230 manifest_parse(&manifest, &content);
231 ticket_insert(&manifest, createFlag, rid);
232 manifest_ticket_event(rid, &manifest, createFlag, tagid);
233 manifest_clear(&manifest);
 
234 createFlag = 0;
235 }
236 db_finalize(&q);
237 }
238
@@ -753,12 +753,11 @@
753 " AND blob.rid=attachid"
754 " ORDER BY 1 DESC",
755 tagid, tagid
756 );
757 while( db_step(&q)==SQLITE_ROW ){
758 Blob content;
759 Manifest m;
760 char zShort[12];
761 const char *zDate = db_column_text(&q, 0);
762 int rid = db_column_int(&q, 1);
763 const char *zChngUuid = db_column_text(&q, 2);
764 const char *zFile = db_column_text(&q, 4);
@@ -777,22 +776,22 @@
777 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
778 @ (rid %d(rid)) by
779 hyperlink_to_user(zUser,zDate," on");
780 hyperlink_to_date(zDate, ".</p>");
781 }else{
782 content_get(rid, &content);
783 if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
784 @
785 @ <p>Ticket change
786 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
787 @ (rid %d(rid)) by
788 hyperlink_to_user(m.zUser,zDate," on");
789 hyperlink_to_date(zDate, ":");
790 @ </p>
791 ticket_output_change_artifact(&m);
792 }
793 manifest_clear(&m);
794 }
795 }
796 db_finalize(&q);
797 style_footer_cmdref("timeline",0);
798 }
799
--- src/tkt.c
+++ src/tkt.c
@@ -214,25 +214,25 @@
214 */
215 void ticket_rebuild_entry(const char *zTktUuid){
216 char *zTag = mprintf("tkt-%s", zTktUuid);
217 int tagid = tag_findid(zTag, 1);
218 Stmt q;
219 Manifest *pTicket;
 
220 int createFlag = 1;
221
222 db_multi_exec(
223 "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
224 );
225 db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
226 while( db_step(&q)==SQLITE_ROW ){
227 int rid = db_column_int(&q, 0);
228 pTicket = manifest_get(rid, CFTYPE_TICKET);
229 if( pTicket ){
230 ticket_insert(pTicket, createFlag, rid);
231 manifest_ticket_event(rid, pTicket, createFlag, tagid);
232 manifest_destroy(pTicket);
233 }
234 createFlag = 0;
235 }
236 db_finalize(&q);
237 }
238
@@ -753,12 +753,11 @@
753 " AND blob.rid=attachid"
754 " ORDER BY 1 DESC",
755 tagid, tagid
756 );
757 while( db_step(&q)==SQLITE_ROW ){
758 Manifest *pTicket;
 
759 char zShort[12];
760 const char *zDate = db_column_text(&q, 0);
761 int rid = db_column_int(&q, 1);
762 const char *zChngUuid = db_column_text(&q, 2);
763 const char *zFile = db_column_text(&q, 4);
@@ -777,22 +776,22 @@
776 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
777 @ (rid %d(rid)) by
778 hyperlink_to_user(zUser,zDate," on");
779 hyperlink_to_date(zDate, ".</p>");
780 }else{
781 pTicket = manifest_get(rid, CFTYPE_TICKET);
782 if( pTicket ){
783 @
784 @ <p>Ticket change
785 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
786 @ (rid %d(rid)) by
787 hyperlink_to_user(pTicket->zUser,zDate," on");
788 hyperlink_to_date(zDate, ":");
789 @ </p>
790 ticket_output_change_artifact(pTicket);
791 }
792 manifest_destroy(pTicket);
793 }
794 }
795 db_finalize(&q);
796 style_footer_cmdref("timeline",0);
797 }
798
+13 -14
--- src/tkt.c
+++ src/tkt.c
@@ -214,25 +214,25 @@
214214
*/
215215
void ticket_rebuild_entry(const char *zTktUuid){
216216
char *zTag = mprintf("tkt-%s", zTktUuid);
217217
int tagid = tag_findid(zTag, 1);
218218
Stmt q;
219
- Manifest manifest;
220
- Blob content;
219
+ Manifest *pTicket;
221220
int createFlag = 1;
222221
223222
db_multi_exec(
224223
"DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
225224
);
226225
db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
227226
while( db_step(&q)==SQLITE_ROW ){
228227
int rid = db_column_int(&q, 0);
229
- content_get(rid, &content);
230
- manifest_parse(&manifest, &content);
231
- ticket_insert(&manifest, createFlag, rid);
232
- manifest_ticket_event(rid, &manifest, createFlag, tagid);
233
- manifest_clear(&manifest);
228
+ pTicket = manifest_get(rid, CFTYPE_TICKET);
229
+ if( pTicket ){
230
+ ticket_insert(pTicket, createFlag, rid);
231
+ manifest_ticket_event(rid, pTicket, createFlag, tagid);
232
+ manifest_destroy(pTicket);
233
+ }
234234
createFlag = 0;
235235
}
236236
db_finalize(&q);
237237
}
238238
@@ -753,12 +753,11 @@
753753
" AND blob.rid=attachid"
754754
" ORDER BY 1 DESC",
755755
tagid, tagid
756756
);
757757
while( db_step(&q)==SQLITE_ROW ){
758
- Blob content;
759
- Manifest m;
758
+ Manifest *pTicket;
760759
char zShort[12];
761760
const char *zDate = db_column_text(&q, 0);
762761
int rid = db_column_int(&q, 1);
763762
const char *zChngUuid = db_column_text(&q, 2);
764763
const char *zFile = db_column_text(&q, 4);
@@ -777,22 +776,22 @@
777776
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
778777
@ (rid %d(rid)) by
779778
hyperlink_to_user(zUser,zDate," on");
780779
hyperlink_to_date(zDate, ".</p>");
781780
}else{
782
- content_get(rid, &content);
783
- if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
781
+ pTicket = manifest_get(rid, CFTYPE_TICKET);
782
+ if( pTicket ){
784783
@
785784
@ <p>Ticket change
786785
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
787786
@ (rid %d(rid)) by
788
- hyperlink_to_user(m.zUser,zDate," on");
787
+ hyperlink_to_user(pTicket->zUser,zDate," on");
789788
hyperlink_to_date(zDate, ":");
790789
@ </p>
791
- ticket_output_change_artifact(&m);
790
+ ticket_output_change_artifact(pTicket);
792791
}
793
- manifest_clear(&m);
792
+ manifest_destroy(pTicket);
794793
}
795794
}
796795
db_finalize(&q);
797796
style_footer_cmdref("timeline",0);
798797
}
799798
--- src/tkt.c
+++ src/tkt.c
@@ -214,25 +214,25 @@
214 */
215 void ticket_rebuild_entry(const char *zTktUuid){
216 char *zTag = mprintf("tkt-%s", zTktUuid);
217 int tagid = tag_findid(zTag, 1);
218 Stmt q;
219 Manifest manifest;
220 Blob content;
221 int createFlag = 1;
222
223 db_multi_exec(
224 "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
225 );
226 db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
227 while( db_step(&q)==SQLITE_ROW ){
228 int rid = db_column_int(&q, 0);
229 content_get(rid, &content);
230 manifest_parse(&manifest, &content);
231 ticket_insert(&manifest, createFlag, rid);
232 manifest_ticket_event(rid, &manifest, createFlag, tagid);
233 manifest_clear(&manifest);
 
234 createFlag = 0;
235 }
236 db_finalize(&q);
237 }
238
@@ -753,12 +753,11 @@
753 " AND blob.rid=attachid"
754 " ORDER BY 1 DESC",
755 tagid, tagid
756 );
757 while( db_step(&q)==SQLITE_ROW ){
758 Blob content;
759 Manifest m;
760 char zShort[12];
761 const char *zDate = db_column_text(&q, 0);
762 int rid = db_column_int(&q, 1);
763 const char *zChngUuid = db_column_text(&q, 2);
764 const char *zFile = db_column_text(&q, 4);
@@ -777,22 +776,22 @@
777 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
778 @ (rid %d(rid)) by
779 hyperlink_to_user(zUser,zDate," on");
780 hyperlink_to_date(zDate, ".</p>");
781 }else{
782 content_get(rid, &content);
783 if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
784 @
785 @ <p>Ticket change
786 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
787 @ (rid %d(rid)) by
788 hyperlink_to_user(m.zUser,zDate," on");
789 hyperlink_to_date(zDate, ":");
790 @ </p>
791 ticket_output_change_artifact(&m);
792 }
793 manifest_clear(&m);
794 }
795 }
796 db_finalize(&q);
797 style_footer_cmdref("timeline",0);
798 }
799
--- src/tkt.c
+++ src/tkt.c
@@ -214,25 +214,25 @@
214 */
215 void ticket_rebuild_entry(const char *zTktUuid){
216 char *zTag = mprintf("tkt-%s", zTktUuid);
217 int tagid = tag_findid(zTag, 1);
218 Stmt q;
219 Manifest *pTicket;
 
220 int createFlag = 1;
221
222 db_multi_exec(
223 "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
224 );
225 db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
226 while( db_step(&q)==SQLITE_ROW ){
227 int rid = db_column_int(&q, 0);
228 pTicket = manifest_get(rid, CFTYPE_TICKET);
229 if( pTicket ){
230 ticket_insert(pTicket, createFlag, rid);
231 manifest_ticket_event(rid, pTicket, createFlag, tagid);
232 manifest_destroy(pTicket);
233 }
234 createFlag = 0;
235 }
236 db_finalize(&q);
237 }
238
@@ -753,12 +753,11 @@
753 " AND blob.rid=attachid"
754 " ORDER BY 1 DESC",
755 tagid, tagid
756 );
757 while( db_step(&q)==SQLITE_ROW ){
758 Manifest *pTicket;
 
759 char zShort[12];
760 const char *zDate = db_column_text(&q, 0);
761 int rid = db_column_int(&q, 1);
762 const char *zChngUuid = db_column_text(&q, 2);
763 const char *zFile = db_column_text(&q, 4);
@@ -777,22 +776,22 @@
776 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
777 @ (rid %d(rid)) by
778 hyperlink_to_user(zUser,zDate," on");
779 hyperlink_to_date(zDate, ".</p>");
780 }else{
781 pTicket = manifest_get(rid, CFTYPE_TICKET);
782 if( pTicket ){
783 @
784 @ <p>Ticket change
785 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
786 @ (rid %d(rid)) by
787 hyperlink_to_user(pTicket->zUser,zDate," on");
788 hyperlink_to_date(zDate, ":");
789 @ </p>
790 ticket_output_change_artifact(pTicket);
791 }
792 manifest_destroy(pTicket);
793 }
794 }
795 db_finalize(&q);
796 style_footer_cmdref("timeline",0);
797 }
798
+14 -11
--- src/update.c
+++ src/update.c
@@ -279,10 +279,11 @@
279279
db_end_transaction(1); /* With --nochange, rollback changes */
280280
}else{
281281
if( g.argc<=3 ){
282282
/* All files updated. Shift the current checkout to the target. */
283283
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
284
+ checkout_set_all_exe(vid);
284285
manifest_to_disk(tid);
285286
db_lset_int("checkout", tid);
286287
}else{
287288
/* A subset of files have been checked out. Keep the current
288289
** checkout unchanged. */
@@ -302,13 +303,13 @@
302303
const char *revision, /* The checkin containing the file */
303304
const char *file, /* Full treename of the file */
304305
Blob *content, /* Put the content here */
305306
int errCode /* Error code if file not found. Panic if 0. */
306307
){
307
- Blob mfile;
308
- Manifest m;
309
- int i, rid=0;
308
+ Manifest *pManifest;
309
+ ManifestFile *pFile;
310
+ int rid=0;
310311
311312
if( revision ){
312313
rid = name_to_rid(revision);
313314
}else{
314315
rid = db_lget_int("checkout", 0);
@@ -315,21 +316,22 @@
315316
}
316317
if( !is_a_version(rid) ){
317318
if( errCode>0 ) return errCode;
318319
fossil_fatal("no such checkin: %s", revision);
319320
}
320
- content_get(rid, &mfile);
321
+ pManifest = manifest_get(rid, CFTYPE_MANIFEST);
321322
322
- if( manifest_parse(&m, &mfile) ){
323
- for(i=0; i<m.nFile; i++){
324
- if( strcmp(m.aFile[i].zName, file)==0 ){
325
- rid = uuid_to_rid(m.aFile[i].zUuid, 0);
326
- manifest_clear(&m);
323
+ if( pManifest ){
324
+ manifest_file_rewind(pManifest);
325
+ while( (pFile = manifest_file_next(pManifest,0))!=0 ){
326
+ if( strcmp(pFile->zName, file)==0 ){
327
+ rid = uuid_to_rid(pFile->zUuid, 0);
328
+ manifest_destroy(pManifest);
327329
return content_get(rid, content);
328330
}
329331
}
330
- manifest_clear(&m);
332
+ manifest_destroy(pManifest);
331333
if( errCode<=0 ){
332334
fossil_fatal("file %s does not exist in checkin: %s", file, revision);
333335
}
334336
}else if( errCode<=0 ){
335337
fossil_panic("could not parse manifest for checkin: %s", revision);
@@ -386,14 +388,15 @@
386388
}else{
387389
int vid;
388390
vid = db_lget_int("checkout", 0);
389391
vfile_check_signature(vid, 0);
390392
db_multi_exec(
393
+ "DELETE FROM vmerge;"
391394
"INSERT INTO torevert "
392395
"SELECT pathname"
393396
" FROM vfile "
394
- " WHERE chnged OR deleted OR rid=0 OR pathname!=origname"
397
+ " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
395398
);
396399
}
397400
blob_zero(&record);
398401
db_prepare(&q, "SELECT name FROM torevert");
399402
while( db_step(&q)==SQLITE_ROW ){
400403
--- src/update.c
+++ src/update.c
@@ -279,10 +279,11 @@
279 db_end_transaction(1); /* With --nochange, rollback changes */
280 }else{
281 if( g.argc<=3 ){
282 /* All files updated. Shift the current checkout to the target. */
283 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
 
284 manifest_to_disk(tid);
285 db_lset_int("checkout", tid);
286 }else{
287 /* A subset of files have been checked out. Keep the current
288 ** checkout unchanged. */
@@ -302,13 +303,13 @@
302 const char *revision, /* The checkin containing the file */
303 const char *file, /* Full treename of the file */
304 Blob *content, /* Put the content here */
305 int errCode /* Error code if file not found. Panic if 0. */
306 ){
307 Blob mfile;
308 Manifest m;
309 int i, rid=0;
310
311 if( revision ){
312 rid = name_to_rid(revision);
313 }else{
314 rid = db_lget_int("checkout", 0);
@@ -315,21 +316,22 @@
315 }
316 if( !is_a_version(rid) ){
317 if( errCode>0 ) return errCode;
318 fossil_fatal("no such checkin: %s", revision);
319 }
320 content_get(rid, &mfile);
321
322 if( manifest_parse(&m, &mfile) ){
323 for(i=0; i<m.nFile; i++){
324 if( strcmp(m.aFile[i].zName, file)==0 ){
325 rid = uuid_to_rid(m.aFile[i].zUuid, 0);
326 manifest_clear(&m);
 
327 return content_get(rid, content);
328 }
329 }
330 manifest_clear(&m);
331 if( errCode<=0 ){
332 fossil_fatal("file %s does not exist in checkin: %s", file, revision);
333 }
334 }else if( errCode<=0 ){
335 fossil_panic("could not parse manifest for checkin: %s", revision);
@@ -386,14 +388,15 @@
386 }else{
387 int vid;
388 vid = db_lget_int("checkout", 0);
389 vfile_check_signature(vid, 0);
390 db_multi_exec(
 
391 "INSERT INTO torevert "
392 "SELECT pathname"
393 " FROM vfile "
394 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname"
395 );
396 }
397 blob_zero(&record);
398 db_prepare(&q, "SELECT name FROM torevert");
399 while( db_step(&q)==SQLITE_ROW ){
400
--- src/update.c
+++ src/update.c
@@ -279,10 +279,11 @@
279 db_end_transaction(1); /* With --nochange, rollback changes */
280 }else{
281 if( g.argc<=3 ){
282 /* All files updated. Shift the current checkout to the target. */
283 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
284 checkout_set_all_exe(vid);
285 manifest_to_disk(tid);
286 db_lset_int("checkout", tid);
287 }else{
288 /* A subset of files have been checked out. Keep the current
289 ** checkout unchanged. */
@@ -302,13 +303,13 @@
303 const char *revision, /* The checkin containing the file */
304 const char *file, /* Full treename of the file */
305 Blob *content, /* Put the content here */
306 int errCode /* Error code if file not found. Panic if 0. */
307 ){
308 Manifest *pManifest;
309 ManifestFile *pFile;
310 int rid=0;
311
312 if( revision ){
313 rid = name_to_rid(revision);
314 }else{
315 rid = db_lget_int("checkout", 0);
@@ -315,21 +316,22 @@
316 }
317 if( !is_a_version(rid) ){
318 if( errCode>0 ) return errCode;
319 fossil_fatal("no such checkin: %s", revision);
320 }
321 pManifest = manifest_get(rid, CFTYPE_MANIFEST);
322
323 if( pManifest ){
324 manifest_file_rewind(pManifest);
325 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
326 if( strcmp(pFile->zName, file)==0 ){
327 rid = uuid_to_rid(pFile->zUuid, 0);
328 manifest_destroy(pManifest);
329 return content_get(rid, content);
330 }
331 }
332 manifest_destroy(pManifest);
333 if( errCode<=0 ){
334 fossil_fatal("file %s does not exist in checkin: %s", file, revision);
335 }
336 }else if( errCode<=0 ){
337 fossil_panic("could not parse manifest for checkin: %s", revision);
@@ -386,14 +388,15 @@
388 }else{
389 int vid;
390 vid = db_lget_int("checkout", 0);
391 vfile_check_signature(vid, 0);
392 db_multi_exec(
393 "DELETE FROM vmerge;"
394 "INSERT INTO torevert "
395 "SELECT pathname"
396 " FROM vfile "
397 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
398 );
399 }
400 blob_zero(&record);
401 db_prepare(&q, "SELECT name FROM torevert");
402 while( db_step(&q)==SQLITE_ROW ){
403
+14 -11
--- src/update.c
+++ src/update.c
@@ -279,10 +279,11 @@
279279
db_end_transaction(1); /* With --nochange, rollback changes */
280280
}else{
281281
if( g.argc<=3 ){
282282
/* All files updated. Shift the current checkout to the target. */
283283
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
284
+ checkout_set_all_exe(vid);
284285
manifest_to_disk(tid);
285286
db_lset_int("checkout", tid);
286287
}else{
287288
/* A subset of files have been checked out. Keep the current
288289
** checkout unchanged. */
@@ -302,13 +303,13 @@
302303
const char *revision, /* The checkin containing the file */
303304
const char *file, /* Full treename of the file */
304305
Blob *content, /* Put the content here */
305306
int errCode /* Error code if file not found. Panic if 0. */
306307
){
307
- Blob mfile;
308
- Manifest m;
309
- int i, rid=0;
308
+ Manifest *pManifest;
309
+ ManifestFile *pFile;
310
+ int rid=0;
310311
311312
if( revision ){
312313
rid = name_to_rid(revision);
313314
}else{
314315
rid = db_lget_int("checkout", 0);
@@ -315,21 +316,22 @@
315316
}
316317
if( !is_a_version(rid) ){
317318
if( errCode>0 ) return errCode;
318319
fossil_fatal("no such checkin: %s", revision);
319320
}
320
- content_get(rid, &mfile);
321
+ pManifest = manifest_get(rid, CFTYPE_MANIFEST);
321322
322
- if( manifest_parse(&m, &mfile) ){
323
- for(i=0; i<m.nFile; i++){
324
- if( strcmp(m.aFile[i].zName, file)==0 ){
325
- rid = uuid_to_rid(m.aFile[i].zUuid, 0);
326
- manifest_clear(&m);
323
+ if( pManifest ){
324
+ manifest_file_rewind(pManifest);
325
+ while( (pFile = manifest_file_next(pManifest,0))!=0 ){
326
+ if( strcmp(pFile->zName, file)==0 ){
327
+ rid = uuid_to_rid(pFile->zUuid, 0);
328
+ manifest_destroy(pManifest);
327329
return content_get(rid, content);
328330
}
329331
}
330
- manifest_clear(&m);
332
+ manifest_destroy(pManifest);
331333
if( errCode<=0 ){
332334
fossil_fatal("file %s does not exist in checkin: %s", file, revision);
333335
}
334336
}else if( errCode<=0 ){
335337
fossil_panic("could not parse manifest for checkin: %s", revision);
@@ -386,14 +388,15 @@
386388
}else{
387389
int vid;
388390
vid = db_lget_int("checkout", 0);
389391
vfile_check_signature(vid, 0);
390392
db_multi_exec(
393
+ "DELETE FROM vmerge;"
391394
"INSERT INTO torevert "
392395
"SELECT pathname"
393396
" FROM vfile "
394
- " WHERE chnged OR deleted OR rid=0 OR pathname!=origname"
397
+ " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
395398
);
396399
}
397400
blob_zero(&record);
398401
db_prepare(&q, "SELECT name FROM torevert");
399402
while( db_step(&q)==SQLITE_ROW ){
400403
--- src/update.c
+++ src/update.c
@@ -279,10 +279,11 @@
279 db_end_transaction(1); /* With --nochange, rollback changes */
280 }else{
281 if( g.argc<=3 ){
282 /* All files updated. Shift the current checkout to the target. */
283 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
 
284 manifest_to_disk(tid);
285 db_lset_int("checkout", tid);
286 }else{
287 /* A subset of files have been checked out. Keep the current
288 ** checkout unchanged. */
@@ -302,13 +303,13 @@
302 const char *revision, /* The checkin containing the file */
303 const char *file, /* Full treename of the file */
304 Blob *content, /* Put the content here */
305 int errCode /* Error code if file not found. Panic if 0. */
306 ){
307 Blob mfile;
308 Manifest m;
309 int i, rid=0;
310
311 if( revision ){
312 rid = name_to_rid(revision);
313 }else{
314 rid = db_lget_int("checkout", 0);
@@ -315,21 +316,22 @@
315 }
316 if( !is_a_version(rid) ){
317 if( errCode>0 ) return errCode;
318 fossil_fatal("no such checkin: %s", revision);
319 }
320 content_get(rid, &mfile);
321
322 if( manifest_parse(&m, &mfile) ){
323 for(i=0; i<m.nFile; i++){
324 if( strcmp(m.aFile[i].zName, file)==0 ){
325 rid = uuid_to_rid(m.aFile[i].zUuid, 0);
326 manifest_clear(&m);
 
327 return content_get(rid, content);
328 }
329 }
330 manifest_clear(&m);
331 if( errCode<=0 ){
332 fossil_fatal("file %s does not exist in checkin: %s", file, revision);
333 }
334 }else if( errCode<=0 ){
335 fossil_panic("could not parse manifest for checkin: %s", revision);
@@ -386,14 +388,15 @@
386 }else{
387 int vid;
388 vid = db_lget_int("checkout", 0);
389 vfile_check_signature(vid, 0);
390 db_multi_exec(
 
391 "INSERT INTO torevert "
392 "SELECT pathname"
393 " FROM vfile "
394 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname"
395 );
396 }
397 blob_zero(&record);
398 db_prepare(&q, "SELECT name FROM torevert");
399 while( db_step(&q)==SQLITE_ROW ){
400
--- src/update.c
+++ src/update.c
@@ -279,10 +279,11 @@
279 db_end_transaction(1); /* With --nochange, rollback changes */
280 }else{
281 if( g.argc<=3 ){
282 /* All files updated. Shift the current checkout to the target. */
283 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
284 checkout_set_all_exe(vid);
285 manifest_to_disk(tid);
286 db_lset_int("checkout", tid);
287 }else{
288 /* A subset of files have been checked out. Keep the current
289 ** checkout unchanged. */
@@ -302,13 +303,13 @@
303 const char *revision, /* The checkin containing the file */
304 const char *file, /* Full treename of the file */
305 Blob *content, /* Put the content here */
306 int errCode /* Error code if file not found. Panic if 0. */
307 ){
308 Manifest *pManifest;
309 ManifestFile *pFile;
310 int rid=0;
311
312 if( revision ){
313 rid = name_to_rid(revision);
314 }else{
315 rid = db_lget_int("checkout", 0);
@@ -315,21 +316,22 @@
316 }
317 if( !is_a_version(rid) ){
318 if( errCode>0 ) return errCode;
319 fossil_fatal("no such checkin: %s", revision);
320 }
321 pManifest = manifest_get(rid, CFTYPE_MANIFEST);
322
323 if( pManifest ){
324 manifest_file_rewind(pManifest);
325 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
326 if( strcmp(pFile->zName, file)==0 ){
327 rid = uuid_to_rid(pFile->zUuid, 0);
328 manifest_destroy(pManifest);
329 return content_get(rid, content);
330 }
331 }
332 manifest_destroy(pManifest);
333 if( errCode<=0 ){
334 fossil_fatal("file %s does not exist in checkin: %s", file, revision);
335 }
336 }else if( errCode<=0 ){
337 fossil_panic("could not parse manifest for checkin: %s", revision);
@@ -386,14 +388,15 @@
388 }else{
389 int vid;
390 vid = db_lget_int("checkout", 0);
391 vfile_check_signature(vid, 0);
392 db_multi_exec(
393 "DELETE FROM vmerge;"
394 "INSERT INTO torevert "
395 "SELECT pathname"
396 " FROM vfile "
397 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
398 );
399 }
400 blob_zero(&record);
401 db_prepare(&q, "SELECT name FROM torevert");
402 while( db_step(&q)==SQLITE_ROW ){
403
+31 -46
--- src/vfile.c
+++ src/vfile.c
@@ -85,57 +85,38 @@
8585
}
8686
}
8787
}
8888
8989
/*
90
-** Build a catalog of all files in a baseline.
91
-** We scan the baseline file for lines of the form:
92
-**
93
-** F NAME UUID
94
-**
95
-** Each such line makes an entry in the VFILE table.
90
+** Build a catalog of all files in a checkin.
9691
*/
97
-void vfile_build(int vid, Blob *p){
92
+void vfile_build(int vid){
9893
int rid;
99
- char *zName, *zUuid;
10094
Stmt ins;
101
- Blob line, token, name, uuid;
102
- int seenHeader = 0;
95
+ Manifest *p;
96
+ ManifestFile *pFile;
97
+
10398
db_begin_transaction();
10499
vfile_verify_not_phantom(vid, 0, 0);
100
+ p = manifest_get(vid, CFTYPE_MANIFEST);
101
+ if( p==0 ) return;
105102
db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
106103
db_prepare(&ins,
107104
"INSERT INTO vfile(vid,rid,mrid,pathname) "
108105
" VALUES(:vid,:id,:id,:name)");
109106
db_bind_int(&ins, ":vid", vid);
110
- while( blob_line(p, &line) ){
111
- char *z = blob_buffer(&line);
112
- if( z[0]=='-' ){
113
- if( seenHeader ) break;
114
- while( blob_line(p, &line)>2 ){}
115
- if( blob_line(p, &line)==0 ) break;
116
- }
117
- seenHeader = 1;
118
- if( z[0]!='F' || z[1]!=' ' ) continue;
119
- blob_token(&line, &token); /* Skip the "F" token */
120
- if( blob_token(&line, &name)==0 ) break;
121
- if( blob_token(&line, &uuid)==0 ) break;
122
- zName = blob_str(&name);
123
- defossilize(zName);
124
- zUuid = blob_str(&uuid);
125
- rid = uuid_to_rid(zUuid, 0);
126
- vfile_verify_not_phantom(rid, zName, zUuid);
127
- if( rid>0 && file_is_simple_pathname(zName) ){
128
- db_bind_int(&ins, ":id", rid);
129
- db_bind_text(&ins, ":name", zName);
130
- db_step(&ins);
131
- db_reset(&ins);
132
- }
133
- blob_reset(&name);
134
- blob_reset(&uuid);
107
+ manifest_file_rewind(p);
108
+ while( (pFile = manifest_file_next(p,0))!=0 ){
109
+ rid = uuid_to_rid(pFile->zUuid, 0);
110
+ vfile_verify_not_phantom(rid, pFile->zName, pFile->zUuid);
111
+ db_bind_int(&ins, ":id", rid);
112
+ db_bind_text(&ins, ":name", pFile->zName);
113
+ db_step(&ins);
114
+ db_reset(&ins);
135115
}
136116
db_finalize(&ins);
117
+ manifest_destroy(p);
137118
db_end_transaction(0);
138119
}
139120
140121
/*
141122
** Check the file signature of the disk image for every VFILE of vid.
@@ -369,10 +350,11 @@
369350
}
370351
fseek(in, 0L, SEEK_END);
371352
sprintf(zBuf, " %ld\n", ftell(in));
372353
fseek(in, 0L, SEEK_SET);
373354
md5sum_step_text(zBuf, -1);
355
+ /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
374356
for(;;){
375357
int n;
376358
n = fread(zBuf, 1, sizeof(zBuf), in);
377359
if( n<=0 ) break;
378360
md5sum_step_text(zBuf, n);
@@ -422,10 +404,11 @@
422404
int rid = db_column_int(&q, 1);
423405
md5sum_step_text(zName, -1);
424406
content_get(rid, &file);
425407
sprintf(zBuf, " %d\n", blob_size(&file));
426408
md5sum_step_text(zBuf, -1);
409
+ /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
427410
md5sum_step_blob(&file);
428411
blob_reset(&file);
429412
}
430413
db_finalize(&q);
431414
md5sum_finish(pOut);
@@ -438,41 +421,43 @@
438421
**
439422
** If pManOut is not NULL then fill it with the checksum found in the
440423
** "R" card near the end of the manifest.
441424
*/
442425
void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){
443
- int i, fid;
444
- Blob file, mfile;
445
- Manifest m;
426
+ int fid;
427
+ Blob file;
428
+ Manifest *pManifest;
429
+ ManifestFile *pFile;
446430
char zBuf[100];
447431
448432
blob_zero(pOut);
449433
if( pManOut ){
450434
blob_zero(pManOut);
451435
}
452436
db_must_be_within_tree();
453
- content_get(vid, &mfile);
454
- if( manifest_parse(&m, &mfile)==0 ){
437
+ pManifest = manifest_get(vid, CFTYPE_MANIFEST);
438
+ if( pManifest==0 ){
455439
fossil_panic("manifest file (%d) is malformed", vid);
456440
}
457
- for(i=0; i<m.nFile; i++){
458
- fid = uuid_to_rid(m.aFile[i].zUuid, 0);
459
- md5sum_step_text(m.aFile[i].zName, -1);
441
+ manifest_file_rewind(pManifest);
442
+ while( (pFile = manifest_file_next(pManifest,0))!=0 ){
443
+ fid = uuid_to_rid(pFile->zUuid, 0);
444
+ md5sum_step_text(pFile->zName, -1);
460445
content_get(fid, &file);
461446
sprintf(zBuf, " %d\n", blob_size(&file));
462447
md5sum_step_text(zBuf, -1);
463448
md5sum_step_blob(&file);
464449
blob_reset(&file);
465450
}
466451
if( pManOut ){
467
- if( m.zRepoCksum ){
468
- blob_append(pManOut, m.zRepoCksum, -1);
452
+ if( pManifest->zRepoCksum ){
453
+ blob_append(pManOut, pManifest->zRepoCksum, -1);
469454
}else{
470455
blob_zero(pManOut);
471456
}
472457
}
473
- manifest_clear(&m);
458
+ manifest_destroy(pManifest);
474459
md5sum_finish(pOut);
475460
}
476461
477462
/*
478463
** COMMAND: test-agg-cksum
479464
--- src/vfile.c
+++ src/vfile.c
@@ -85,57 +85,38 @@
85 }
86 }
87 }
88
89 /*
90 ** Build a catalog of all files in a baseline.
91 ** We scan the baseline file for lines of the form:
92 **
93 ** F NAME UUID
94 **
95 ** Each such line makes an entry in the VFILE table.
96 */
97 void vfile_build(int vid, Blob *p){
98 int rid;
99 char *zName, *zUuid;
100 Stmt ins;
101 Blob line, token, name, uuid;
102 int seenHeader = 0;
 
103 db_begin_transaction();
104 vfile_verify_not_phantom(vid, 0, 0);
 
 
105 db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
106 db_prepare(&ins,
107 "INSERT INTO vfile(vid,rid,mrid,pathname) "
108 " VALUES(:vid,:id,:id,:name)");
109 db_bind_int(&ins, ":vid", vid);
110 while( blob_line(p, &line) ){
111 char *z = blob_buffer(&line);
112 if( z[0]=='-' ){
113 if( seenHeader ) break;
114 while( blob_line(p, &line)>2 ){}
115 if( blob_line(p, &line)==0 ) break;
116 }
117 seenHeader = 1;
118 if( z[0]!='F' || z[1]!=' ' ) continue;
119 blob_token(&line, &token); /* Skip the "F" token */
120 if( blob_token(&line, &name)==0 ) break;
121 if( blob_token(&line, &uuid)==0 ) break;
122 zName = blob_str(&name);
123 defossilize(zName);
124 zUuid = blob_str(&uuid);
125 rid = uuid_to_rid(zUuid, 0);
126 vfile_verify_not_phantom(rid, zName, zUuid);
127 if( rid>0 && file_is_simple_pathname(zName) ){
128 db_bind_int(&ins, ":id", rid);
129 db_bind_text(&ins, ":name", zName);
130 db_step(&ins);
131 db_reset(&ins);
132 }
133 blob_reset(&name);
134 blob_reset(&uuid);
135 }
136 db_finalize(&ins);
 
137 db_end_transaction(0);
138 }
139
140 /*
141 ** Check the file signature of the disk image for every VFILE of vid.
@@ -369,10 +350,11 @@
369 }
370 fseek(in, 0L, SEEK_END);
371 sprintf(zBuf, " %ld\n", ftell(in));
372 fseek(in, 0L, SEEK_SET);
373 md5sum_step_text(zBuf, -1);
 
374 for(;;){
375 int n;
376 n = fread(zBuf, 1, sizeof(zBuf), in);
377 if( n<=0 ) break;
378 md5sum_step_text(zBuf, n);
@@ -422,10 +404,11 @@
422 int rid = db_column_int(&q, 1);
423 md5sum_step_text(zName, -1);
424 content_get(rid, &file);
425 sprintf(zBuf, " %d\n", blob_size(&file));
426 md5sum_step_text(zBuf, -1);
 
427 md5sum_step_blob(&file);
428 blob_reset(&file);
429 }
430 db_finalize(&q);
431 md5sum_finish(pOut);
@@ -438,41 +421,43 @@
438 **
439 ** If pManOut is not NULL then fill it with the checksum found in the
440 ** "R" card near the end of the manifest.
441 */
442 void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){
443 int i, fid;
444 Blob file, mfile;
445 Manifest m;
 
446 char zBuf[100];
447
448 blob_zero(pOut);
449 if( pManOut ){
450 blob_zero(pManOut);
451 }
452 db_must_be_within_tree();
453 content_get(vid, &mfile);
454 if( manifest_parse(&m, &mfile)==0 ){
455 fossil_panic("manifest file (%d) is malformed", vid);
456 }
457 for(i=0; i<m.nFile; i++){
458 fid = uuid_to_rid(m.aFile[i].zUuid, 0);
459 md5sum_step_text(m.aFile[i].zName, -1);
 
460 content_get(fid, &file);
461 sprintf(zBuf, " %d\n", blob_size(&file));
462 md5sum_step_text(zBuf, -1);
463 md5sum_step_blob(&file);
464 blob_reset(&file);
465 }
466 if( pManOut ){
467 if( m.zRepoCksum ){
468 blob_append(pManOut, m.zRepoCksum, -1);
469 }else{
470 blob_zero(pManOut);
471 }
472 }
473 manifest_clear(&m);
474 md5sum_finish(pOut);
475 }
476
477 /*
478 ** COMMAND: test-agg-cksum
479
--- src/vfile.c
+++ src/vfile.c
@@ -85,57 +85,38 @@
85 }
86 }
87 }
88
89 /*
90 ** Build a catalog of all files in a checkin.
 
 
 
 
 
91 */
92 void vfile_build(int vid){
93 int rid;
 
94 Stmt ins;
95 Manifest *p;
96 ManifestFile *pFile;
97
98 db_begin_transaction();
99 vfile_verify_not_phantom(vid, 0, 0);
100 p = manifest_get(vid, CFTYPE_MANIFEST);
101 if( p==0 ) return;
102 db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
103 db_prepare(&ins,
104 "INSERT INTO vfile(vid,rid,mrid,pathname) "
105 " VALUES(:vid,:id,:id,:name)");
106 db_bind_int(&ins, ":vid", vid);
107 manifest_file_rewind(p);
108 while( (pFile = manifest_file_next(p,0))!=0 ){
109 rid = uuid_to_rid(pFile->zUuid, 0);
110 vfile_verify_not_phantom(rid, pFile->zName, pFile->zUuid);
111 db_bind_int(&ins, ":id", rid);
112 db_bind_text(&ins, ":name", pFile->zName);
113 db_step(&ins);
114 db_reset(&ins);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115 }
116 db_finalize(&ins);
117 manifest_destroy(p);
118 db_end_transaction(0);
119 }
120
121 /*
122 ** Check the file signature of the disk image for every VFILE of vid.
@@ -369,10 +350,11 @@
350 }
351 fseek(in, 0L, SEEK_END);
352 sprintf(zBuf, " %ld\n", ftell(in));
353 fseek(in, 0L, SEEK_SET);
354 md5sum_step_text(zBuf, -1);
355 /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
356 for(;;){
357 int n;
358 n = fread(zBuf, 1, sizeof(zBuf), in);
359 if( n<=0 ) break;
360 md5sum_step_text(zBuf, n);
@@ -422,10 +404,11 @@
404 int rid = db_column_int(&q, 1);
405 md5sum_step_text(zName, -1);
406 content_get(rid, &file);
407 sprintf(zBuf, " %d\n", blob_size(&file));
408 md5sum_step_text(zBuf, -1);
409 /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
410 md5sum_step_blob(&file);
411 blob_reset(&file);
412 }
413 db_finalize(&q);
414 md5sum_finish(pOut);
@@ -438,41 +421,43 @@
421 **
422 ** If pManOut is not NULL then fill it with the checksum found in the
423 ** "R" card near the end of the manifest.
424 */
425 void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){
426 int fid;
427 Blob file;
428 Manifest *pManifest;
429 ManifestFile *pFile;
430 char zBuf[100];
431
432 blob_zero(pOut);
433 if( pManOut ){
434 blob_zero(pManOut);
435 }
436 db_must_be_within_tree();
437 pManifest = manifest_get(vid, CFTYPE_MANIFEST);
438 if( pManifest==0 ){
439 fossil_panic("manifest file (%d) is malformed", vid);
440 }
441 manifest_file_rewind(pManifest);
442 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
443 fid = uuid_to_rid(pFile->zUuid, 0);
444 md5sum_step_text(pFile->zName, -1);
445 content_get(fid, &file);
446 sprintf(zBuf, " %d\n", blob_size(&file));
447 md5sum_step_text(zBuf, -1);
448 md5sum_step_blob(&file);
449 blob_reset(&file);
450 }
451 if( pManOut ){
452 if( pManifest->zRepoCksum ){
453 blob_append(pManOut, pManifest->zRepoCksum, -1);
454 }else{
455 blob_zero(pManOut);
456 }
457 }
458 manifest_destroy(pManifest);
459 md5sum_finish(pOut);
460 }
461
462 /*
463 ** COMMAND: test-agg-cksum
464
+32 -58
--- src/wiki.c
+++ src/wiki.c
@@ -127,11 +127,11 @@
127127
void wiki_page(void){
128128
char *zTag;
129129
int rid = 0;
130130
int isSandbox;
131131
Blob wiki;
132
- Manifest m;
132
+ Manifest *pWiki = 0;
133133
const char *zPageName;
134134
char *zBody = mprintf("%s","<i>Empty Page</i>");
135135
Stmt q;
136136
int cnt = 0;
137137
@@ -179,20 +179,14 @@
179179
"SELECT rid FROM tagxref"
180180
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
181181
" ORDER BY mtime DESC", zTag
182182
);
183183
free(zTag);
184
- memset(&m, 0, sizeof(m));
185
- blob_zero(&m.content);
186
- if( rid ){
187
- Blob content;
188
- content_get(rid, &content);
189
- manifest_parse(&m, &content);
190
- if( m.type==CFTYPE_WIKI && m.zWiki ){
191
- while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
192
- if( m.zWiki[0] ) zBody = m.zWiki;
193
- }
184
+ pWiki = manifest_get(rid, CFTYPE_WIKI);
185
+ if( pWiki ){
186
+ while( fossil_isspace(pWiki->zWiki[0]) ) pWiki->zWiki++;
187
+ if( pWiki->zWiki[0] ) zBody = pWiki->zWiki;
194188
}
195189
}
196190
if( !g.isHome ){
197191
if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
198192
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
@@ -249,13 +243,11 @@
249243
if( cnt ){
250244
@ </ul>
251245
}
252246
db_finalize(&q);
253247
254
- if( !isSandbox ){
255
- manifest_clear(&m);
256
- }
248
+ manifest_destroy(pWiki);
257249
/* don'tshow the help cross reference, because we are rendering
258250
** the wiki conten!
259251
** style_footer_cmdref("wiki","export");
260252
*/
261253
style_footer();
@@ -268,11 +260,11 @@
268260
void wikiedit_page(void){
269261
char *zTag;
270262
int rid = 0;
271263
int isSandbox;
272264
Blob wiki;
273
- Manifest m;
265
+ Manifest *pWiki = 0;
274266
const char *zPageName;
275267
char *zHtmlPageName;
276268
int n;
277269
const char *z;
278270
char *zBody = (char*)P("w");
@@ -302,19 +294,12 @@
302294
free(zTag);
303295
if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
304296
login_needed();
305297
return;
306298
}
307
- memset(&m, 0, sizeof(m));
308
- blob_zero(&m.content);
309
- if( rid && zBody==0 ){
310
- Blob content;
311
- content_get(rid, &content);
312
- manifest_parse(&m, &content);
313
- if( m.type==CFTYPE_WIKI ){
314
- zBody = m.zWiki;
315
- }
299
+ if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
300
+ zBody = pWiki->zWiki;
316301
}
317302
}
318303
if( P("submit")!=0 && zBody!=0 ){
319304
char *zDate;
320305
Blob cksum;
@@ -381,13 +366,11 @@
381366
@ <br />
382367
@ <input type="submit" name="preview" value="Preview Your Changes" />
383368
@ <input type="submit" name="submit" value="Apply These Changes" />
384369
@ <input type="submit" name="cancel" value="Cancel" />
385370
@ </div></form>
386
- if( !isSandbox ){
387
- manifest_clear(&m);
388
- }
371
+ manifest_destroy(pWiki);
389372
style_footer_cmdref("wiki"," / <a href=\"wiki_rules\">wiki format</a>");
390373
}
391374
392375
/*
393376
** WEBPAGE: wikinew
@@ -481,27 +464,25 @@
481464
if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
482465
char *zDate;
483466
Blob cksum;
484467
int nrid;
485468
Blob body;
486
- Blob content;
487469
Blob wiki;
488
- Manifest m;
470
+ Manifest *pWiki = 0;
489471
490472
blob_zero(&body);
491473
if( isSandbox ){
492474
blob_appendf(&body, db_get("sandbox",""));
493475
appendRemark(&body);
494476
db_set("sandbox", blob_str(&body), 0);
495477
}else{
496478
login_verify_csrf_secret();
497
- content_get(rid, &content);
498
- manifest_parse(&m, &content);
499
- if( m.type==CFTYPE_WIKI ){
500
- blob_append(&body, m.zWiki, -1);
479
+ pWiki = manifest_get(rid, CFTYPE_WIKI);
480
+ if( pWiki ){
481
+ blob_append(&body, pWiki->zWiki, -1);
482
+ manifest_destroy(pWiki);
501483
}
502
- manifest_clear(&m);
503484
blob_zero(&wiki);
504485
db_begin_transaction();
505486
zDate = db_text(0, "SELECT datetime('now')");
506487
zDate[10] = 'T';
507488
blob_appendf(&wiki, "D %s\n", zDate);
@@ -616,12 +597,11 @@
616597
*/
617598
void wdiff_page(void){
618599
char *zTitle;
619600
int rid1, rid2;
620601
const char *zPageName;
621
- Blob content1, content2;
622
- Manifest m1, m2;
602
+ Manifest *pW1, *pW2 = 0;
623603
Blob w1, w2, d;
624604
625605
login_check_credentials();
626606
rid1 = atoi(PD("a","0"));
627607
if( !g.okHistory ){ login_needed(); return; }
@@ -639,27 +619,24 @@
639619
" WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
640620
" ORDER BY event.mtime DESC LIMIT 1",
641621
zPageName, rid1
642622
);
643623
}
644
- content_get(rid1, &content1);
645
- manifest_parse(&m1, &content1);
646
- if( m1.type!=CFTYPE_WIKI ) fossil_redirect_home();
647
- blob_init(&w1, m1.zWiki, -1);
624
+ pW1 = manifest_get(rid1, CFTYPE_WIKI);
625
+ if( pW1==0 ) fossil_redirect_home();
626
+ blob_init(&w1, pW1->zWiki, -1);
648627
blob_zero(&w2);
649
- if( rid2 ){
650
- content_get(rid2, &content2);
651
- manifest_parse(&m2, &content2);
652
- if( m2.type==CFTYPE_WIKI ){
653
- blob_init(&w2, m2.zWiki, -1);
654
- }
628
+ if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
629
+ blob_init(&w2, pW2->zWiki, -1);
655630
}
656631
blob_zero(&d);
657632
text_diff(&w2, &w1, &d, 5, 1);
658633
@ <pre>
659634
@ %h(blob_str(&d))
660635
@ </pre>
636
+ manifest_destroy(pW1);
637
+ manifest_destroy(pW2);
661638
style_footer();
662639
}
663640
664641
/*
665642
** WEBPAGE: wcontent
@@ -921,30 +898,26 @@
921898
}
922899
923900
if( strncmp(g.argv[2],"export",n)==0 ){
924901
char const *zPageName; /* Name of the wiki page to export */
925902
char const *zFile; /* Name of the output file (0=stdout) */
926
- int rid; /* Artifact ID of the wiki page */
927
- int i; /* Loop counter */
928
- char *zBody = 0; /* Wiki page content */
929
- Manifest m; /* Parsed wiki page content */
903
+ int rid; /* Artifact ID of the wiki page */
904
+ int i; /* Loop counter */
905
+ char *zBody = 0; /* Wiki page content */
906
+ Manifest *pWiki = 0; /* Parsed wiki page content */
907
+
930908
if( (g.argc!=4) && (g.argc!=5) ){
931909
usage("export PAGENAME ?FILE?");
932910
}
933911
zPageName = g.argv[3];
934912
rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
935913
" WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
936914
" ORDER BY x.mtime DESC LIMIT 1",
937915
zPageName
938916
);
939
- if( rid ){
940
- Blob content;
941
- content_get(rid, &content);
942
- manifest_parse(&m, &content);
943
- if( m.type==CFTYPE_WIKI ){
944
- zBody = m.zWiki;
945
- }
917
+ if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
918
+ zBody = pWiki->zWiki;
946919
}
947920
if( zBody==0 ){
948921
fossil_fatal("wiki page [%s] not found",zPageName);
949922
}
950923
for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){}
@@ -962,12 +935,13 @@
962935
fossil_fatal("wiki export could not open output file for writing.");
963936
}
964937
fprintf(zF,"%.*s\n", i, zBody);
965938
if( doClose ) fclose(zF);
966939
}else{
967
- printf("%.*s\n", i, zBody);
940
+ printf("%.*s\n", i, zBody);
968941
}
942
+ manifest_destroy(pWiki);
969943
return;
970944
}else
971945
if( strncmp(g.argv[2],"commit",n)==0
972946
|| strncmp(g.argv[2],"create",n)==0 ){
973947
char *zPageName;
974948
--- src/wiki.c
+++ src/wiki.c
@@ -127,11 +127,11 @@
127 void wiki_page(void){
128 char *zTag;
129 int rid = 0;
130 int isSandbox;
131 Blob wiki;
132 Manifest m;
133 const char *zPageName;
134 char *zBody = mprintf("%s","<i>Empty Page</i>");
135 Stmt q;
136 int cnt = 0;
137
@@ -179,20 +179,14 @@
179 "SELECT rid FROM tagxref"
180 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
181 " ORDER BY mtime DESC", zTag
182 );
183 free(zTag);
184 memset(&m, 0, sizeof(m));
185 blob_zero(&m.content);
186 if( rid ){
187 Blob content;
188 content_get(rid, &content);
189 manifest_parse(&m, &content);
190 if( m.type==CFTYPE_WIKI && m.zWiki ){
191 while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
192 if( m.zWiki[0] ) zBody = m.zWiki;
193 }
194 }
195 }
196 if( !g.isHome ){
197 if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
198 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
@@ -249,13 +243,11 @@
249 if( cnt ){
250 @ </ul>
251 }
252 db_finalize(&q);
253
254 if( !isSandbox ){
255 manifest_clear(&m);
256 }
257 /* don'tshow the help cross reference, because we are rendering
258 ** the wiki conten!
259 ** style_footer_cmdref("wiki","export");
260 */
261 style_footer();
@@ -268,11 +260,11 @@
268 void wikiedit_page(void){
269 char *zTag;
270 int rid = 0;
271 int isSandbox;
272 Blob wiki;
273 Manifest m;
274 const char *zPageName;
275 char *zHtmlPageName;
276 int n;
277 const char *z;
278 char *zBody = (char*)P("w");
@@ -302,19 +294,12 @@
302 free(zTag);
303 if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
304 login_needed();
305 return;
306 }
307 memset(&m, 0, sizeof(m));
308 blob_zero(&m.content);
309 if( rid && zBody==0 ){
310 Blob content;
311 content_get(rid, &content);
312 manifest_parse(&m, &content);
313 if( m.type==CFTYPE_WIKI ){
314 zBody = m.zWiki;
315 }
316 }
317 }
318 if( P("submit")!=0 && zBody!=0 ){
319 char *zDate;
320 Blob cksum;
@@ -381,13 +366,11 @@
381 @ <br />
382 @ <input type="submit" name="preview" value="Preview Your Changes" />
383 @ <input type="submit" name="submit" value="Apply These Changes" />
384 @ <input type="submit" name="cancel" value="Cancel" />
385 @ </div></form>
386 if( !isSandbox ){
387 manifest_clear(&m);
388 }
389 style_footer_cmdref("wiki"," / <a href=\"wiki_rules\">wiki format</a>");
390 }
391
392 /*
393 ** WEBPAGE: wikinew
@@ -481,27 +464,25 @@
481 if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
482 char *zDate;
483 Blob cksum;
484 int nrid;
485 Blob body;
486 Blob content;
487 Blob wiki;
488 Manifest m;
489
490 blob_zero(&body);
491 if( isSandbox ){
492 blob_appendf(&body, db_get("sandbox",""));
493 appendRemark(&body);
494 db_set("sandbox", blob_str(&body), 0);
495 }else{
496 login_verify_csrf_secret();
497 content_get(rid, &content);
498 manifest_parse(&m, &content);
499 if( m.type==CFTYPE_WIKI ){
500 blob_append(&body, m.zWiki, -1);
501 }
502 manifest_clear(&m);
503 blob_zero(&wiki);
504 db_begin_transaction();
505 zDate = db_text(0, "SELECT datetime('now')");
506 zDate[10] = 'T';
507 blob_appendf(&wiki, "D %s\n", zDate);
@@ -616,12 +597,11 @@
616 */
617 void wdiff_page(void){
618 char *zTitle;
619 int rid1, rid2;
620 const char *zPageName;
621 Blob content1, content2;
622 Manifest m1, m2;
623 Blob w1, w2, d;
624
625 login_check_credentials();
626 rid1 = atoi(PD("a","0"));
627 if( !g.okHistory ){ login_needed(); return; }
@@ -639,27 +619,24 @@
639 " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
640 " ORDER BY event.mtime DESC LIMIT 1",
641 zPageName, rid1
642 );
643 }
644 content_get(rid1, &content1);
645 manifest_parse(&m1, &content1);
646 if( m1.type!=CFTYPE_WIKI ) fossil_redirect_home();
647 blob_init(&w1, m1.zWiki, -1);
648 blob_zero(&w2);
649 if( rid2 ){
650 content_get(rid2, &content2);
651 manifest_parse(&m2, &content2);
652 if( m2.type==CFTYPE_WIKI ){
653 blob_init(&w2, m2.zWiki, -1);
654 }
655 }
656 blob_zero(&d);
657 text_diff(&w2, &w1, &d, 5, 1);
658 @ <pre>
659 @ %h(blob_str(&d))
660 @ </pre>
 
 
661 style_footer();
662 }
663
664 /*
665 ** WEBPAGE: wcontent
@@ -921,30 +898,26 @@
921 }
922
923 if( strncmp(g.argv[2],"export",n)==0 ){
924 char const *zPageName; /* Name of the wiki page to export */
925 char const *zFile; /* Name of the output file (0=stdout) */
926 int rid; /* Artifact ID of the wiki page */
927 int i; /* Loop counter */
928 char *zBody = 0; /* Wiki page content */
929 Manifest m; /* Parsed wiki page content */
 
930 if( (g.argc!=4) && (g.argc!=5) ){
931 usage("export PAGENAME ?FILE?");
932 }
933 zPageName = g.argv[3];
934 rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
935 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
936 " ORDER BY x.mtime DESC LIMIT 1",
937 zPageName
938 );
939 if( rid ){
940 Blob content;
941 content_get(rid, &content);
942 manifest_parse(&m, &content);
943 if( m.type==CFTYPE_WIKI ){
944 zBody = m.zWiki;
945 }
946 }
947 if( zBody==0 ){
948 fossil_fatal("wiki page [%s] not found",zPageName);
949 }
950 for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){}
@@ -962,12 +935,13 @@
962 fossil_fatal("wiki export could not open output file for writing.");
963 }
964 fprintf(zF,"%.*s\n", i, zBody);
965 if( doClose ) fclose(zF);
966 }else{
967 printf("%.*s\n", i, zBody);
968 }
 
969 return;
970 }else
971 if( strncmp(g.argv[2],"commit",n)==0
972 || strncmp(g.argv[2],"create",n)==0 ){
973 char *zPageName;
974
--- src/wiki.c
+++ src/wiki.c
@@ -127,11 +127,11 @@
127 void wiki_page(void){
128 char *zTag;
129 int rid = 0;
130 int isSandbox;
131 Blob wiki;
132 Manifest *pWiki = 0;
133 const char *zPageName;
134 char *zBody = mprintf("%s","<i>Empty Page</i>");
135 Stmt q;
136 int cnt = 0;
137
@@ -179,20 +179,14 @@
179 "SELECT rid FROM tagxref"
180 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
181 " ORDER BY mtime DESC", zTag
182 );
183 free(zTag);
184 pWiki = manifest_get(rid, CFTYPE_WIKI);
185 if( pWiki ){
186 while( fossil_isspace(pWiki->zWiki[0]) ) pWiki->zWiki++;
187 if( pWiki->zWiki[0] ) zBody = pWiki->zWiki;
 
 
 
 
 
 
188 }
189 }
190 if( !g.isHome ){
191 if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
192 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
@@ -249,13 +243,11 @@
243 if( cnt ){
244 @ </ul>
245 }
246 db_finalize(&q);
247
248 manifest_destroy(pWiki);
 
 
249 /* don'tshow the help cross reference, because we are rendering
250 ** the wiki conten!
251 ** style_footer_cmdref("wiki","export");
252 */
253 style_footer();
@@ -268,11 +260,11 @@
260 void wikiedit_page(void){
261 char *zTag;
262 int rid = 0;
263 int isSandbox;
264 Blob wiki;
265 Manifest *pWiki = 0;
266 const char *zPageName;
267 char *zHtmlPageName;
268 int n;
269 const char *z;
270 char *zBody = (char*)P("w");
@@ -302,19 +294,12 @@
294 free(zTag);
295 if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
296 login_needed();
297 return;
298 }
299 if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
300 zBody = pWiki->zWiki;
 
 
 
 
 
 
 
301 }
302 }
303 if( P("submit")!=0 && zBody!=0 ){
304 char *zDate;
305 Blob cksum;
@@ -381,13 +366,11 @@
366 @ <br />
367 @ <input type="submit" name="preview" value="Preview Your Changes" />
368 @ <input type="submit" name="submit" value="Apply These Changes" />
369 @ <input type="submit" name="cancel" value="Cancel" />
370 @ </div></form>
371 manifest_destroy(pWiki);
 
 
372 style_footer_cmdref("wiki"," / <a href=\"wiki_rules\">wiki format</a>");
373 }
374
375 /*
376 ** WEBPAGE: wikinew
@@ -481,27 +464,25 @@
464 if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
465 char *zDate;
466 Blob cksum;
467 int nrid;
468 Blob body;
 
469 Blob wiki;
470 Manifest *pWiki = 0;
471
472 blob_zero(&body);
473 if( isSandbox ){
474 blob_appendf(&body, db_get("sandbox",""));
475 appendRemark(&body);
476 db_set("sandbox", blob_str(&body), 0);
477 }else{
478 login_verify_csrf_secret();
479 pWiki = manifest_get(rid, CFTYPE_WIKI);
480 if( pWiki ){
481 blob_append(&body, pWiki->zWiki, -1);
482 manifest_destroy(pWiki);
483 }
 
484 blob_zero(&wiki);
485 db_begin_transaction();
486 zDate = db_text(0, "SELECT datetime('now')");
487 zDate[10] = 'T';
488 blob_appendf(&wiki, "D %s\n", zDate);
@@ -616,12 +597,11 @@
597 */
598 void wdiff_page(void){
599 char *zTitle;
600 int rid1, rid2;
601 const char *zPageName;
602 Manifest *pW1, *pW2 = 0;
 
603 Blob w1, w2, d;
604
605 login_check_credentials();
606 rid1 = atoi(PD("a","0"));
607 if( !g.okHistory ){ login_needed(); return; }
@@ -639,27 +619,24 @@
619 " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
620 " ORDER BY event.mtime DESC LIMIT 1",
621 zPageName, rid1
622 );
623 }
624 pW1 = manifest_get(rid1, CFTYPE_WIKI);
625 if( pW1==0 ) fossil_redirect_home();
626 blob_init(&w1, pW1->zWiki, -1);
 
627 blob_zero(&w2);
628 if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
629 blob_init(&w2, pW2->zWiki, -1);
 
 
 
 
630 }
631 blob_zero(&d);
632 text_diff(&w2, &w1, &d, 5, 1);
633 @ <pre>
634 @ %h(blob_str(&d))
635 @ </pre>
636 manifest_destroy(pW1);
637 manifest_destroy(pW2);
638 style_footer();
639 }
640
641 /*
642 ** WEBPAGE: wcontent
@@ -921,30 +898,26 @@
898 }
899
900 if( strncmp(g.argv[2],"export",n)==0 ){
901 char const *zPageName; /* Name of the wiki page to export */
902 char const *zFile; /* Name of the output file (0=stdout) */
903 int rid; /* Artifact ID of the wiki page */
904 int i; /* Loop counter */
905 char *zBody = 0; /* Wiki page content */
906 Manifest *pWiki = 0; /* Parsed wiki page content */
907
908 if( (g.argc!=4) && (g.argc!=5) ){
909 usage("export PAGENAME ?FILE?");
910 }
911 zPageName = g.argv[3];
912 rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
913 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
914 " ORDER BY x.mtime DESC LIMIT 1",
915 zPageName
916 );
917 if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
918 zBody = pWiki->zWiki;
 
 
 
 
 
919 }
920 if( zBody==0 ){
921 fossil_fatal("wiki page [%s] not found",zPageName);
922 }
923 for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){}
@@ -962,12 +935,13 @@
935 fossil_fatal("wiki export could not open output file for writing.");
936 }
937 fprintf(zF,"%.*s\n", i, zBody);
938 if( doClose ) fclose(zF);
939 }else{
940 printf("%.*s\n", i, zBody);
941 }
942 manifest_destroy(pWiki);
943 return;
944 }else
945 if( strncmp(g.argv[2],"commit",n)==0
946 || strncmp(g.argv[2],"create",n)==0 ){
947 char *zPageName;
948
+32 -58
--- src/wiki.c
+++ src/wiki.c
@@ -127,11 +127,11 @@
127127
void wiki_page(void){
128128
char *zTag;
129129
int rid = 0;
130130
int isSandbox;
131131
Blob wiki;
132
- Manifest m;
132
+ Manifest *pWiki = 0;
133133
const char *zPageName;
134134
char *zBody = mprintf("%s","<i>Empty Page</i>");
135135
Stmt q;
136136
int cnt = 0;
137137
@@ -179,20 +179,14 @@
179179
"SELECT rid FROM tagxref"
180180
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
181181
" ORDER BY mtime DESC", zTag
182182
);
183183
free(zTag);
184
- memset(&m, 0, sizeof(m));
185
- blob_zero(&m.content);
186
- if( rid ){
187
- Blob content;
188
- content_get(rid, &content);
189
- manifest_parse(&m, &content);
190
- if( m.type==CFTYPE_WIKI && m.zWiki ){
191
- while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
192
- if( m.zWiki[0] ) zBody = m.zWiki;
193
- }
184
+ pWiki = manifest_get(rid, CFTYPE_WIKI);
185
+ if( pWiki ){
186
+ while( fossil_isspace(pWiki->zWiki[0]) ) pWiki->zWiki++;
187
+ if( pWiki->zWiki[0] ) zBody = pWiki->zWiki;
194188
}
195189
}
196190
if( !g.isHome ){
197191
if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
198192
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
@@ -249,13 +243,11 @@
249243
if( cnt ){
250244
@ </ul>
251245
}
252246
db_finalize(&q);
253247
254
- if( !isSandbox ){
255
- manifest_clear(&m);
256
- }
248
+ manifest_destroy(pWiki);
257249
/* don'tshow the help cross reference, because we are rendering
258250
** the wiki conten!
259251
** style_footer_cmdref("wiki","export");
260252
*/
261253
style_footer();
@@ -268,11 +260,11 @@
268260
void wikiedit_page(void){
269261
char *zTag;
270262
int rid = 0;
271263
int isSandbox;
272264
Blob wiki;
273
- Manifest m;
265
+ Manifest *pWiki = 0;
274266
const char *zPageName;
275267
char *zHtmlPageName;
276268
int n;
277269
const char *z;
278270
char *zBody = (char*)P("w");
@@ -302,19 +294,12 @@
302294
free(zTag);
303295
if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
304296
login_needed();
305297
return;
306298
}
307
- memset(&m, 0, sizeof(m));
308
- blob_zero(&m.content);
309
- if( rid && zBody==0 ){
310
- Blob content;
311
- content_get(rid, &content);
312
- manifest_parse(&m, &content);
313
- if( m.type==CFTYPE_WIKI ){
314
- zBody = m.zWiki;
315
- }
299
+ if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
300
+ zBody = pWiki->zWiki;
316301
}
317302
}
318303
if( P("submit")!=0 && zBody!=0 ){
319304
char *zDate;
320305
Blob cksum;
@@ -381,13 +366,11 @@
381366
@ <br />
382367
@ <input type="submit" name="preview" value="Preview Your Changes" />
383368
@ <input type="submit" name="submit" value="Apply These Changes" />
384369
@ <input type="submit" name="cancel" value="Cancel" />
385370
@ </div></form>
386
- if( !isSandbox ){
387
- manifest_clear(&m);
388
- }
371
+ manifest_destroy(pWiki);
389372
style_footer_cmdref("wiki"," / <a href=\"wiki_rules\">wiki format</a>");
390373
}
391374
392375
/*
393376
** WEBPAGE: wikinew
@@ -481,27 +464,25 @@
481464
if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
482465
char *zDate;
483466
Blob cksum;
484467
int nrid;
485468
Blob body;
486
- Blob content;
487469
Blob wiki;
488
- Manifest m;
470
+ Manifest *pWiki = 0;
489471
490472
blob_zero(&body);
491473
if( isSandbox ){
492474
blob_appendf(&body, db_get("sandbox",""));
493475
appendRemark(&body);
494476
db_set("sandbox", blob_str(&body), 0);
495477
}else{
496478
login_verify_csrf_secret();
497
- content_get(rid, &content);
498
- manifest_parse(&m, &content);
499
- if( m.type==CFTYPE_WIKI ){
500
- blob_append(&body, m.zWiki, -1);
479
+ pWiki = manifest_get(rid, CFTYPE_WIKI);
480
+ if( pWiki ){
481
+ blob_append(&body, pWiki->zWiki, -1);
482
+ manifest_destroy(pWiki);
501483
}
502
- manifest_clear(&m);
503484
blob_zero(&wiki);
504485
db_begin_transaction();
505486
zDate = db_text(0, "SELECT datetime('now')");
506487
zDate[10] = 'T';
507488
blob_appendf(&wiki, "D %s\n", zDate);
@@ -616,12 +597,11 @@
616597
*/
617598
void wdiff_page(void){
618599
char *zTitle;
619600
int rid1, rid2;
620601
const char *zPageName;
621
- Blob content1, content2;
622
- Manifest m1, m2;
602
+ Manifest *pW1, *pW2 = 0;
623603
Blob w1, w2, d;
624604
625605
login_check_credentials();
626606
rid1 = atoi(PD("a","0"));
627607
if( !g.okHistory ){ login_needed(); return; }
@@ -639,27 +619,24 @@
639619
" WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
640620
" ORDER BY event.mtime DESC LIMIT 1",
641621
zPageName, rid1
642622
);
643623
}
644
- content_get(rid1, &content1);
645
- manifest_parse(&m1, &content1);
646
- if( m1.type!=CFTYPE_WIKI ) fossil_redirect_home();
647
- blob_init(&w1, m1.zWiki, -1);
624
+ pW1 = manifest_get(rid1, CFTYPE_WIKI);
625
+ if( pW1==0 ) fossil_redirect_home();
626
+ blob_init(&w1, pW1->zWiki, -1);
648627
blob_zero(&w2);
649
- if( rid2 ){
650
- content_get(rid2, &content2);
651
- manifest_parse(&m2, &content2);
652
- if( m2.type==CFTYPE_WIKI ){
653
- blob_init(&w2, m2.zWiki, -1);
654
- }
628
+ if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
629
+ blob_init(&w2, pW2->zWiki, -1);
655630
}
656631
blob_zero(&d);
657632
text_diff(&w2, &w1, &d, 5, 1);
658633
@ <pre>
659634
@ %h(blob_str(&d))
660635
@ </pre>
636
+ manifest_destroy(pW1);
637
+ manifest_destroy(pW2);
661638
style_footer();
662639
}
663640
664641
/*
665642
** WEBPAGE: wcontent
@@ -921,30 +898,26 @@
921898
}
922899
923900
if( strncmp(g.argv[2],"export",n)==0 ){
924901
char const *zPageName; /* Name of the wiki page to export */
925902
char const *zFile; /* Name of the output file (0=stdout) */
926
- int rid; /* Artifact ID of the wiki page */
927
- int i; /* Loop counter */
928
- char *zBody = 0; /* Wiki page content */
929
- Manifest m; /* Parsed wiki page content */
903
+ int rid; /* Artifact ID of the wiki page */
904
+ int i; /* Loop counter */
905
+ char *zBody = 0; /* Wiki page content */
906
+ Manifest *pWiki = 0; /* Parsed wiki page content */
907
+
930908
if( (g.argc!=4) && (g.argc!=5) ){
931909
usage("export PAGENAME ?FILE?");
932910
}
933911
zPageName = g.argv[3];
934912
rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
935913
" WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
936914
" ORDER BY x.mtime DESC LIMIT 1",
937915
zPageName
938916
);
939
- if( rid ){
940
- Blob content;
941
- content_get(rid, &content);
942
- manifest_parse(&m, &content);
943
- if( m.type==CFTYPE_WIKI ){
944
- zBody = m.zWiki;
945
- }
917
+ if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
918
+ zBody = pWiki->zWiki;
946919
}
947920
if( zBody==0 ){
948921
fossil_fatal("wiki page [%s] not found",zPageName);
949922
}
950923
for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){}
@@ -962,12 +935,13 @@
962935
fossil_fatal("wiki export could not open output file for writing.");
963936
}
964937
fprintf(zF,"%.*s\n", i, zBody);
965938
if( doClose ) fclose(zF);
966939
}else{
967
- printf("%.*s\n", i, zBody);
940
+ printf("%.*s\n", i, zBody);
968941
}
942
+ manifest_destroy(pWiki);
969943
return;
970944
}else
971945
if( strncmp(g.argv[2],"commit",n)==0
972946
|| strncmp(g.argv[2],"create",n)==0 ){
973947
char *zPageName;
974948
--- src/wiki.c
+++ src/wiki.c
@@ -127,11 +127,11 @@
127 void wiki_page(void){
128 char *zTag;
129 int rid = 0;
130 int isSandbox;
131 Blob wiki;
132 Manifest m;
133 const char *zPageName;
134 char *zBody = mprintf("%s","<i>Empty Page</i>");
135 Stmt q;
136 int cnt = 0;
137
@@ -179,20 +179,14 @@
179 "SELECT rid FROM tagxref"
180 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
181 " ORDER BY mtime DESC", zTag
182 );
183 free(zTag);
184 memset(&m, 0, sizeof(m));
185 blob_zero(&m.content);
186 if( rid ){
187 Blob content;
188 content_get(rid, &content);
189 manifest_parse(&m, &content);
190 if( m.type==CFTYPE_WIKI && m.zWiki ){
191 while( fossil_isspace(m.zWiki[0]) ) m.zWiki++;
192 if( m.zWiki[0] ) zBody = m.zWiki;
193 }
194 }
195 }
196 if( !g.isHome ){
197 if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
198 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
@@ -249,13 +243,11 @@
249 if( cnt ){
250 @ </ul>
251 }
252 db_finalize(&q);
253
254 if( !isSandbox ){
255 manifest_clear(&m);
256 }
257 /* don'tshow the help cross reference, because we are rendering
258 ** the wiki conten!
259 ** style_footer_cmdref("wiki","export");
260 */
261 style_footer();
@@ -268,11 +260,11 @@
268 void wikiedit_page(void){
269 char *zTag;
270 int rid = 0;
271 int isSandbox;
272 Blob wiki;
273 Manifest m;
274 const char *zPageName;
275 char *zHtmlPageName;
276 int n;
277 const char *z;
278 char *zBody = (char*)P("w");
@@ -302,19 +294,12 @@
302 free(zTag);
303 if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
304 login_needed();
305 return;
306 }
307 memset(&m, 0, sizeof(m));
308 blob_zero(&m.content);
309 if( rid && zBody==0 ){
310 Blob content;
311 content_get(rid, &content);
312 manifest_parse(&m, &content);
313 if( m.type==CFTYPE_WIKI ){
314 zBody = m.zWiki;
315 }
316 }
317 }
318 if( P("submit")!=0 && zBody!=0 ){
319 char *zDate;
320 Blob cksum;
@@ -381,13 +366,11 @@
381 @ <br />
382 @ <input type="submit" name="preview" value="Preview Your Changes" />
383 @ <input type="submit" name="submit" value="Apply These Changes" />
384 @ <input type="submit" name="cancel" value="Cancel" />
385 @ </div></form>
386 if( !isSandbox ){
387 manifest_clear(&m);
388 }
389 style_footer_cmdref("wiki"," / <a href=\"wiki_rules\">wiki format</a>");
390 }
391
392 /*
393 ** WEBPAGE: wikinew
@@ -481,27 +464,25 @@
481 if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
482 char *zDate;
483 Blob cksum;
484 int nrid;
485 Blob body;
486 Blob content;
487 Blob wiki;
488 Manifest m;
489
490 blob_zero(&body);
491 if( isSandbox ){
492 blob_appendf(&body, db_get("sandbox",""));
493 appendRemark(&body);
494 db_set("sandbox", blob_str(&body), 0);
495 }else{
496 login_verify_csrf_secret();
497 content_get(rid, &content);
498 manifest_parse(&m, &content);
499 if( m.type==CFTYPE_WIKI ){
500 blob_append(&body, m.zWiki, -1);
501 }
502 manifest_clear(&m);
503 blob_zero(&wiki);
504 db_begin_transaction();
505 zDate = db_text(0, "SELECT datetime('now')");
506 zDate[10] = 'T';
507 blob_appendf(&wiki, "D %s\n", zDate);
@@ -616,12 +597,11 @@
616 */
617 void wdiff_page(void){
618 char *zTitle;
619 int rid1, rid2;
620 const char *zPageName;
621 Blob content1, content2;
622 Manifest m1, m2;
623 Blob w1, w2, d;
624
625 login_check_credentials();
626 rid1 = atoi(PD("a","0"));
627 if( !g.okHistory ){ login_needed(); return; }
@@ -639,27 +619,24 @@
639 " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
640 " ORDER BY event.mtime DESC LIMIT 1",
641 zPageName, rid1
642 );
643 }
644 content_get(rid1, &content1);
645 manifest_parse(&m1, &content1);
646 if( m1.type!=CFTYPE_WIKI ) fossil_redirect_home();
647 blob_init(&w1, m1.zWiki, -1);
648 blob_zero(&w2);
649 if( rid2 ){
650 content_get(rid2, &content2);
651 manifest_parse(&m2, &content2);
652 if( m2.type==CFTYPE_WIKI ){
653 blob_init(&w2, m2.zWiki, -1);
654 }
655 }
656 blob_zero(&d);
657 text_diff(&w2, &w1, &d, 5, 1);
658 @ <pre>
659 @ %h(blob_str(&d))
660 @ </pre>
 
 
661 style_footer();
662 }
663
664 /*
665 ** WEBPAGE: wcontent
@@ -921,30 +898,26 @@
921 }
922
923 if( strncmp(g.argv[2],"export",n)==0 ){
924 char const *zPageName; /* Name of the wiki page to export */
925 char const *zFile; /* Name of the output file (0=stdout) */
926 int rid; /* Artifact ID of the wiki page */
927 int i; /* Loop counter */
928 char *zBody = 0; /* Wiki page content */
929 Manifest m; /* Parsed wiki page content */
 
930 if( (g.argc!=4) && (g.argc!=5) ){
931 usage("export PAGENAME ?FILE?");
932 }
933 zPageName = g.argv[3];
934 rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
935 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
936 " ORDER BY x.mtime DESC LIMIT 1",
937 zPageName
938 );
939 if( rid ){
940 Blob content;
941 content_get(rid, &content);
942 manifest_parse(&m, &content);
943 if( m.type==CFTYPE_WIKI ){
944 zBody = m.zWiki;
945 }
946 }
947 if( zBody==0 ){
948 fossil_fatal("wiki page [%s] not found",zPageName);
949 }
950 for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){}
@@ -962,12 +935,13 @@
962 fossil_fatal("wiki export could not open output file for writing.");
963 }
964 fprintf(zF,"%.*s\n", i, zBody);
965 if( doClose ) fclose(zF);
966 }else{
967 printf("%.*s\n", i, zBody);
968 }
 
969 return;
970 }else
971 if( strncmp(g.argv[2],"commit",n)==0
972 || strncmp(g.argv[2],"create",n)==0 ){
973 char *zPageName;
974
--- src/wiki.c
+++ src/wiki.c
@@ -127,11 +127,11 @@
127 void wiki_page(void){
128 char *zTag;
129 int rid = 0;
130 int isSandbox;
131 Blob wiki;
132 Manifest *pWiki = 0;
133 const char *zPageName;
134 char *zBody = mprintf("%s","<i>Empty Page</i>");
135 Stmt q;
136 int cnt = 0;
137
@@ -179,20 +179,14 @@
179 "SELECT rid FROM tagxref"
180 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
181 " ORDER BY mtime DESC", zTag
182 );
183 free(zTag);
184 pWiki = manifest_get(rid, CFTYPE_WIKI);
185 if( pWiki ){
186 while( fossil_isspace(pWiki->zWiki[0]) ) pWiki->zWiki++;
187 if( pWiki->zWiki[0] ) zBody = pWiki->zWiki;
 
 
 
 
 
 
188 }
189 }
190 if( !g.isHome ){
191 if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
192 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
@@ -249,13 +243,11 @@
243 if( cnt ){
244 @ </ul>
245 }
246 db_finalize(&q);
247
248 manifest_destroy(pWiki);
 
 
249 /* don'tshow the help cross reference, because we are rendering
250 ** the wiki conten!
251 ** style_footer_cmdref("wiki","export");
252 */
253 style_footer();
@@ -268,11 +260,11 @@
260 void wikiedit_page(void){
261 char *zTag;
262 int rid = 0;
263 int isSandbox;
264 Blob wiki;
265 Manifest *pWiki = 0;
266 const char *zPageName;
267 char *zHtmlPageName;
268 int n;
269 const char *z;
270 char *zBody = (char*)P("w");
@@ -302,19 +294,12 @@
294 free(zTag);
295 if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
296 login_needed();
297 return;
298 }
299 if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
300 zBody = pWiki->zWiki;
 
 
 
 
 
 
 
301 }
302 }
303 if( P("submit")!=0 && zBody!=0 ){
304 char *zDate;
305 Blob cksum;
@@ -381,13 +366,11 @@
366 @ <br />
367 @ <input type="submit" name="preview" value="Preview Your Changes" />
368 @ <input type="submit" name="submit" value="Apply These Changes" />
369 @ <input type="submit" name="cancel" value="Cancel" />
370 @ </div></form>
371 manifest_destroy(pWiki);
 
 
372 style_footer_cmdref("wiki"," / <a href=\"wiki_rules\">wiki format</a>");
373 }
374
375 /*
376 ** WEBPAGE: wikinew
@@ -481,27 +464,25 @@
464 if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
465 char *zDate;
466 Blob cksum;
467 int nrid;
468 Blob body;
 
469 Blob wiki;
470 Manifest *pWiki = 0;
471
472 blob_zero(&body);
473 if( isSandbox ){
474 blob_appendf(&body, db_get("sandbox",""));
475 appendRemark(&body);
476 db_set("sandbox", blob_str(&body), 0);
477 }else{
478 login_verify_csrf_secret();
479 pWiki = manifest_get(rid, CFTYPE_WIKI);
480 if( pWiki ){
481 blob_append(&body, pWiki->zWiki, -1);
482 manifest_destroy(pWiki);
483 }
 
484 blob_zero(&wiki);
485 db_begin_transaction();
486 zDate = db_text(0, "SELECT datetime('now')");
487 zDate[10] = 'T';
488 blob_appendf(&wiki, "D %s\n", zDate);
@@ -616,12 +597,11 @@
597 */
598 void wdiff_page(void){
599 char *zTitle;
600 int rid1, rid2;
601 const char *zPageName;
602 Manifest *pW1, *pW2 = 0;
 
603 Blob w1, w2, d;
604
605 login_check_credentials();
606 rid1 = atoi(PD("a","0"));
607 if( !g.okHistory ){ login_needed(); return; }
@@ -639,27 +619,24 @@
619 " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
620 " ORDER BY event.mtime DESC LIMIT 1",
621 zPageName, rid1
622 );
623 }
624 pW1 = manifest_get(rid1, CFTYPE_WIKI);
625 if( pW1==0 ) fossil_redirect_home();
626 blob_init(&w1, pW1->zWiki, -1);
 
627 blob_zero(&w2);
628 if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
629 blob_init(&w2, pW2->zWiki, -1);
 
 
 
 
630 }
631 blob_zero(&d);
632 text_diff(&w2, &w1, &d, 5, 1);
633 @ <pre>
634 @ %h(blob_str(&d))
635 @ </pre>
636 manifest_destroy(pW1);
637 manifest_destroy(pW2);
638 style_footer();
639 }
640
641 /*
642 ** WEBPAGE: wcontent
@@ -921,30 +898,26 @@
898 }
899
900 if( strncmp(g.argv[2],"export",n)==0 ){
901 char const *zPageName; /* Name of the wiki page to export */
902 char const *zFile; /* Name of the output file (0=stdout) */
903 int rid; /* Artifact ID of the wiki page */
904 int i; /* Loop counter */
905 char *zBody = 0; /* Wiki page content */
906 Manifest *pWiki = 0; /* Parsed wiki page content */
907
908 if( (g.argc!=4) && (g.argc!=5) ){
909 usage("export PAGENAME ?FILE?");
910 }
911 zPageName = g.argv[3];
912 rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
913 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
914 " ORDER BY x.mtime DESC LIMIT 1",
915 zPageName
916 );
917 if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
918 zBody = pWiki->zWiki;
 
 
 
 
 
919 }
920 if( zBody==0 ){
921 fossil_fatal("wiki page [%s] not found",zPageName);
922 }
923 for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){}
@@ -962,12 +935,13 @@
935 fossil_fatal("wiki export could not open output file for writing.");
936 }
937 fprintf(zF,"%.*s\n", i, zBody);
938 if( doClose ) fclose(zF);
939 }else{
940 printf("%.*s\n", i, zBody);
941 }
942 manifest_destroy(pWiki);
943 return;
944 }else
945 if( strncmp(g.argv[2],"commit",n)==0
946 || strncmp(g.argv[2],"create",n)==0 ){
947 char *zPageName;
948
+25 -24
--- src/zip.c
+++ src/zip.c
@@ -313,64 +313,65 @@
313313
** politely expands into a subdir instead of filling your current dir
314314
** with source files. For example, pass a UUID or "ProjectName".
315315
**
316316
*/
317317
void zip_of_baseline(int rid, Blob *pZip, const char *zDir){
318
- int i;
319
- Blob mfile, file, hash;
320
- Manifest m;
318
+ Blob mfile, hash, file;
319
+ Manifest *pManifest;
320
+ ManifestFile *pFile;
321321
Blob filename;
322322
int nPrefix;
323323
324324
content_get(rid, &mfile);
325325
if( blob_size(&mfile)==0 ){
326326
blob_zero(pZip);
327327
return;
328328
}
329
- blob_zero(&file);
330329
blob_zero(&hash);
331
- blob_copy(&file, &mfile);
332330
blob_zero(&filename);
333331
zip_open();
334332
335333
if( zDir && zDir[0] ){
336334
blob_appendf(&filename, "%s/", zDir);
337335
}
338336
nPrefix = blob_size(&filename);
339337
340
- if( manifest_parse(&m, &mfile) ){
338
+ pManifest = manifest_get(rid, CFTYPE_MANIFEST);
339
+ if( pManifest ){
341340
char *zName;
342
- zip_set_timedate(m.rDate);
343
- blob_append(&filename, "manifest", -1);
344
- zName = blob_str(&filename);
345
- zip_add_folders(zName);
346
- zip_add_file(zName, &file);
347
- sha1sum_blob(&file, &hash);
348
- blob_reset(&file);
349
- blob_append(&hash, "\n", 1);
350
- blob_resize(&filename, nPrefix);
351
- blob_append(&filename, "manifest.uuid", -1);
352
- zName = blob_str(&filename);
353
- zip_add_file(zName, &hash);
354
- blob_reset(&hash);
355
- for(i=0; i<m.nFile; i++){
356
- int fid = uuid_to_rid(m.aFile[i].zUuid, 0);
341
+ zip_set_timedate(pManifest->rDate);
342
+ if( db_get_boolean("manifest", 0) ){
343
+ blob_append(&filename, "manifest", -1);
344
+ zName = blob_str(&filename);
345
+ zip_add_folders(zName);
346
+ zip_add_file(zName, &mfile);
347
+ sha1sum_blob(&mfile, &hash);
348
+ blob_reset(&mfile);
349
+ blob_append(&hash, "\n", 1);
350
+ blob_resize(&filename, nPrefix);
351
+ blob_append(&filename, "manifest.uuid", -1);
352
+ zName = blob_str(&filename);
353
+ zip_add_file(zName, &hash);
354
+ blob_reset(&hash);
355
+ }
356
+ manifest_file_rewind(pManifest);
357
+ while( (pFile = manifest_file_next(pManifest,0))!=0 ){
358
+ int fid = uuid_to_rid(pFile->zUuid, 0);
357359
if( fid ){
358360
content_get(fid, &file);
359361
blob_resize(&filename, nPrefix);
360
- blob_append(&filename, m.aFile[i].zName, -1);
362
+ blob_append(&filename, pFile->zName, -1);
361363
zName = blob_str(&filename);
362364
zip_add_folders(zName);
363365
zip_add_file(zName, &file);
364366
blob_reset(&file);
365367
}
366368
}
367
- manifest_clear(&m);
368369
}else{
369370
blob_reset(&mfile);
370
- blob_reset(&file);
371371
}
372
+ manifest_destroy(pManifest);
372373
blob_reset(&filename);
373374
zip_close(pZip);
374375
}
375376
376377
/*
377378
--- src/zip.c
+++ src/zip.c
@@ -313,64 +313,65 @@
313 ** politely expands into a subdir instead of filling your current dir
314 ** with source files. For example, pass a UUID or "ProjectName".
315 **
316 */
317 void zip_of_baseline(int rid, Blob *pZip, const char *zDir){
318 int i;
319 Blob mfile, file, hash;
320 Manifest m;
321 Blob filename;
322 int nPrefix;
323
324 content_get(rid, &mfile);
325 if( blob_size(&mfile)==0 ){
326 blob_zero(pZip);
327 return;
328 }
329 blob_zero(&file);
330 blob_zero(&hash);
331 blob_copy(&file, &mfile);
332 blob_zero(&filename);
333 zip_open();
334
335 if( zDir && zDir[0] ){
336 blob_appendf(&filename, "%s/", zDir);
337 }
338 nPrefix = blob_size(&filename);
339
340 if( manifest_parse(&m, &mfile) ){
 
341 char *zName;
342 zip_set_timedate(m.rDate);
343 blob_append(&filename, "manifest", -1);
344 zName = blob_str(&filename);
345 zip_add_folders(zName);
346 zip_add_file(zName, &file);
347 sha1sum_blob(&file, &hash);
348 blob_reset(&file);
349 blob_append(&hash, "\n", 1);
350 blob_resize(&filename, nPrefix);
351 blob_append(&filename, "manifest.uuid", -1);
352 zName = blob_str(&filename);
353 zip_add_file(zName, &hash);
354 blob_reset(&hash);
355 for(i=0; i<m.nFile; i++){
356 int fid = uuid_to_rid(m.aFile[i].zUuid, 0);
 
 
 
357 if( fid ){
358 content_get(fid, &file);
359 blob_resize(&filename, nPrefix);
360 blob_append(&filename, m.aFile[i].zName, -1);
361 zName = blob_str(&filename);
362 zip_add_folders(zName);
363 zip_add_file(zName, &file);
364 blob_reset(&file);
365 }
366 }
367 manifest_clear(&m);
368 }else{
369 blob_reset(&mfile);
370 blob_reset(&file);
371 }
 
372 blob_reset(&filename);
373 zip_close(pZip);
374 }
375
376 /*
377
--- src/zip.c
+++ src/zip.c
@@ -313,64 +313,65 @@
313 ** politely expands into a subdir instead of filling your current dir
314 ** with source files. For example, pass a UUID or "ProjectName".
315 **
316 */
317 void zip_of_baseline(int rid, Blob *pZip, const char *zDir){
318 Blob mfile, hash, file;
319 Manifest *pManifest;
320 ManifestFile *pFile;
321 Blob filename;
322 int nPrefix;
323
324 content_get(rid, &mfile);
325 if( blob_size(&mfile)==0 ){
326 blob_zero(pZip);
327 return;
328 }
 
329 blob_zero(&hash);
 
330 blob_zero(&filename);
331 zip_open();
332
333 if( zDir && zDir[0] ){
334 blob_appendf(&filename, "%s/", zDir);
335 }
336 nPrefix = blob_size(&filename);
337
338 pManifest = manifest_get(rid, CFTYPE_MANIFEST);
339 if( pManifest ){
340 char *zName;
341 zip_set_timedate(pManifest->rDate);
342 if( db_get_boolean("manifest", 0) ){
343 blob_append(&filename, "manifest", -1);
344 zName = blob_str(&filename);
345 zip_add_folders(zName);
346 zip_add_file(zName, &mfile);
347 sha1sum_blob(&mfile, &hash);
348 blob_reset(&mfile);
349 blob_append(&hash, "\n", 1);
350 blob_resize(&filename, nPrefix);
351 blob_append(&filename, "manifest.uuid", -1);
352 zName = blob_str(&filename);
353 zip_add_file(zName, &hash);
354 blob_reset(&hash);
355 }
356 manifest_file_rewind(pManifest);
357 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
358 int fid = uuid_to_rid(pFile->zUuid, 0);
359 if( fid ){
360 content_get(fid, &file);
361 blob_resize(&filename, nPrefix);
362 blob_append(&filename, pFile->zName, -1);
363 zName = blob_str(&filename);
364 zip_add_folders(zName);
365 zip_add_file(zName, &file);
366 blob_reset(&file);
367 }
368 }
 
369 }else{
370 blob_reset(&mfile);
 
371 }
372 manifest_destroy(pManifest);
373 blob_reset(&filename);
374 zip_close(pZip);
375 }
376
377 /*
378
+25 -24
--- src/zip.c
+++ src/zip.c
@@ -313,64 +313,65 @@
313313
** politely expands into a subdir instead of filling your current dir
314314
** with source files. For example, pass a UUID or "ProjectName".
315315
**
316316
*/
317317
void zip_of_baseline(int rid, Blob *pZip, const char *zDir){
318
- int i;
319
- Blob mfile, file, hash;
320
- Manifest m;
318
+ Blob mfile, hash, file;
319
+ Manifest *pManifest;
320
+ ManifestFile *pFile;
321321
Blob filename;
322322
int nPrefix;
323323
324324
content_get(rid, &mfile);
325325
if( blob_size(&mfile)==0 ){
326326
blob_zero(pZip);
327327
return;
328328
}
329
- blob_zero(&file);
330329
blob_zero(&hash);
331
- blob_copy(&file, &mfile);
332330
blob_zero(&filename);
333331
zip_open();
334332
335333
if( zDir && zDir[0] ){
336334
blob_appendf(&filename, "%s/", zDir);
337335
}
338336
nPrefix = blob_size(&filename);
339337
340
- if( manifest_parse(&m, &mfile) ){
338
+ pManifest = manifest_get(rid, CFTYPE_MANIFEST);
339
+ if( pManifest ){
341340
char *zName;
342
- zip_set_timedate(m.rDate);
343
- blob_append(&filename, "manifest", -1);
344
- zName = blob_str(&filename);
345
- zip_add_folders(zName);
346
- zip_add_file(zName, &file);
347
- sha1sum_blob(&file, &hash);
348
- blob_reset(&file);
349
- blob_append(&hash, "\n", 1);
350
- blob_resize(&filename, nPrefix);
351
- blob_append(&filename, "manifest.uuid", -1);
352
- zName = blob_str(&filename);
353
- zip_add_file(zName, &hash);
354
- blob_reset(&hash);
355
- for(i=0; i<m.nFile; i++){
356
- int fid = uuid_to_rid(m.aFile[i].zUuid, 0);
341
+ zip_set_timedate(pManifest->rDate);
342
+ if( db_get_boolean("manifest", 0) ){
343
+ blob_append(&filename, "manifest", -1);
344
+ zName = blob_str(&filename);
345
+ zip_add_folders(zName);
346
+ zip_add_file(zName, &mfile);
347
+ sha1sum_blob(&mfile, &hash);
348
+ blob_reset(&mfile);
349
+ blob_append(&hash, "\n", 1);
350
+ blob_resize(&filename, nPrefix);
351
+ blob_append(&filename, "manifest.uuid", -1);
352
+ zName = blob_str(&filename);
353
+ zip_add_file(zName, &hash);
354
+ blob_reset(&hash);
355
+ }
356
+ manifest_file_rewind(pManifest);
357
+ while( (pFile = manifest_file_next(pManifest,0))!=0 ){
358
+ int fid = uuid_to_rid(pFile->zUuid, 0);
357359
if( fid ){
358360
content_get(fid, &file);
359361
blob_resize(&filename, nPrefix);
360
- blob_append(&filename, m.aFile[i].zName, -1);
362
+ blob_append(&filename, pFile->zName, -1);
361363
zName = blob_str(&filename);
362364
zip_add_folders(zName);
363365
zip_add_file(zName, &file);
364366
blob_reset(&file);
365367
}
366368
}
367
- manifest_clear(&m);
368369
}else{
369370
blob_reset(&mfile);
370
- blob_reset(&file);
371371
}
372
+ manifest_destroy(pManifest);
372373
blob_reset(&filename);
373374
zip_close(pZip);
374375
}
375376
376377
/*
377378
--- src/zip.c
+++ src/zip.c
@@ -313,64 +313,65 @@
313 ** politely expands into a subdir instead of filling your current dir
314 ** with source files. For example, pass a UUID or "ProjectName".
315 **
316 */
317 void zip_of_baseline(int rid, Blob *pZip, const char *zDir){
318 int i;
319 Blob mfile, file, hash;
320 Manifest m;
321 Blob filename;
322 int nPrefix;
323
324 content_get(rid, &mfile);
325 if( blob_size(&mfile)==0 ){
326 blob_zero(pZip);
327 return;
328 }
329 blob_zero(&file);
330 blob_zero(&hash);
331 blob_copy(&file, &mfile);
332 blob_zero(&filename);
333 zip_open();
334
335 if( zDir && zDir[0] ){
336 blob_appendf(&filename, "%s/", zDir);
337 }
338 nPrefix = blob_size(&filename);
339
340 if( manifest_parse(&m, &mfile) ){
 
341 char *zName;
342 zip_set_timedate(m.rDate);
343 blob_append(&filename, "manifest", -1);
344 zName = blob_str(&filename);
345 zip_add_folders(zName);
346 zip_add_file(zName, &file);
347 sha1sum_blob(&file, &hash);
348 blob_reset(&file);
349 blob_append(&hash, "\n", 1);
350 blob_resize(&filename, nPrefix);
351 blob_append(&filename, "manifest.uuid", -1);
352 zName = blob_str(&filename);
353 zip_add_file(zName, &hash);
354 blob_reset(&hash);
355 for(i=0; i<m.nFile; i++){
356 int fid = uuid_to_rid(m.aFile[i].zUuid, 0);
 
 
 
357 if( fid ){
358 content_get(fid, &file);
359 blob_resize(&filename, nPrefix);
360 blob_append(&filename, m.aFile[i].zName, -1);
361 zName = blob_str(&filename);
362 zip_add_folders(zName);
363 zip_add_file(zName, &file);
364 blob_reset(&file);
365 }
366 }
367 manifest_clear(&m);
368 }else{
369 blob_reset(&mfile);
370 blob_reset(&file);
371 }
 
372 blob_reset(&filename);
373 zip_close(pZip);
374 }
375
376 /*
377
--- src/zip.c
+++ src/zip.c
@@ -313,64 +313,65 @@
313 ** politely expands into a subdir instead of filling your current dir
314 ** with source files. For example, pass a UUID or "ProjectName".
315 **
316 */
317 void zip_of_baseline(int rid, Blob *pZip, const char *zDir){
318 Blob mfile, hash, file;
319 Manifest *pManifest;
320 ManifestFile *pFile;
321 Blob filename;
322 int nPrefix;
323
324 content_get(rid, &mfile);
325 if( blob_size(&mfile)==0 ){
326 blob_zero(pZip);
327 return;
328 }
 
329 blob_zero(&hash);
 
330 blob_zero(&filename);
331 zip_open();
332
333 if( zDir && zDir[0] ){
334 blob_appendf(&filename, "%s/", zDir);
335 }
336 nPrefix = blob_size(&filename);
337
338 pManifest = manifest_get(rid, CFTYPE_MANIFEST);
339 if( pManifest ){
340 char *zName;
341 zip_set_timedate(pManifest->rDate);
342 if( db_get_boolean("manifest", 0) ){
343 blob_append(&filename, "manifest", -1);
344 zName = blob_str(&filename);
345 zip_add_folders(zName);
346 zip_add_file(zName, &mfile);
347 sha1sum_blob(&mfile, &hash);
348 blob_reset(&mfile);
349 blob_append(&hash, "\n", 1);
350 blob_resize(&filename, nPrefix);
351 blob_append(&filename, "manifest.uuid", -1);
352 zName = blob_str(&filename);
353 zip_add_file(zName, &hash);
354 blob_reset(&hash);
355 }
356 manifest_file_rewind(pManifest);
357 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
358 int fid = uuid_to_rid(pFile->zUuid, 0);
359 if( fid ){
360 content_get(fid, &file);
361 blob_resize(&filename, nPrefix);
362 blob_append(&filename, pFile->zName, -1);
363 zName = blob_str(&filename);
364 zip_add_folders(zName);
365 zip_add_file(zName, &file);
366 blob_reset(&file);
367 }
368 }
 
369 }else{
370 blob_reset(&mfile);
 
371 }
372 manifest_destroy(pManifest);
373 blob_reset(&filename);
374 zip_close(pZip);
375 }
376
377 /*
378
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -95,19 +95,28 @@
9595
may contain no additional text or data beyond what is described here.
9696
9797
Allowed cards in the manifest are as follows:
9898
9999
<blockquote>
100
+<b>B</b> <i>baseline-manifest</i><br>
100101
<b>C</b> <i>checkin-comment</i><br>
101102
<b>D</b> <i>time-and-date-stamp</i><br>
102103
<b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
103104
<b>P</b> <i>SHA1-hash</i>+<br>
104105
<b>R</b> <i>repository-checksum</i><br>
105106
<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br>
106107
<b>U</b> <i>user-login</i><br>
107108
<b>Z</b> <i>manifest-checksum</i>
108109
</blockquote>
110
+
111
+A manifest may optionally have a single B-card. The B-card specifies
112
+another manifest that serves as the "baseline" for this manifest. A
113
+manifest that has a B-card is called a delta-manifest and a manifest
114
+that omits the B-card is a baseline-manifest. The other manifest
115
+identified by the argument of the B-card must be a baseline-manifest.
116
+A baseline-manifest records the complete contents of a checkin.
117
+A delta-manifest records only changes from its baseline.
109118
110119
A manifest must have exactly one C-card. The sole argument to
111120
the C-card is a check-in comment that describes the check-in that
112121
the manifest defines. The check-in comment is text. The following
113122
escape sequences are applied to the text:
@@ -125,26 +134,29 @@
125134
126135
<blockquote>
127136
<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>
128137
</blockquote>
129138
130
-A manifest has zero or more F-cards. Each F-card defines a file
131
-(other than the manifest itself) which is part of the check-in that
132
-the manifest defines. There are two, three, or four arguments.
139
+A manifest has zero or more F-cards. Each F-card identifies a file
140
+that is part of the check-in. There are one, two, three, or four arguments.
133141
The first argument
134142
is the pathname of the file in the check-in relative to the root
135143
of the project file hierarchy. No ".." or "." directories are allowed
136144
within the filename. Space characters are escaped as in C-card
137145
comment text. Backslash characters and newlines are not allowed
138146
within filenames. The directory separator character is a forward
139147
slash (ASCII 0x2F). The second argument to the F-card is the
140148
full 40-character lower-case hexadecimal SHA1 hash of the content
141
-artifact. The optional 3rd argument defines any special access
149
+artifact. The second argument is required for baseline manifests
150
+but is optional for delta manifests. When the second argument to the
151
+F-card is omitted, it means that the file has been deleted relative
152
+to the baseline. The optional 3rd argument defines any special access
142153
permissions associated with the file. The only special code currently
143154
defined is "x" which means that the file is executable. All files are
144155
always readable and writable. This can be expressed by "w" permission
145
-if desired but is optional.
156
+if desired but is optional. The file format might be extended with
157
+new permission letters in the future.
146158
The optional 4th argument is the name of the same file as it existed in
147159
the parent check-in. If the name of the file is unchanged from its
148160
parent, then the 4th argument is omitted.
149161
150162
A manifest has zero or one P-cards. Most manifests have one P-card.
@@ -504,10 +516,20 @@
504516
<td>&nbsp;</td>
505517
<td>&nbsp;</td>
506518
<td align=center><b>X</b></td>
507519
<td>&nbsp;</td>
508520
</tr>
521
+<tr>
522
+<td><b>B</b> <i>baseline</i></td>
523
+<td align=center><b>X</b></td>
524
+<td>&nbsp;</td>
525
+<td>&nbsp;</td>
526
+<td>&nbsp;</td>
527
+<td>&nbsp;</td>
528
+<td>&nbsp;</td>
529
+<td>&nbsp;</td>
530
+</tr>
509531
<tr>
510532
<td><b>C</b> <i>comment-text</i></td>
511533
<td align=center><b>X</b></td>
512534
<td>&nbsp;</td>
513535
<td>&nbsp;</td>
514536
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -95,19 +95,28 @@
95 may contain no additional text or data beyond what is described here.
96
97 Allowed cards in the manifest are as follows:
98
99 <blockquote>
 
100 <b>C</b> <i>checkin-comment</i><br>
101 <b>D</b> <i>time-and-date-stamp</i><br>
102 <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
103 <b>P</b> <i>SHA1-hash</i>+<br>
104 <b>R</b> <i>repository-checksum</i><br>
105 <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br>
106 <b>U</b> <i>user-login</i><br>
107 <b>Z</b> <i>manifest-checksum</i>
108 </blockquote>
 
 
 
 
 
 
 
 
109
110 A manifest must have exactly one C-card. The sole argument to
111 the C-card is a check-in comment that describes the check-in that
112 the manifest defines. The check-in comment is text. The following
113 escape sequences are applied to the text:
@@ -125,26 +134,29 @@
125
126 <blockquote>
127 <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>
128 </blockquote>
129
130 A manifest has zero or more F-cards. Each F-card defines a file
131 (other than the manifest itself) which is part of the check-in that
132 the manifest defines. There are two, three, or four arguments.
133 The first argument
134 is the pathname of the file in the check-in relative to the root
135 of the project file hierarchy. No ".." or "." directories are allowed
136 within the filename. Space characters are escaped as in C-card
137 comment text. Backslash characters and newlines are not allowed
138 within filenames. The directory separator character is a forward
139 slash (ASCII 0x2F). The second argument to the F-card is the
140 full 40-character lower-case hexadecimal SHA1 hash of the content
141 artifact. The optional 3rd argument defines any special access
 
 
 
142 permissions associated with the file. The only special code currently
143 defined is "x" which means that the file is executable. All files are
144 always readable and writable. This can be expressed by "w" permission
145 if desired but is optional.
 
146 The optional 4th argument is the name of the same file as it existed in
147 the parent check-in. If the name of the file is unchanged from its
148 parent, then the 4th argument is omitted.
149
150 A manifest has zero or one P-cards. Most manifests have one P-card.
@@ -504,10 +516,20 @@
504 <td>&nbsp;</td>
505 <td>&nbsp;</td>
506 <td align=center><b>X</b></td>
507 <td>&nbsp;</td>
508 </tr>
 
 
 
 
 
 
 
 
 
 
509 <tr>
510 <td><b>C</b> <i>comment-text</i></td>
511 <td align=center><b>X</b></td>
512 <td>&nbsp;</td>
513 <td>&nbsp;</td>
514
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -95,19 +95,28 @@
95 may contain no additional text or data beyond what is described here.
96
97 Allowed cards in the manifest are as follows:
98
99 <blockquote>
100 <b>B</b> <i>baseline-manifest</i><br>
101 <b>C</b> <i>checkin-comment</i><br>
102 <b>D</b> <i>time-and-date-stamp</i><br>
103 <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
104 <b>P</b> <i>SHA1-hash</i>+<br>
105 <b>R</b> <i>repository-checksum</i><br>
106 <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br>
107 <b>U</b> <i>user-login</i><br>
108 <b>Z</b> <i>manifest-checksum</i>
109 </blockquote>
110
111 A manifest may optionally have a single B-card. The B-card specifies
112 another manifest that serves as the "baseline" for this manifest. A
113 manifest that has a B-card is called a delta-manifest and a manifest
114 that omits the B-card is a baseline-manifest. The other manifest
115 identified by the argument of the B-card must be a baseline-manifest.
116 A baseline-manifest records the complete contents of a checkin.
117 A delta-manifest records only changes from its baseline.
118
119 A manifest must have exactly one C-card. The sole argument to
120 the C-card is a check-in comment that describes the check-in that
121 the manifest defines. The check-in comment is text. The following
122 escape sequences are applied to the text:
@@ -125,26 +134,29 @@
134
135 <blockquote>
136 <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>
137 </blockquote>
138
139 A manifest has zero or more F-cards. Each F-card identifies a file
140 that is part of the check-in. There are one, two, three, or four arguments.
 
141 The first argument
142 is the pathname of the file in the check-in relative to the root
143 of the project file hierarchy. No ".." or "." directories are allowed
144 within the filename. Space characters are escaped as in C-card
145 comment text. Backslash characters and newlines are not allowed
146 within filenames. The directory separator character is a forward
147 slash (ASCII 0x2F). The second argument to the F-card is the
148 full 40-character lower-case hexadecimal SHA1 hash of the content
149 artifact. The second argument is required for baseline manifests
150 but is optional for delta manifests. When the second argument to the
151 F-card is omitted, it means that the file has been deleted relative
152 to the baseline. The optional 3rd argument defines any special access
153 permissions associated with the file. The only special code currently
154 defined is "x" which means that the file is executable. All files are
155 always readable and writable. This can be expressed by "w" permission
156 if desired but is optional. The file format might be extended with
157 new permission letters in the future.
158 The optional 4th argument is the name of the same file as it existed in
159 the parent check-in. If the name of the file is unchanged from its
160 parent, then the 4th argument is omitted.
161
162 A manifest has zero or one P-cards. Most manifests have one P-card.
@@ -504,10 +516,20 @@
516 <td>&nbsp;</td>
517 <td>&nbsp;</td>
518 <td align=center><b>X</b></td>
519 <td>&nbsp;</td>
520 </tr>
521 <tr>
522 <td><b>B</b> <i>baseline</i></td>
523 <td align=center><b>X</b></td>
524 <td>&nbsp;</td>
525 <td>&nbsp;</td>
526 <td>&nbsp;</td>
527 <td>&nbsp;</td>
528 <td>&nbsp;</td>
529 <td>&nbsp;</td>
530 </tr>
531 <tr>
532 <td><b>C</b> <i>comment-text</i></td>
533 <td align=center><b>X</b></td>
534 <td>&nbsp;</td>
535 <td>&nbsp;</td>
536

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button