Fossil SCM

Merge in all of the latest clear-title changes from the trunk.

drh 2010-03-18 14:40 clear-title merge
Commit 7c2577bd637e91b0d3649f75555644cd8b46189d
--- COPYRIGHT-GPL2.txt
+++ COPYRIGHT-GPL2.txt
@@ -1,5 +1,19 @@
1
+Fossil is licensed under the terms of the GPLv2 shown below. In
2
+addition, permission is granted to link Fossil against the OpenSSL
3
+project's "OpenSSL" library (or with modified versions of that
4
+library that use the same license), and distribute the linked
5
+executables. If you modify Fossil, you may extend the exception
6
+described in this paragraph to your modifications, or not, at your
7
+discretion.
8
+
9
+The "clear-title" branch of Fossil is available for more liberal
10
+licenses such as Berkeley-style licenses or the Apache license.
11
+
12
+The original text of GPLv2 follows.
13
+--------------------------------------------------------------------------
14
+
115
GNU GENERAL PUBLIC LICENSE
216
Version 2, June 1991
317
418
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
519
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
620
721
ADDED src/attach.c
--- COPYRIGHT-GPL2.txt
+++ COPYRIGHT-GPL2.txt
@@ -1,5 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
7 DDED src/attach.c
--- COPYRIGHT-GPL2.txt
+++ COPYRIGHT-GPL2.txt
@@ -1,5 +1,19 @@
1 Fossil is licensed under the terms of the GPLv2 shown below. In
2 addition, permission is granted to link Fossil against the OpenSSL
3 project's "OpenSSL" library (or with modified versions of that
4 library that use the same license), and distribute the linked
5 executables. If you modify Fossil, you may extend the exception
6 described in this paragraph to your modifications, or not, at your
7 discretion.
8
9 The "clear-title" branch of Fossil is available for more liberal
10 licenses such as Berkeley-style licenses or the Apache license.
11
12 The original text of GPLv2 follows.
13 --------------------------------------------------------------------------
14
15 GNU GENERAL PUBLIC LICENSE
16 Version 2, June 1991
17
18 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21 DDED src/attach.c
--- a/src/attach.c
+++ b/src/attach.c
@@ -0,0 +1,9 @@
1
+[] Oif(zMime =Name);[] O .10s", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
2
+
3
+
4
+}
5
+S[SSg.zLogin ? g.zLogin : "noboG all[] Oif(zMimes", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
6
+
7
+
8
+}
9
+S[SSg.zLogin ? g.zLogin : "noboG
--- a/src/attach.c
+++ b/src/attach.c
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
--- a/src/attach.c
+++ b/src/attach.c
@@ -0,0 +1,9 @@
1 [] Oif(zMime =Name);[] O .10s", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
2
3
4 }
5 S[SSg.zLogin ? g.zLogin : "noboG all[] Oif(zMimes", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
6
7
8 }
9 S[SSg.zLogin ? g.zLogin : "noboG
+22 -10
--- src/browse.c
+++ src/browse.c
@@ -113,10 +113,11 @@
113113
char *zPrefix;
114114
Stmt q;
115115
const char *zCI = P("ci");
116116
int rid = 0;
117117
Blob content;
118
+ Blob dirname;
118119
Manifest m;
119120
const char *zSubdirLink;
120121
121122
login_check_credentials();
122123
if( !g.okHistory ){ login_needed(); return; }
@@ -133,36 +134,47 @@
133134
zCI = 0;
134135
}
135136
}
136137
137138
/* Compute the title of the page */
139
+ blob_zero(&dirname);
138140
if( zD ){
139
- Blob title;
140
-
141
- blob_zero(&title);
142
- blob_appendf(&title, "Files in directory ");
143
- hyperlinked_path(zD, &title);
144
- @ <h2>%s(blob_str(&title))
145
- blob_reset(&title);
141
+ blob_append(&dirname, "in directory ", -1);
142
+ hyperlinked_path(zD, &dirname);
146143
zPrefix = mprintf("%h/", zD);
147144
}else{
148
- @ <h2>Files in the top-level directory
145
+ blob_append(&dirname, "in the top-level directory", -1);
149146
zPrefix = "";
150147
}
151148
if( zCI ){
152149
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
153150
char zShort[20];
154151
memcpy(zShort, zUuid, 10);
155152
zShort[10] = 0;
156
- @ of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]</h2>
153
+ @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
154
+ @ %s(blob_str(&dirname))</h2>
157155
zSubdirLink = mprintf("%s/dir?ci=%s&name=%T", g.zBaseURL, zUuid, zPrefix);
158156
if( zD ){
159157
style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid);
158
+ style_submenu_element("All", "All", "%s/dir?name=%t", g.zBaseURL, zD);
159
+ }else{
160
+ style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
160161
}
161162
}else{
162
- @ </h2>
163
+ @ <h2>The union of all files from all check-ins
164
+ @ %s(blob_str(&dirname))</h2>
163165
zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
166
+ if( zD ){
167
+ style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
168
+ style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip",
169
+ g.zBaseURL, zD);
170
+ style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk",
171
+ g.zBaseURL,zD);
172
+ }else{
173
+ style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
174
+ style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
175
+ }
164176
}
165177
166178
/* Compute the temporary table "localfiles" containing the names
167179
** of all files and subdirectories in the zD[] directory.
168180
**
169181
--- src/browse.c
+++ src/browse.c
@@ -113,10 +113,11 @@
113 char *zPrefix;
114 Stmt q;
115 const char *zCI = P("ci");
116 int rid = 0;
117 Blob content;
 
118 Manifest m;
119 const char *zSubdirLink;
120
121 login_check_credentials();
122 if( !g.okHistory ){ login_needed(); return; }
@@ -133,36 +134,47 @@
133 zCI = 0;
134 }
135 }
136
137 /* Compute the title of the page */
 
138 if( zD ){
139 Blob title;
140
141 blob_zero(&title);
142 blob_appendf(&title, "Files in directory ");
143 hyperlinked_path(zD, &title);
144 @ <h2>%s(blob_str(&title))
145 blob_reset(&title);
146 zPrefix = mprintf("%h/", zD);
147 }else{
148 @ <h2>Files in the top-level directory
149 zPrefix = "";
150 }
151 if( zCI ){
152 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
153 char zShort[20];
154 memcpy(zShort, zUuid, 10);
155 zShort[10] = 0;
156 @ of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]</h2>
 
157 zSubdirLink = mprintf("%s/dir?ci=%s&name=%T", g.zBaseURL, zUuid, zPrefix);
158 if( zD ){
159 style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid);
 
 
 
160 }
161 }else{
162 @ </h2>
 
163 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
 
 
 
 
 
 
 
 
 
 
164 }
165
166 /* Compute the temporary table "localfiles" containing the names
167 ** of all files and subdirectories in the zD[] directory.
168 **
169
--- src/browse.c
+++ src/browse.c
@@ -113,10 +113,11 @@
113 char *zPrefix;
114 Stmt q;
115 const char *zCI = P("ci");
116 int rid = 0;
117 Blob content;
118 Blob dirname;
119 Manifest m;
120 const char *zSubdirLink;
121
122 login_check_credentials();
123 if( !g.okHistory ){ login_needed(); return; }
@@ -133,36 +134,47 @@
134 zCI = 0;
135 }
136 }
137
138 /* Compute the title of the page */
139 blob_zero(&dirname);
140 if( zD ){
141 blob_append(&dirname, "in directory ", -1);
142 hyperlinked_path(zD, &dirname);
 
 
 
 
 
143 zPrefix = mprintf("%h/", zD);
144 }else{
145 blob_append(&dirname, "in the top-level directory", -1);
146 zPrefix = "";
147 }
148 if( zCI ){
149 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
150 char zShort[20];
151 memcpy(zShort, zUuid, 10);
152 zShort[10] = 0;
153 @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
154 @ %s(blob_str(&dirname))</h2>
155 zSubdirLink = mprintf("%s/dir?ci=%s&name=%T", g.zBaseURL, zUuid, zPrefix);
156 if( zD ){
157 style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid);
158 style_submenu_element("All", "All", "%s/dir?name=%t", g.zBaseURL, zD);
159 }else{
160 style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
161 }
162 }else{
163 @ <h2>The union of all files from all check-ins
164 @ %s(blob_str(&dirname))</h2>
165 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
166 if( zD ){
167 style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
168 style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip",
169 g.zBaseURL, zD);
170 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk",
171 g.zBaseURL,zD);
172 }else{
173 style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
174 style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
175 }
176 }
177
178 /* Compute the temporary table "localfiles" containing the names
179 ** of all files and subdirectories in the zD[] directory.
180 **
181
+73 -4
--- src/checkin.c
+++ src/checkin.c
@@ -193,14 +193,68 @@
193193
}
194194
free(zFullName);
195195
}
196196
db_finalize(&q);
197197
}
198
+
199
+/*
200
+** Construct and return a string which is an SQL expression that will
201
+** be TRUE if value zVal matches any of the GLOB expressions in the list
202
+** zGlobList. For example:
203
+**
204
+** zVal: "x"
205
+** zGlobList: "*.o,*.obj"
206
+**
207
+** Result: "(x GLOB '*.o' OR x GLOB '*.obj')"
208
+**
209
+** Each element of the GLOB list may optionally be enclosed in either '...'
210
+** or "...". This allows commas in the expression. Whitespace at the
211
+** beginning and end of each GLOB pattern is ignored, except when enclosed
212
+** within '...' or "...".
213
+**
214
+** This routine makes no effort to free the memory space it uses.
215
+*/
216
+char *glob_expr(const char *zVal, const char *zGlobList){
217
+ Blob expr;
218
+ char *zSep = "(";
219
+ int nTerm = 0;
220
+ int i;
221
+ int cTerm;
222
+
223
+ if( zGlobList==0 || zGlobList[0]==0 ) return "0";
224
+ blob_zero(&expr);
225
+ while( zGlobList[0] ){
226
+ while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
227
+ if( zGlobList[0]==0 ) break;
228
+ if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
229
+ cTerm = zGlobList[0];
230
+ zGlobList++;
231
+ }else{
232
+ cTerm = ',';
233
+ }
234
+ for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){}
235
+ if( cTerm==',' ){
236
+ while( i>0 && isspace(zGlobList[i-1]) ){ i--; }
237
+ }
238
+ blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList);
239
+ zSep = " OR ";
240
+ if( cTerm!=',' && zGlobList[i] ) i++;
241
+ zGlobList += i;
242
+ if( zGlobList[0] ) zGlobList++;
243
+ nTerm++;
244
+ }
245
+ if( nTerm ){
246
+ blob_appendf(&expr, ")");
247
+ return blob_str(&expr);
248
+ }else{
249
+ return "0";
250
+ }
251
+}
198252
199253
/*
200254
** COMMAND: extras
201
-** Usage: %fossil extras ?--dotfiles?
255
+** Usage: %fossil extras ?--dotfiles? ?--ignore GLOBPATTERN?
202256
**
203257
** Print a list of all files in the source tree that are not part of
204258
** the current checkout. See also the "clean" command.
205259
**
206260
** Files and subdirectories whose names begin with "." are normally
@@ -209,20 +263,28 @@
209263
void extra_cmd(void){
210264
Blob path;
211265
Blob repo;
212266
Stmt q;
213267
int n;
268
+ const char *zIgnoreFlag = find_option("ignore",0,1);
214269
int allFlag = find_option("dotfiles",0,0)!=0;
270
+
215271
db_must_be_within_tree();
216272
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
217273
n = strlen(g.zLocalRoot);
218274
blob_init(&path, g.zLocalRoot, n-1);
275
+ if( zIgnoreFlag==0 ){
276
+ zIgnoreFlag = db_get("ignore-glob", 0);
277
+ }
219278
vfile_scan(0, &path, blob_size(&path), allFlag);
220279
db_prepare(&q,
221280
"SELECT x FROM sfile"
222281
" WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')"
223
- " ORDER BY 1");
282
+ " AND NOT %s"
283
+ " ORDER BY 1",
284
+ glob_expr("x", zIgnoreFlag)
285
+ );
224286
if( file_tree_name(g.zRepositoryName, &repo, 0) ){
225287
db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
226288
}
227289
while( db_step(&q)==SQLITE_ROW ){
228290
printf("%s\n", db_column_text(&q, 0));
@@ -683,11 +745,11 @@
683745
zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
684746
zDate[10] = 'T';
685747
blob_appendf(&manifest, "D %s\n", zDate);
686748
zDate[10] = ' ';
687749
db_prepare(&q,
688
- "SELECT pathname, uuid, origname, blob.rid"
750
+ "SELECT pathname, uuid, origname, blob.rid, isexe"
689751
" FROM vfile JOIN blob ON vfile.mrid=blob.rid"
690752
" WHERE NOT deleted AND vfile.vid=%d"
691753
" ORDER BY 1", vid);
692754
blob_zero(&filename);
693755
blob_appendf(&filename, "%s", g.zLocalRoot);
@@ -695,13 +757,20 @@
695757
while( db_step(&q)==SQLITE_ROW ){
696758
const char *zName = db_column_text(&q, 0);
697759
const char *zUuid = db_column_text(&q, 1);
698760
const char *zOrig = db_column_text(&q, 2);
699761
int frid = db_column_int(&q, 3);
762
+ int isexe = db_column_int(&q, 4);
700763
const char *zPerm;
701764
blob_append(&filename, zName, -1);
702
- if( file_isexe(blob_str(&filename)) ){
765
+#ifndef __MINGW32__
766
+ /* For unix, extract the "executable" permission bit directly from
767
+ ** the filesystem. On windows, the "executable" bit is retained
768
+ ** unchanged from the original. */
769
+ isexe = file_isexe(blob_str(&filename));
770
+#endif
771
+ if( isexe ){
703772
zPerm = " x";
704773
}else{
705774
zPerm = "";
706775
}
707776
blob_resize(&filename, nBasename);
708777
--- src/checkin.c
+++ src/checkin.c
@@ -193,14 +193,68 @@
193 }
194 free(zFullName);
195 }
196 db_finalize(&q);
197 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
199 /*
200 ** COMMAND: extras
201 ** Usage: %fossil extras ?--dotfiles?
202 **
203 ** Print a list of all files in the source tree that are not part of
204 ** the current checkout. See also the "clean" command.
205 **
206 ** Files and subdirectories whose names begin with "." are normally
@@ -209,20 +263,28 @@
209 void extra_cmd(void){
210 Blob path;
211 Blob repo;
212 Stmt q;
213 int n;
 
214 int allFlag = find_option("dotfiles",0,0)!=0;
 
215 db_must_be_within_tree();
216 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
217 n = strlen(g.zLocalRoot);
218 blob_init(&path, g.zLocalRoot, n-1);
 
 
 
219 vfile_scan(0, &path, blob_size(&path), allFlag);
220 db_prepare(&q,
221 "SELECT x FROM sfile"
222 " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')"
223 " ORDER BY 1");
 
 
 
224 if( file_tree_name(g.zRepositoryName, &repo, 0) ){
225 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
226 }
227 while( db_step(&q)==SQLITE_ROW ){
228 printf("%s\n", db_column_text(&q, 0));
@@ -683,11 +745,11 @@
683 zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
684 zDate[10] = 'T';
685 blob_appendf(&manifest, "D %s\n", zDate);
686 zDate[10] = ' ';
687 db_prepare(&q,
688 "SELECT pathname, uuid, origname, blob.rid"
689 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
690 " WHERE NOT deleted AND vfile.vid=%d"
691 " ORDER BY 1", vid);
692 blob_zero(&filename);
693 blob_appendf(&filename, "%s", g.zLocalRoot);
@@ -695,13 +757,20 @@
695 while( db_step(&q)==SQLITE_ROW ){
696 const char *zName = db_column_text(&q, 0);
697 const char *zUuid = db_column_text(&q, 1);
698 const char *zOrig = db_column_text(&q, 2);
699 int frid = db_column_int(&q, 3);
 
700 const char *zPerm;
701 blob_append(&filename, zName, -1);
702 if( file_isexe(blob_str(&filename)) ){
 
 
 
 
 
 
703 zPerm = " x";
704 }else{
705 zPerm = "";
706 }
707 blob_resize(&filename, nBasename);
708
--- src/checkin.c
+++ src/checkin.c
@@ -193,14 +193,68 @@
193 }
194 free(zFullName);
195 }
196 db_finalize(&q);
197 }
198
199 /*
200 ** Construct and return a string which is an SQL expression that will
201 ** be TRUE if value zVal matches any of the GLOB expressions in the list
202 ** zGlobList. For example:
203 **
204 ** zVal: "x"
205 ** zGlobList: "*.o,*.obj"
206 **
207 ** Result: "(x GLOB '*.o' OR x GLOB '*.obj')"
208 **
209 ** Each element of the GLOB list may optionally be enclosed in either '...'
210 ** or "...". This allows commas in the expression. Whitespace at the
211 ** beginning and end of each GLOB pattern is ignored, except when enclosed
212 ** within '...' or "...".
213 **
214 ** This routine makes no effort to free the memory space it uses.
215 */
216 char *glob_expr(const char *zVal, const char *zGlobList){
217 Blob expr;
218 char *zSep = "(";
219 int nTerm = 0;
220 int i;
221 int cTerm;
222
223 if( zGlobList==0 || zGlobList[0]==0 ) return "0";
224 blob_zero(&expr);
225 while( zGlobList[0] ){
226 while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
227 if( zGlobList[0]==0 ) break;
228 if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
229 cTerm = zGlobList[0];
230 zGlobList++;
231 }else{
232 cTerm = ',';
233 }
234 for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){}
235 if( cTerm==',' ){
236 while( i>0 && isspace(zGlobList[i-1]) ){ i--; }
237 }
238 blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList);
239 zSep = " OR ";
240 if( cTerm!=',' && zGlobList[i] ) i++;
241 zGlobList += i;
242 if( zGlobList[0] ) zGlobList++;
243 nTerm++;
244 }
245 if( nTerm ){
246 blob_appendf(&expr, ")");
247 return blob_str(&expr);
248 }else{
249 return "0";
250 }
251 }
252
253 /*
254 ** COMMAND: extras
255 ** Usage: %fossil extras ?--dotfiles? ?--ignore GLOBPATTERN?
256 **
257 ** Print a list of all files in the source tree that are not part of
258 ** the current checkout. See also the "clean" command.
259 **
260 ** Files and subdirectories whose names begin with "." are normally
@@ -209,20 +263,28 @@
263 void extra_cmd(void){
264 Blob path;
265 Blob repo;
266 Stmt q;
267 int n;
268 const char *zIgnoreFlag = find_option("ignore",0,1);
269 int allFlag = find_option("dotfiles",0,0)!=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 if( zIgnoreFlag==0 ){
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 ('manifest','manifest.uuid','_FOSSIL_')"
282 " AND NOT %s"
283 " ORDER BY 1",
284 glob_expr("x", zIgnoreFlag)
285 );
286 if( file_tree_name(g.zRepositoryName, &repo, 0) ){
287 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
288 }
289 while( db_step(&q)==SQLITE_ROW ){
290 printf("%s\n", db_column_text(&q, 0));
@@ -683,11 +745,11 @@
745 zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
746 zDate[10] = 'T';
747 blob_appendf(&manifest, "D %s\n", zDate);
748 zDate[10] = ' ';
749 db_prepare(&q,
750 "SELECT pathname, uuid, origname, blob.rid, isexe"
751 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
752 " WHERE NOT deleted AND vfile.vid=%d"
753 " ORDER BY 1", vid);
754 blob_zero(&filename);
755 blob_appendf(&filename, "%s", g.zLocalRoot);
@@ -695,13 +757,20 @@
757 while( db_step(&q)==SQLITE_ROW ){
758 const char *zName = db_column_text(&q, 0);
759 const char *zUuid = db_column_text(&q, 1);
760 const char *zOrig = db_column_text(&q, 2);
761 int frid = db_column_int(&q, 3);
762 int isexe = db_column_int(&q, 4);
763 const char *zPerm;
764 blob_append(&filename, zName, -1);
765 #ifndef __MINGW32__
766 /* For unix, extract the "executable" permission bit directly from
767 ** the filesystem. On windows, the "executable" bit is retained
768 ** unchanged from the original. */
769 isexe = file_isexe(blob_str(&filename));
770 #endif
771 if( isexe ){
772 zPerm = " x";
773 }else{
774 zPerm = "";
775 }
776 blob_resize(&filename, nBasename);
777
+73 -4
--- src/checkin.c
+++ src/checkin.c
@@ -193,14 +193,68 @@
193193
}
194194
free(zFullName);
195195
}
196196
db_finalize(&q);
197197
}
198
+
199
+/*
200
+** Construct and return a string which is an SQL expression that will
201
+** be TRUE if value zVal matches any of the GLOB expressions in the list
202
+** zGlobList. For example:
203
+**
204
+** zVal: "x"
205
+** zGlobList: "*.o,*.obj"
206
+**
207
+** Result: "(x GLOB '*.o' OR x GLOB '*.obj')"
208
+**
209
+** Each element of the GLOB list may optionally be enclosed in either '...'
210
+** or "...". This allows commas in the expression. Whitespace at the
211
+** beginning and end of each GLOB pattern is ignored, except when enclosed
212
+** within '...' or "...".
213
+**
214
+** This routine makes no effort to free the memory space it uses.
215
+*/
216
+char *glob_expr(const char *zVal, const char *zGlobList){
217
+ Blob expr;
218
+ char *zSep = "(";
219
+ int nTerm = 0;
220
+ int i;
221
+ int cTerm;
222
+
223
+ if( zGlobList==0 || zGlobList[0]==0 ) return "0";
224
+ blob_zero(&expr);
225
+ while( zGlobList[0] ){
226
+ while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
227
+ if( zGlobList[0]==0 ) break;
228
+ if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
229
+ cTerm = zGlobList[0];
230
+ zGlobList++;
231
+ }else{
232
+ cTerm = ',';
233
+ }
234
+ for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){}
235
+ if( cTerm==',' ){
236
+ while( i>0 && isspace(zGlobList[i-1]) ){ i--; }
237
+ }
238
+ blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList);
239
+ zSep = " OR ";
240
+ if( cTerm!=',' && zGlobList[i] ) i++;
241
+ zGlobList += i;
242
+ if( zGlobList[0] ) zGlobList++;
243
+ nTerm++;
244
+ }
245
+ if( nTerm ){
246
+ blob_appendf(&expr, ")");
247
+ return blob_str(&expr);
248
+ }else{
249
+ return "0";
250
+ }
251
+}
198252
199253
/*
200254
** COMMAND: extras
201
-** Usage: %fossil extras ?--dotfiles?
255
+** Usage: %fossil extras ?--dotfiles? ?--ignore GLOBPATTERN?
202256
**
203257
** Print a list of all files in the source tree that are not part of
204258
** the current checkout. See also the "clean" command.
205259
**
206260
** Files and subdirectories whose names begin with "." are normally
@@ -209,20 +263,28 @@
209263
void extra_cmd(void){
210264
Blob path;
211265
Blob repo;
212266
Stmt q;
213267
int n;
268
+ const char *zIgnoreFlag = find_option("ignore",0,1);
214269
int allFlag = find_option("dotfiles",0,0)!=0;
270
+
215271
db_must_be_within_tree();
216272
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
217273
n = strlen(g.zLocalRoot);
218274
blob_init(&path, g.zLocalRoot, n-1);
275
+ if( zIgnoreFlag==0 ){
276
+ zIgnoreFlag = db_get("ignore-glob", 0);
277
+ }
219278
vfile_scan(0, &path, blob_size(&path), allFlag);
220279
db_prepare(&q,
221280
"SELECT x FROM sfile"
222281
" WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')"
223
- " ORDER BY 1");
282
+ " AND NOT %s"
283
+ " ORDER BY 1",
284
+ glob_expr("x", zIgnoreFlag)
285
+ );
224286
if( file_tree_name(g.zRepositoryName, &repo, 0) ){
225287
db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
226288
}
227289
while( db_step(&q)==SQLITE_ROW ){
228290
printf("%s\n", db_column_text(&q, 0));
@@ -683,11 +745,11 @@
683745
zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
684746
zDate[10] = 'T';
685747
blob_appendf(&manifest, "D %s\n", zDate);
686748
zDate[10] = ' ';
687749
db_prepare(&q,
688
- "SELECT pathname, uuid, origname, blob.rid"
750
+ "SELECT pathname, uuid, origname, blob.rid, isexe"
689751
" FROM vfile JOIN blob ON vfile.mrid=blob.rid"
690752
" WHERE NOT deleted AND vfile.vid=%d"
691753
" ORDER BY 1", vid);
692754
blob_zero(&filename);
693755
blob_appendf(&filename, "%s", g.zLocalRoot);
@@ -695,13 +757,20 @@
695757
while( db_step(&q)==SQLITE_ROW ){
696758
const char *zName = db_column_text(&q, 0);
697759
const char *zUuid = db_column_text(&q, 1);
698760
const char *zOrig = db_column_text(&q, 2);
699761
int frid = db_column_int(&q, 3);
762
+ int isexe = db_column_int(&q, 4);
700763
const char *zPerm;
701764
blob_append(&filename, zName, -1);
702
- if( file_isexe(blob_str(&filename)) ){
765
+#ifndef __MINGW32__
766
+ /* For unix, extract the "executable" permission bit directly from
767
+ ** the filesystem. On windows, the "executable" bit is retained
768
+ ** unchanged from the original. */
769
+ isexe = file_isexe(blob_str(&filename));
770
+#endif
771
+ if( isexe ){
703772
zPerm = " x";
704773
}else{
705774
zPerm = "";
706775
}
707776
blob_resize(&filename, nBasename);
708777
--- src/checkin.c
+++ src/checkin.c
@@ -193,14 +193,68 @@
193 }
194 free(zFullName);
195 }
196 db_finalize(&q);
197 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
199 /*
200 ** COMMAND: extras
201 ** Usage: %fossil extras ?--dotfiles?
202 **
203 ** Print a list of all files in the source tree that are not part of
204 ** the current checkout. See also the "clean" command.
205 **
206 ** Files and subdirectories whose names begin with "." are normally
@@ -209,20 +263,28 @@
209 void extra_cmd(void){
210 Blob path;
211 Blob repo;
212 Stmt q;
213 int n;
 
214 int allFlag = find_option("dotfiles",0,0)!=0;
 
215 db_must_be_within_tree();
216 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
217 n = strlen(g.zLocalRoot);
218 blob_init(&path, g.zLocalRoot, n-1);
 
 
 
219 vfile_scan(0, &path, blob_size(&path), allFlag);
220 db_prepare(&q,
221 "SELECT x FROM sfile"
222 " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')"
223 " ORDER BY 1");
 
 
 
224 if( file_tree_name(g.zRepositoryName, &repo, 0) ){
225 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
226 }
227 while( db_step(&q)==SQLITE_ROW ){
228 printf("%s\n", db_column_text(&q, 0));
@@ -683,11 +745,11 @@
683 zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
684 zDate[10] = 'T';
685 blob_appendf(&manifest, "D %s\n", zDate);
686 zDate[10] = ' ';
687 db_prepare(&q,
688 "SELECT pathname, uuid, origname, blob.rid"
689 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
690 " WHERE NOT deleted AND vfile.vid=%d"
691 " ORDER BY 1", vid);
692 blob_zero(&filename);
693 blob_appendf(&filename, "%s", g.zLocalRoot);
@@ -695,13 +757,20 @@
695 while( db_step(&q)==SQLITE_ROW ){
696 const char *zName = db_column_text(&q, 0);
697 const char *zUuid = db_column_text(&q, 1);
698 const char *zOrig = db_column_text(&q, 2);
699 int frid = db_column_int(&q, 3);
 
700 const char *zPerm;
701 blob_append(&filename, zName, -1);
702 if( file_isexe(blob_str(&filename)) ){
 
 
 
 
 
 
703 zPerm = " x";
704 }else{
705 zPerm = "";
706 }
707 blob_resize(&filename, nBasename);
708
--- src/checkin.c
+++ src/checkin.c
@@ -193,14 +193,68 @@
193 }
194 free(zFullName);
195 }
196 db_finalize(&q);
197 }
198
199 /*
200 ** Construct and return a string which is an SQL expression that will
201 ** be TRUE if value zVal matches any of the GLOB expressions in the list
202 ** zGlobList. For example:
203 **
204 ** zVal: "x"
205 ** zGlobList: "*.o,*.obj"
206 **
207 ** Result: "(x GLOB '*.o' OR x GLOB '*.obj')"
208 **
209 ** Each element of the GLOB list may optionally be enclosed in either '...'
210 ** or "...". This allows commas in the expression. Whitespace at the
211 ** beginning and end of each GLOB pattern is ignored, except when enclosed
212 ** within '...' or "...".
213 **
214 ** This routine makes no effort to free the memory space it uses.
215 */
216 char *glob_expr(const char *zVal, const char *zGlobList){
217 Blob expr;
218 char *zSep = "(";
219 int nTerm = 0;
220 int i;
221 int cTerm;
222
223 if( zGlobList==0 || zGlobList[0]==0 ) return "0";
224 blob_zero(&expr);
225 while( zGlobList[0] ){
226 while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
227 if( zGlobList[0]==0 ) break;
228 if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
229 cTerm = zGlobList[0];
230 zGlobList++;
231 }else{
232 cTerm = ',';
233 }
234 for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){}
235 if( cTerm==',' ){
236 while( i>0 && isspace(zGlobList[i-1]) ){ i--; }
237 }
238 blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList);
239 zSep = " OR ";
240 if( cTerm!=',' && zGlobList[i] ) i++;
241 zGlobList += i;
242 if( zGlobList[0] ) zGlobList++;
243 nTerm++;
244 }
245 if( nTerm ){
246 blob_appendf(&expr, ")");
247 return blob_str(&expr);
248 }else{
249 return "0";
250 }
251 }
252
253 /*
254 ** COMMAND: extras
255 ** Usage: %fossil extras ?--dotfiles? ?--ignore GLOBPATTERN?
256 **
257 ** Print a list of all files in the source tree that are not part of
258 ** the current checkout. See also the "clean" command.
259 **
260 ** Files and subdirectories whose names begin with "." are normally
@@ -209,20 +263,28 @@
263 void extra_cmd(void){
264 Blob path;
265 Blob repo;
266 Stmt q;
267 int n;
268 const char *zIgnoreFlag = find_option("ignore",0,1);
269 int allFlag = find_option("dotfiles",0,0)!=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 if( zIgnoreFlag==0 ){
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 ('manifest','manifest.uuid','_FOSSIL_')"
282 " AND NOT %s"
283 " ORDER BY 1",
284 glob_expr("x", zIgnoreFlag)
285 );
286 if( file_tree_name(g.zRepositoryName, &repo, 0) ){
287 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
288 }
289 while( db_step(&q)==SQLITE_ROW ){
290 printf("%s\n", db_column_text(&q, 0));
@@ -683,11 +745,11 @@
745 zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
746 zDate[10] = 'T';
747 blob_appendf(&manifest, "D %s\n", zDate);
748 zDate[10] = ' ';
749 db_prepare(&q,
750 "SELECT pathname, uuid, origname, blob.rid, isexe"
751 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
752 " WHERE NOT deleted AND vfile.vid=%d"
753 " ORDER BY 1", vid);
754 blob_zero(&filename);
755 blob_appendf(&filename, "%s", g.zLocalRoot);
@@ -695,13 +757,20 @@
757 while( db_step(&q)==SQLITE_ROW ){
758 const char *zName = db_column_text(&q, 0);
759 const char *zUuid = db_column_text(&q, 1);
760 const char *zOrig = db_column_text(&q, 2);
761 int frid = db_column_int(&q, 3);
762 int isexe = db_column_int(&q, 4);
763 const char *zPerm;
764 blob_append(&filename, zName, -1);
765 #ifndef __MINGW32__
766 /* For unix, extract the "executable" permission bit directly from
767 ** the filesystem. On windows, the "executable" bit is retained
768 ** unchanged from the original. */
769 isexe = file_isexe(blob_str(&filename));
770 #endif
771 if( isexe ){
772 zPerm = " x";
773 }else{
774 zPerm = "";
775 }
776 blob_resize(&filename, nBasename);
777
--- src/checkout.c
+++ src/checkout.c
@@ -93,10 +93,18 @@
9393
}
9494
content_get(vid, &manifest);
9595
vfile_build(vid, &manifest);
9696
blob_reset(&manifest);
9797
}
98
+
99
+/*
100
+** Set or clear the vfile.isexe flag for a file.
101
+*/
102
+static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
103
+ db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
104
+ onoff, vid, zFilename);
105
+}
98106
99107
/*
100108
** Read the manifest file given by vid out of the repository
101109
** and store it in the root of the local check-out.
102110
*/
@@ -128,10 +136,11 @@
128136
for(i=0; i<m.nFile; i++){
129137
int isExe;
130138
blob_append(&filename, m.aFile[i].zName, -1);
131139
isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
132140
file_setexe(blob_str(&filename), isExe);
141
+ set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
133142
blob_resize(&filename, baseLen);
134143
}
135144
blob_reset(&filename);
136145
manifest_clear(&m);
137146
}
138147
--- src/checkout.c
+++ src/checkout.c
@@ -93,10 +93,18 @@
93 }
94 content_get(vid, &manifest);
95 vfile_build(vid, &manifest);
96 blob_reset(&manifest);
97 }
 
 
 
 
 
 
 
 
98
99 /*
100 ** Read the manifest file given by vid out of the repository
101 ** and store it in the root of the local check-out.
102 */
@@ -128,10 +136,11 @@
128 for(i=0; i<m.nFile; i++){
129 int isExe;
130 blob_append(&filename, m.aFile[i].zName, -1);
131 isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
132 file_setexe(blob_str(&filename), isExe);
 
133 blob_resize(&filename, baseLen);
134 }
135 blob_reset(&filename);
136 manifest_clear(&m);
137 }
138
--- src/checkout.c
+++ src/checkout.c
@@ -93,10 +93,18 @@
93 }
94 content_get(vid, &manifest);
95 vfile_build(vid, &manifest);
96 blob_reset(&manifest);
97 }
98
99 /*
100 ** Set or clear the vfile.isexe flag for a file.
101 */
102 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
103 db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
104 onoff, vid, zFilename);
105 }
106
107 /*
108 ** Read the manifest file given by vid out of the repository
109 ** and store it in the root of the local check-out.
110 */
@@ -128,10 +136,11 @@
136 for(i=0; i<m.nFile; i++){
137 int isExe;
138 blob_append(&filename, m.aFile[i].zName, -1);
139 isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
140 file_setexe(blob_str(&filename), isExe);
141 set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
142 blob_resize(&filename, baseLen);
143 }
144 blob_reset(&filename);
145 manifest_clear(&m);
146 }
147
--- src/checkout.c
+++ src/checkout.c
@@ -93,10 +93,18 @@
9393
}
9494
content_get(vid, &manifest);
9595
vfile_build(vid, &manifest);
9696
blob_reset(&manifest);
9797
}
98
+
99
+/*
100
+** Set or clear the vfile.isexe flag for a file.
101
+*/
102
+static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
103
+ db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
104
+ onoff, vid, zFilename);
105
+}
98106
99107
/*
100108
** Read the manifest file given by vid out of the repository
101109
** and store it in the root of the local check-out.
102110
*/
@@ -128,10 +136,11 @@
128136
for(i=0; i<m.nFile; i++){
129137
int isExe;
130138
blob_append(&filename, m.aFile[i].zName, -1);
131139
isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
132140
file_setexe(blob_str(&filename), isExe);
141
+ set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
133142
blob_resize(&filename, baseLen);
134143
}
135144
blob_reset(&filename);
136145
manifest_clear(&m);
137146
}
138147
--- src/checkout.c
+++ src/checkout.c
@@ -93,10 +93,18 @@
93 }
94 content_get(vid, &manifest);
95 vfile_build(vid, &manifest);
96 blob_reset(&manifest);
97 }
 
 
 
 
 
 
 
 
98
99 /*
100 ** Read the manifest file given by vid out of the repository
101 ** and store it in the root of the local check-out.
102 */
@@ -128,10 +136,11 @@
128 for(i=0; i<m.nFile; i++){
129 int isExe;
130 blob_append(&filename, m.aFile[i].zName, -1);
131 isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
132 file_setexe(blob_str(&filename), isExe);
 
133 blob_resize(&filename, baseLen);
134 }
135 blob_reset(&filename);
136 manifest_clear(&m);
137 }
138
--- src/checkout.c
+++ src/checkout.c
@@ -93,10 +93,18 @@
93 }
94 content_get(vid, &manifest);
95 vfile_build(vid, &manifest);
96 blob_reset(&manifest);
97 }
98
99 /*
100 ** Set or clear the vfile.isexe flag for a file.
101 */
102 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
103 db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
104 onoff, vid, zFilename);
105 }
106
107 /*
108 ** Read the manifest file given by vid out of the repository
109 ** and store it in the root of the local check-out.
110 */
@@ -128,10 +136,11 @@
136 for(i=0; i<m.nFile; i++){
137 int isExe;
138 blob_append(&filename, m.aFile[i].zName, -1);
139 isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
140 file_setexe(blob_str(&filename), isExe);
141 set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
142 blob_resize(&filename, baseLen);
143 }
144 blob_reset(&filename);
145 manifest_clear(&m);
146 }
147
+39 -2
--- src/db.c
+++ src/db.c
@@ -496,20 +496,28 @@
496496
** zDefault instead.
497497
*/
498498
char *db_text(char *zDefault, const char *zSql, ...){
499499
va_list ap;
500500
Stmt s;
501
- char *z = zDefault;
501
+ char *z;
502502
va_start(ap, zSql);
503503
db_vprepare(&s, zSql, ap);
504504
va_end(ap);
505505
if( db_step(&s)==SQLITE_ROW ){
506506
z = mprintf("%s", sqlite3_column_text(s.pStmt, 0));
507
+ }else if( zDefault ){
508
+ z = mprintf("%s", zDefault);
509
+ }else{
510
+ z = 0;
507511
}
508512
db_finalize(&s);
509513
return z;
510514
}
515
+
516
+#ifdef __MINGW32__
517
+extern char *sqlite3_win32_mbcs_to_utf8(const char*);
518
+#endif
511519
512520
/*
513521
** Initialize a new database file with the given schema. If anything
514522
** goes wrong, call db_err() to exit.
515523
*/
@@ -661,29 +669,43 @@
661669
db_open_or_attach(zDbName, "localdb");
662670
g.localOpen = 1;
663671
db_open_config(0);
664672
db_open_repository(0);
665673
674
+ /* If the "isexe" column is missing from the vfile table, then
675
+ ** add it now. This code added on 2010-03-06. After all users have
676
+ ** upgraded, this code can be safely deleted.
677
+ */
678
+ rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
679
+ sqlite3_finalize(pStmt);
680
+ if( rc==SQLITE_ERROR ){
681
+ sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
682
+ }
683
+
684
+#if 0
666685
/* If the "mtime" column is missing from the vfile table, then
667686
** add it now. This code added on 2008-12-06. After all users have
668687
** upgraded, this code can be safely deleted.
669688
*/
670689
rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
671690
sqlite3_finalize(pStmt);
672691
if( rc==SQLITE_ERROR ){
673692
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
674693
}
694
+#endif
675695
696
+#if 0
676697
/* If the "origname" column is missing from the vfile table, then
677698
** add it now. This code added on 2008-11-09. After all users have
678699
** upgraded, this code can be safely deleted.
679700
*/
680701
rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
681702
sqlite3_finalize(pStmt);
682703
if( rc==SQLITE_ERROR ){
683704
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
684705
}
706
+#endif
685707
686708
return 1;
687709
}
688710
689711
/*
@@ -885,11 +907,15 @@
885907
** The zInitialDate parameter determines the date of the initial check-in
886908
** that is automatically created. If zInitialDate is 0 then no initial
887909
** check-in is created. The makeServerCodes flag determines whether or
888910
** not server and project codes are invented for this repository.
889911
*/
890
-void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int makeServerCodes){
912
+void db_initial_setup(
913
+ const char *zInitialDate, /* Initial date of repository. (ex: "now") */
914
+ const char *zDefaultUser, /* Default user for the repository */
915
+ int makeServerCodes /* True to make new server & project codes */
916
+){
891917
char *zDate;
892918
Blob hash;
893919
Blob manifest;
894920
895921
db_set("content-schema", CONTENT_SCHEMA, 0);
@@ -942,10 +968,11 @@
942968
** parameter.
943969
**
944970
** Options:
945971
**
946972
** --admin-user|-A USERNAME
973
+** --date-override DATETIME
947974
**
948975
*/
949976
void create_repository_cmd(void){
950977
char *zPassword;
951978
const char *zDate; /* Date of the initial check-in */
@@ -1405,10 +1432,14 @@
14051432
**
14061433
** autosync If enabled, automatically pull prior to commit
14071434
** or update and automatically push after commit or
14081435
** tag or branch creation. If the the value is "pullonly"
14091436
** then only pull operations occur automatically.
1437
+**
1438
+** binary-glob The VALUE is a comma-separated list of GLOB patterns
1439
+** that should be treated as binary files for merging
1440
+** purposes. Example: *.xml
14101441
**
14111442
** clearsign When enabled, fossil will attempt to sign all commits
14121443
** with gpg. When disabled (the default), commits will
14131444
** be unsigned.
14141445
**
@@ -1423,10 +1454,14 @@
14231454
** gdiff-command External command to run when performing a graphical
14241455
** diff. If undefined, text diff will be used.
14251456
**
14261457
** http-port The TCP/IP port number to use by the "server"
14271458
** and "ui" commands. Default: 8080
1459
+**
1460
+** ignore-glob The VALUE is a comma-separated list of GLOB patterns
1461
+** specifying files that the "extra" command will ignore.
1462
+** Example: *.o,*.obj,*.exe
14281463
**
14291464
** localauth If enabled, require that HTTP connections from
14301465
** 127.0.0.1 be authenticated by password. If
14311466
** false, all HTTP requests from localhost have
14321467
** unrestricted access to the repository.
@@ -1448,15 +1483,17 @@
14481483
** and "firefox" on Unix.
14491484
*/
14501485
void setting_cmd(void){
14511486
static const char *azName[] = {
14521487
"autosync",
1488
+ "binary-glob",
14531489
"clearsign",
14541490
"diff-command",
14551491
"dont-push",
14561492
"editor",
14571493
"gdiff-command",
1494
+ "ignore-glob",
14581495
"http-port",
14591496
"localauth",
14601497
"mtime-changes",
14611498
"pgp-command",
14621499
"proxy",
14631500
--- src/db.c
+++ src/db.c
@@ -496,20 +496,28 @@
496 ** zDefault instead.
497 */
498 char *db_text(char *zDefault, const char *zSql, ...){
499 va_list ap;
500 Stmt s;
501 char *z = zDefault;
502 va_start(ap, zSql);
503 db_vprepare(&s, zSql, ap);
504 va_end(ap);
505 if( db_step(&s)==SQLITE_ROW ){
506 z = mprintf("%s", sqlite3_column_text(s.pStmt, 0));
 
 
 
 
507 }
508 db_finalize(&s);
509 return z;
510 }
 
 
 
 
511
512 /*
513 ** Initialize a new database file with the given schema. If anything
514 ** goes wrong, call db_err() to exit.
515 */
@@ -661,29 +669,43 @@
661 db_open_or_attach(zDbName, "localdb");
662 g.localOpen = 1;
663 db_open_config(0);
664 db_open_repository(0);
665
 
 
 
 
 
 
 
 
 
 
 
666 /* If the "mtime" column is missing from the vfile table, then
667 ** add it now. This code added on 2008-12-06. After all users have
668 ** upgraded, this code can be safely deleted.
669 */
670 rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
671 sqlite3_finalize(pStmt);
672 if( rc==SQLITE_ERROR ){
673 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
674 }
 
675
 
676 /* If the "origname" column is missing from the vfile table, then
677 ** add it now. This code added on 2008-11-09. After all users have
678 ** upgraded, this code can be safely deleted.
679 */
680 rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
681 sqlite3_finalize(pStmt);
682 if( rc==SQLITE_ERROR ){
683 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
684 }
 
685
686 return 1;
687 }
688
689 /*
@@ -885,11 +907,15 @@
885 ** The zInitialDate parameter determines the date of the initial check-in
886 ** that is automatically created. If zInitialDate is 0 then no initial
887 ** check-in is created. The makeServerCodes flag determines whether or
888 ** not server and project codes are invented for this repository.
889 */
890 void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int makeServerCodes){
 
 
 
 
891 char *zDate;
892 Blob hash;
893 Blob manifest;
894
895 db_set("content-schema", CONTENT_SCHEMA, 0);
@@ -942,10 +968,11 @@
942 ** parameter.
943 **
944 ** Options:
945 **
946 ** --admin-user|-A USERNAME
 
947 **
948 */
949 void create_repository_cmd(void){
950 char *zPassword;
951 const char *zDate; /* Date of the initial check-in */
@@ -1405,10 +1432,14 @@
1405 **
1406 ** autosync If enabled, automatically pull prior to commit
1407 ** or update and automatically push after commit or
1408 ** tag or branch creation. If the the value is "pullonly"
1409 ** then only pull operations occur automatically.
 
 
 
 
1410 **
1411 ** clearsign When enabled, fossil will attempt to sign all commits
1412 ** with gpg. When disabled (the default), commits will
1413 ** be unsigned.
1414 **
@@ -1423,10 +1454,14 @@
1423 ** gdiff-command External command to run when performing a graphical
1424 ** diff. If undefined, text diff will be used.
1425 **
1426 ** http-port The TCP/IP port number to use by the "server"
1427 ** and "ui" commands. Default: 8080
 
 
 
 
1428 **
1429 ** localauth If enabled, require that HTTP connections from
1430 ** 127.0.0.1 be authenticated by password. If
1431 ** false, all HTTP requests from localhost have
1432 ** unrestricted access to the repository.
@@ -1448,15 +1483,17 @@
1448 ** and "firefox" on Unix.
1449 */
1450 void setting_cmd(void){
1451 static const char *azName[] = {
1452 "autosync",
 
1453 "clearsign",
1454 "diff-command",
1455 "dont-push",
1456 "editor",
1457 "gdiff-command",
 
1458 "http-port",
1459 "localauth",
1460 "mtime-changes",
1461 "pgp-command",
1462 "proxy",
1463
--- src/db.c
+++ src/db.c
@@ -496,20 +496,28 @@
496 ** zDefault instead.
497 */
498 char *db_text(char *zDefault, const char *zSql, ...){
499 va_list ap;
500 Stmt s;
501 char *z;
502 va_start(ap, zSql);
503 db_vprepare(&s, zSql, ap);
504 va_end(ap);
505 if( db_step(&s)==SQLITE_ROW ){
506 z = mprintf("%s", sqlite3_column_text(s.pStmt, 0));
507 }else if( zDefault ){
508 z = mprintf("%s", zDefault);
509 }else{
510 z = 0;
511 }
512 db_finalize(&s);
513 return z;
514 }
515
516 #ifdef __MINGW32__
517 extern char *sqlite3_win32_mbcs_to_utf8(const char*);
518 #endif
519
520 /*
521 ** Initialize a new database file with the given schema. If anything
522 ** goes wrong, call db_err() to exit.
523 */
@@ -661,29 +669,43 @@
669 db_open_or_attach(zDbName, "localdb");
670 g.localOpen = 1;
671 db_open_config(0);
672 db_open_repository(0);
673
674 /* If the "isexe" column is missing from the vfile table, then
675 ** add it now. This code added on 2010-03-06. After all users have
676 ** upgraded, this code can be safely deleted.
677 */
678 rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
679 sqlite3_finalize(pStmt);
680 if( rc==SQLITE_ERROR ){
681 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
682 }
683
684 #if 0
685 /* If the "mtime" column is missing from the vfile table, then
686 ** add it now. This code added on 2008-12-06. After all users have
687 ** upgraded, this code can be safely deleted.
688 */
689 rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
690 sqlite3_finalize(pStmt);
691 if( rc==SQLITE_ERROR ){
692 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
693 }
694 #endif
695
696 #if 0
697 /* If the "origname" column is missing from the vfile table, then
698 ** add it now. This code added on 2008-11-09. After all users have
699 ** upgraded, this code can be safely deleted.
700 */
701 rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
702 sqlite3_finalize(pStmt);
703 if( rc==SQLITE_ERROR ){
704 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
705 }
706 #endif
707
708 return 1;
709 }
710
711 /*
@@ -885,11 +907,15 @@
907 ** The zInitialDate parameter determines the date of the initial check-in
908 ** that is automatically created. If zInitialDate is 0 then no initial
909 ** check-in is created. The makeServerCodes flag determines whether or
910 ** not server and project codes are invented for this repository.
911 */
912 void db_initial_setup(
913 const char *zInitialDate, /* Initial date of repository. (ex: "now") */
914 const char *zDefaultUser, /* Default user for the repository */
915 int makeServerCodes /* True to make new server & project codes */
916 ){
917 char *zDate;
918 Blob hash;
919 Blob manifest;
920
921 db_set("content-schema", CONTENT_SCHEMA, 0);
@@ -942,10 +968,11 @@
968 ** parameter.
969 **
970 ** Options:
971 **
972 ** --admin-user|-A USERNAME
973 ** --date-override DATETIME
974 **
975 */
976 void create_repository_cmd(void){
977 char *zPassword;
978 const char *zDate; /* Date of the initial check-in */
@@ -1405,10 +1432,14 @@
1432 **
1433 ** autosync If enabled, automatically pull prior to commit
1434 ** or update and automatically push after commit or
1435 ** tag or branch creation. If the the value is "pullonly"
1436 ** then only pull operations occur automatically.
1437 **
1438 ** binary-glob The VALUE is a comma-separated list of GLOB patterns
1439 ** that should be treated as binary files for merging
1440 ** purposes. Example: *.xml
1441 **
1442 ** clearsign When enabled, fossil will attempt to sign all commits
1443 ** with gpg. When disabled (the default), commits will
1444 ** be unsigned.
1445 **
@@ -1423,10 +1454,14 @@
1454 ** gdiff-command External command to run when performing a graphical
1455 ** diff. If undefined, text diff will be used.
1456 **
1457 ** http-port The TCP/IP port number to use by the "server"
1458 ** and "ui" commands. Default: 8080
1459 **
1460 ** ignore-glob The VALUE is a comma-separated list of GLOB patterns
1461 ** specifying files that the "extra" command will ignore.
1462 ** Example: *.o,*.obj,*.exe
1463 **
1464 ** localauth If enabled, require that HTTP connections from
1465 ** 127.0.0.1 be authenticated by password. If
1466 ** false, all HTTP requests from localhost have
1467 ** unrestricted access to the repository.
@@ -1448,15 +1483,17 @@
1483 ** and "firefox" on Unix.
1484 */
1485 void setting_cmd(void){
1486 static const char *azName[] = {
1487 "autosync",
1488 "binary-glob",
1489 "clearsign",
1490 "diff-command",
1491 "dont-push",
1492 "editor",
1493 "gdiff-command",
1494 "ignore-glob",
1495 "http-port",
1496 "localauth",
1497 "mtime-changes",
1498 "pgp-command",
1499 "proxy",
1500
+39 -2
--- src/db.c
+++ src/db.c
@@ -496,20 +496,28 @@
496496
** zDefault instead.
497497
*/
498498
char *db_text(char *zDefault, const char *zSql, ...){
499499
va_list ap;
500500
Stmt s;
501
- char *z = zDefault;
501
+ char *z;
502502
va_start(ap, zSql);
503503
db_vprepare(&s, zSql, ap);
504504
va_end(ap);
505505
if( db_step(&s)==SQLITE_ROW ){
506506
z = mprintf("%s", sqlite3_column_text(s.pStmt, 0));
507
+ }else if( zDefault ){
508
+ z = mprintf("%s", zDefault);
509
+ }else{
510
+ z = 0;
507511
}
508512
db_finalize(&s);
509513
return z;
510514
}
515
+
516
+#ifdef __MINGW32__
517
+extern char *sqlite3_win32_mbcs_to_utf8(const char*);
518
+#endif
511519
512520
/*
513521
** Initialize a new database file with the given schema. If anything
514522
** goes wrong, call db_err() to exit.
515523
*/
@@ -661,29 +669,43 @@
661669
db_open_or_attach(zDbName, "localdb");
662670
g.localOpen = 1;
663671
db_open_config(0);
664672
db_open_repository(0);
665673
674
+ /* If the "isexe" column is missing from the vfile table, then
675
+ ** add it now. This code added on 2010-03-06. After all users have
676
+ ** upgraded, this code can be safely deleted.
677
+ */
678
+ rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
679
+ sqlite3_finalize(pStmt);
680
+ if( rc==SQLITE_ERROR ){
681
+ sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
682
+ }
683
+
684
+#if 0
666685
/* If the "mtime" column is missing from the vfile table, then
667686
** add it now. This code added on 2008-12-06. After all users have
668687
** upgraded, this code can be safely deleted.
669688
*/
670689
rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
671690
sqlite3_finalize(pStmt);
672691
if( rc==SQLITE_ERROR ){
673692
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
674693
}
694
+#endif
675695
696
+#if 0
676697
/* If the "origname" column is missing from the vfile table, then
677698
** add it now. This code added on 2008-11-09. After all users have
678699
** upgraded, this code can be safely deleted.
679700
*/
680701
rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
681702
sqlite3_finalize(pStmt);
682703
if( rc==SQLITE_ERROR ){
683704
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
684705
}
706
+#endif
685707
686708
return 1;
687709
}
688710
689711
/*
@@ -885,11 +907,15 @@
885907
** The zInitialDate parameter determines the date of the initial check-in
886908
** that is automatically created. If zInitialDate is 0 then no initial
887909
** check-in is created. The makeServerCodes flag determines whether or
888910
** not server and project codes are invented for this repository.
889911
*/
890
-void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int makeServerCodes){
912
+void db_initial_setup(
913
+ const char *zInitialDate, /* Initial date of repository. (ex: "now") */
914
+ const char *zDefaultUser, /* Default user for the repository */
915
+ int makeServerCodes /* True to make new server & project codes */
916
+){
891917
char *zDate;
892918
Blob hash;
893919
Blob manifest;
894920
895921
db_set("content-schema", CONTENT_SCHEMA, 0);
@@ -942,10 +968,11 @@
942968
** parameter.
943969
**
944970
** Options:
945971
**
946972
** --admin-user|-A USERNAME
973
+** --date-override DATETIME
947974
**
948975
*/
949976
void create_repository_cmd(void){
950977
char *zPassword;
951978
const char *zDate; /* Date of the initial check-in */
@@ -1405,10 +1432,14 @@
14051432
**
14061433
** autosync If enabled, automatically pull prior to commit
14071434
** or update and automatically push after commit or
14081435
** tag or branch creation. If the the value is "pullonly"
14091436
** then only pull operations occur automatically.
1437
+**
1438
+** binary-glob The VALUE is a comma-separated list of GLOB patterns
1439
+** that should be treated as binary files for merging
1440
+** purposes. Example: *.xml
14101441
**
14111442
** clearsign When enabled, fossil will attempt to sign all commits
14121443
** with gpg. When disabled (the default), commits will
14131444
** be unsigned.
14141445
**
@@ -1423,10 +1454,14 @@
14231454
** gdiff-command External command to run when performing a graphical
14241455
** diff. If undefined, text diff will be used.
14251456
**
14261457
** http-port The TCP/IP port number to use by the "server"
14271458
** and "ui" commands. Default: 8080
1459
+**
1460
+** ignore-glob The VALUE is a comma-separated list of GLOB patterns
1461
+** specifying files that the "extra" command will ignore.
1462
+** Example: *.o,*.obj,*.exe
14281463
**
14291464
** localauth If enabled, require that HTTP connections from
14301465
** 127.0.0.1 be authenticated by password. If
14311466
** false, all HTTP requests from localhost have
14321467
** unrestricted access to the repository.
@@ -1448,15 +1483,17 @@
14481483
** and "firefox" on Unix.
14491484
*/
14501485
void setting_cmd(void){
14511486
static const char *azName[] = {
14521487
"autosync",
1488
+ "binary-glob",
14531489
"clearsign",
14541490
"diff-command",
14551491
"dont-push",
14561492
"editor",
14571493
"gdiff-command",
1494
+ "ignore-glob",
14581495
"http-port",
14591496
"localauth",
14601497
"mtime-changes",
14611498
"pgp-command",
14621499
"proxy",
14631500
--- src/db.c
+++ src/db.c
@@ -496,20 +496,28 @@
496 ** zDefault instead.
497 */
498 char *db_text(char *zDefault, const char *zSql, ...){
499 va_list ap;
500 Stmt s;
501 char *z = zDefault;
502 va_start(ap, zSql);
503 db_vprepare(&s, zSql, ap);
504 va_end(ap);
505 if( db_step(&s)==SQLITE_ROW ){
506 z = mprintf("%s", sqlite3_column_text(s.pStmt, 0));
 
 
 
 
507 }
508 db_finalize(&s);
509 return z;
510 }
 
 
 
 
511
512 /*
513 ** Initialize a new database file with the given schema. If anything
514 ** goes wrong, call db_err() to exit.
515 */
@@ -661,29 +669,43 @@
661 db_open_or_attach(zDbName, "localdb");
662 g.localOpen = 1;
663 db_open_config(0);
664 db_open_repository(0);
665
 
 
 
 
 
 
 
 
 
 
 
666 /* If the "mtime" column is missing from the vfile table, then
667 ** add it now. This code added on 2008-12-06. After all users have
668 ** upgraded, this code can be safely deleted.
669 */
670 rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
671 sqlite3_finalize(pStmt);
672 if( rc==SQLITE_ERROR ){
673 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
674 }
 
675
 
676 /* If the "origname" column is missing from the vfile table, then
677 ** add it now. This code added on 2008-11-09. After all users have
678 ** upgraded, this code can be safely deleted.
679 */
680 rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
681 sqlite3_finalize(pStmt);
682 if( rc==SQLITE_ERROR ){
683 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
684 }
 
685
686 return 1;
687 }
688
689 /*
@@ -885,11 +907,15 @@
885 ** The zInitialDate parameter determines the date of the initial check-in
886 ** that is automatically created. If zInitialDate is 0 then no initial
887 ** check-in is created. The makeServerCodes flag determines whether or
888 ** not server and project codes are invented for this repository.
889 */
890 void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int makeServerCodes){
 
 
 
 
891 char *zDate;
892 Blob hash;
893 Blob manifest;
894
895 db_set("content-schema", CONTENT_SCHEMA, 0);
@@ -942,10 +968,11 @@
942 ** parameter.
943 **
944 ** Options:
945 **
946 ** --admin-user|-A USERNAME
 
947 **
948 */
949 void create_repository_cmd(void){
950 char *zPassword;
951 const char *zDate; /* Date of the initial check-in */
@@ -1405,10 +1432,14 @@
1405 **
1406 ** autosync If enabled, automatically pull prior to commit
1407 ** or update and automatically push after commit or
1408 ** tag or branch creation. If the the value is "pullonly"
1409 ** then only pull operations occur automatically.
 
 
 
 
1410 **
1411 ** clearsign When enabled, fossil will attempt to sign all commits
1412 ** with gpg. When disabled (the default), commits will
1413 ** be unsigned.
1414 **
@@ -1423,10 +1454,14 @@
1423 ** gdiff-command External command to run when performing a graphical
1424 ** diff. If undefined, text diff will be used.
1425 **
1426 ** http-port The TCP/IP port number to use by the "server"
1427 ** and "ui" commands. Default: 8080
 
 
 
 
1428 **
1429 ** localauth If enabled, require that HTTP connections from
1430 ** 127.0.0.1 be authenticated by password. If
1431 ** false, all HTTP requests from localhost have
1432 ** unrestricted access to the repository.
@@ -1448,15 +1483,17 @@
1448 ** and "firefox" on Unix.
1449 */
1450 void setting_cmd(void){
1451 static const char *azName[] = {
1452 "autosync",
 
1453 "clearsign",
1454 "diff-command",
1455 "dont-push",
1456 "editor",
1457 "gdiff-command",
 
1458 "http-port",
1459 "localauth",
1460 "mtime-changes",
1461 "pgp-command",
1462 "proxy",
1463
--- src/db.c
+++ src/db.c
@@ -496,20 +496,28 @@
496 ** zDefault instead.
497 */
498 char *db_text(char *zDefault, const char *zSql, ...){
499 va_list ap;
500 Stmt s;
501 char *z;
502 va_start(ap, zSql);
503 db_vprepare(&s, zSql, ap);
504 va_end(ap);
505 if( db_step(&s)==SQLITE_ROW ){
506 z = mprintf("%s", sqlite3_column_text(s.pStmt, 0));
507 }else if( zDefault ){
508 z = mprintf("%s", zDefault);
509 }else{
510 z = 0;
511 }
512 db_finalize(&s);
513 return z;
514 }
515
516 #ifdef __MINGW32__
517 extern char *sqlite3_win32_mbcs_to_utf8(const char*);
518 #endif
519
520 /*
521 ** Initialize a new database file with the given schema. If anything
522 ** goes wrong, call db_err() to exit.
523 */
@@ -661,29 +669,43 @@
669 db_open_or_attach(zDbName, "localdb");
670 g.localOpen = 1;
671 db_open_config(0);
672 db_open_repository(0);
673
674 /* If the "isexe" column is missing from the vfile table, then
675 ** add it now. This code added on 2010-03-06. After all users have
676 ** upgraded, this code can be safely deleted.
677 */
678 rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
679 sqlite3_finalize(pStmt);
680 if( rc==SQLITE_ERROR ){
681 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
682 }
683
684 #if 0
685 /* If the "mtime" column is missing from the vfile table, then
686 ** add it now. This code added on 2008-12-06. After all users have
687 ** upgraded, this code can be safely deleted.
688 */
689 rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
690 sqlite3_finalize(pStmt);
691 if( rc==SQLITE_ERROR ){
692 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
693 }
694 #endif
695
696 #if 0
697 /* If the "origname" column is missing from the vfile table, then
698 ** add it now. This code added on 2008-11-09. After all users have
699 ** upgraded, this code can be safely deleted.
700 */
701 rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
702 sqlite3_finalize(pStmt);
703 if( rc==SQLITE_ERROR ){
704 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
705 }
706 #endif
707
708 return 1;
709 }
710
711 /*
@@ -885,11 +907,15 @@
907 ** The zInitialDate parameter determines the date of the initial check-in
908 ** that is automatically created. If zInitialDate is 0 then no initial
909 ** check-in is created. The makeServerCodes flag determines whether or
910 ** not server and project codes are invented for this repository.
911 */
912 void db_initial_setup(
913 const char *zInitialDate, /* Initial date of repository. (ex: "now") */
914 const char *zDefaultUser, /* Default user for the repository */
915 int makeServerCodes /* True to make new server & project codes */
916 ){
917 char *zDate;
918 Blob hash;
919 Blob manifest;
920
921 db_set("content-schema", CONTENT_SCHEMA, 0);
@@ -942,10 +968,11 @@
968 ** parameter.
969 **
970 ** Options:
971 **
972 ** --admin-user|-A USERNAME
973 ** --date-override DATETIME
974 **
975 */
976 void create_repository_cmd(void){
977 char *zPassword;
978 const char *zDate; /* Date of the initial check-in */
@@ -1405,10 +1432,14 @@
1432 **
1433 ** autosync If enabled, automatically pull prior to commit
1434 ** or update and automatically push after commit or
1435 ** tag or branch creation. If the the value is "pullonly"
1436 ** then only pull operations occur automatically.
1437 **
1438 ** binary-glob The VALUE is a comma-separated list of GLOB patterns
1439 ** that should be treated as binary files for merging
1440 ** purposes. Example: *.xml
1441 **
1442 ** clearsign When enabled, fossil will attempt to sign all commits
1443 ** with gpg. When disabled (the default), commits will
1444 ** be unsigned.
1445 **
@@ -1423,10 +1454,14 @@
1454 ** gdiff-command External command to run when performing a graphical
1455 ** diff. If undefined, text diff will be used.
1456 **
1457 ** http-port The TCP/IP port number to use by the "server"
1458 ** and "ui" commands. Default: 8080
1459 **
1460 ** ignore-glob The VALUE is a comma-separated list of GLOB patterns
1461 ** specifying files that the "extra" command will ignore.
1462 ** Example: *.o,*.obj,*.exe
1463 **
1464 ** localauth If enabled, require that HTTP connections from
1465 ** 127.0.0.1 be authenticated by password. If
1466 ** false, all HTTP requests from localhost have
1467 ** unrestricted access to the repository.
@@ -1448,15 +1483,17 @@
1483 ** and "firefox" on Unix.
1484 */
1485 void setting_cmd(void){
1486 static const char *azName[] = {
1487 "autosync",
1488 "binary-glob",
1489 "clearsign",
1490 "diff-command",
1491 "dont-push",
1492 "editor",
1493 "gdiff-command",
1494 "ignore-glob",
1495 "http-port",
1496 "localauth",
1497 "mtime-changes",
1498 "pgp-command",
1499 "proxy",
1500
+1 -1
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -49,11 +49,11 @@
4949
}
5050
5151
/*
5252
** This function implements a cross-platform "system()" interface.
5353
*/
54
-int portable_system(char *zOrigCmd){
54
+int portable_system(const char *zOrigCmd){
5555
int rc;
5656
#ifdef __MINGW32__
5757
/* On windows, we have to put double-quotes around the entire command.
5858
** Who knows why - this is just the way windows works.
5959
*/
6060
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -49,11 +49,11 @@
49 }
50
51 /*
52 ** This function implements a cross-platform "system()" interface.
53 */
54 int portable_system(char *zOrigCmd){
55 int rc;
56 #ifdef __MINGW32__
57 /* On windows, we have to put double-quotes around the entire command.
58 ** Who knows why - this is just the way windows works.
59 */
60
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -49,11 +49,11 @@
49 }
50
51 /*
52 ** This function implements a cross-platform "system()" interface.
53 */
54 int portable_system(const char *zOrigCmd){
55 int rc;
56 #ifdef __MINGW32__
57 /* On windows, we have to put double-quotes around the entire command.
58 ** Who knows why - this is just the way windows works.
59 */
60
+1 -1
--- src/file.c
+++ src/file.c
@@ -163,11 +163,11 @@
163163
}else{
164164
if( (buf.st_mode & 0111)!=0 ){
165165
chmod(zFilename, buf.st_mode & ~0111);
166166
}
167167
}
168
-#endif
168
+#endif /* __MINGW32__ */
169169
}
170170
171171
/*
172172
** Create the directory named in the argument, if it does not already
173173
** exist. If forceFlag is 1, delete any prior non-directory object
174174
--- src/file.c
+++ src/file.c
@@ -163,11 +163,11 @@
163 }else{
164 if( (buf.st_mode & 0111)!=0 ){
165 chmod(zFilename, buf.st_mode & ~0111);
166 }
167 }
168 #endif
169 }
170
171 /*
172 ** Create the directory named in the argument, if it does not already
173 ** exist. If forceFlag is 1, delete any prior non-directory object
174
--- src/file.c
+++ src/file.c
@@ -163,11 +163,11 @@
163 }else{
164 if( (buf.st_mode & 0111)!=0 ){
165 chmod(zFilename, buf.st_mode & ~0111);
166 }
167 }
168 #endif /* __MINGW32__ */
169 }
170
171 /*
172 ** Create the directory named in the argument, if it does not already
173 ** exist. If forceFlag is 1, delete any prior non-directory object
174
+48 -12
--- src/finfo.c
+++ src/finfo.c
@@ -100,36 +100,49 @@
100100
void finfo_page(void){
101101
Stmt q;
102102
const char *zFilename;
103103
char zPrevDate[20];
104104
Blob title;
105
+ GraphContext *pGraph;
105106
106107
login_check_credentials();
107108
if( !g.okRead ){ login_needed(); return; }
108109
style_header("File History");
109110
login_anonymous_available();
110111
111112
zPrevDate[0] = 0;
112113
zFilename = PD("name","");
113114
db_prepare(&q,
114
- "SELECT substr(b.uuid,1,10), datetime(event.mtime,'localtime'),"
115
- " coalesce(event.ecomment, event.comment),"
116
- " coalesce(event.euser, event.user),"
117
- " mlink.pid, mlink.fid, mlink.mid, mlink.fnid, ci.uuid"
115
+ "SELECT"
116
+ " substr(b.uuid,1,10),"
117
+ " datetime(event.mtime,'localtime'),"
118
+ " coalesce(event.ecomment, event.comment),"
119
+ " coalesce(event.euser, event.user),"
120
+ " mlink.pid,"
121
+ " mlink.fid,"
122
+ " mlink.mid,"
123
+ " mlink.fnid,"
124
+ " ci.uuid,"
125
+ " event.bgcolor,"
126
+ " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
127
+ " AND tagxref.rid=mlink.mid)"
118128
" FROM mlink, blob b, event, blob ci"
119129
" WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
120130
" AND b.rid=mlink.fid"
121131
" AND event.objid=mlink.mid"
122132
" AND event.objid=ci.rid"
123133
" ORDER BY event.mtime DESC",
134
+ TAG_BRANCH,
124135
zFilename
125136
);
126137
blob_zero(&title);
127138
blob_appendf(&title, "History of ");
128139
hyperlinked_path(zFilename, &title);
129140
@ <h2>%b(&title)</h2>
130141
blob_reset(&title);
142
+ pGraph = graph_init();
143
+ @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
131144
@ <table cellspacing=0 border=0 cellpadding=0>
132145
while( db_step(&q)==SQLITE_ROW ){
133146
const char *zUuid = db_column_text(&q, 0);
134147
const char *zDate = db_column_text(&q, 1);
135148
const char *zCom = db_column_text(&q, 2);
@@ -137,33 +150,46 @@
137150
int fpid = db_column_int(&q, 4);
138151
int frid = db_column_int(&q, 5);
139152
int mid = db_column_int(&q, 6);
140153
int fnid = db_column_int(&q, 7);
141154
const char *zCkin = db_column_text(&q,8);
155
+ const char *zBgClr = db_column_text(&q, 9);
156
+ const char *zBr = db_column_text(&q, 10);
157
+ int gidx;
158
+ char zTime[10];
142159
char zShort[20];
143160
char zShortCkin[20];
161
+ if( zBr==0 ) zBr = "trunk";
162
+ gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr);
144163
if( memcmp(zDate, zPrevDate, 10) ){
145164
sprintf(zPrevDate, "%.10s", zDate);
146
- @ <tr><td colspan=3>
147
- @ <div class="divider">%s(zPrevDate)</div>
165
+ @ <tr><td>
166
+ @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
148167
@ </td></tr>
149168
}
150
- @ <tr><td valign="top">%s(&zDate[11])</td>
151
- @ <td width="20"></td>
152
- @ <td valign="top" align="left">
169
+ memcpy(zTime, &zDate[11], 5);
170
+ zTime[5] = 0;
171
+ @ <tr><td valign="top" align="right">
172
+ @ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
173
+ @ <td width="20" align="left" valign="top"><div id="m%d(gidx)"></div></td>
174
+ if( zBgClr && zBgClr[0] ){
175
+ @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
176
+ }else{
177
+ @ <td valign="top" align="left">
178
+ }
153179
sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
154180
sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
155181
if( g.okHistory ){
156182
@ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%s(zShort)]</a>
157183
}else{
158184
@ [%s(zShort)]
159185
}
160186
@ part of check-in
161187
hyperlink_to_uuid(zShortCkin);
162
- @ %h(zCom) (By:
163
- hyperlink_to_user(zUser, zDate, " on");
164
- hyperlink_to_date(zDate, ")");
188
+ @ %h(zCom) (user:
189
+ hyperlink_to_user(zUser, zDate, "");
190
+ @ branch: %h(zBr))
165191
if( g.okHistory ){
166192
if( fpid ){
167193
@ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&amp;v2=%d(frid)">[diff]</a>
168194
}
169195
@ <a href="%s(g.zBaseURL)/annotate?mid=%d(mid)&amp;fnid=%d(fnid)">
@@ -170,8 +196,18 @@
170196
@ [annotate]</a>
171197
@ </td>
172198
}
173199
}
174200
db_finalize(&q);
201
+ if( pGraph ){
202
+ graph_finish(pGraph, 1);
203
+ if( pGraph->nErr ){
204
+ graph_free(pGraph);
205
+ pGraph = 0;
206
+ }else{
207
+ @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
208
+ }
209
+ }
175210
@ </table>
211
+ timeline_output_graph_javascript(pGraph);
176212
style_footer();
177213
}
178214
--- src/finfo.c
+++ src/finfo.c
@@ -100,36 +100,49 @@
100 void finfo_page(void){
101 Stmt q;
102 const char *zFilename;
103 char zPrevDate[20];
104 Blob title;
 
105
106 login_check_credentials();
107 if( !g.okRead ){ login_needed(); return; }
108 style_header("File History");
109 login_anonymous_available();
110
111 zPrevDate[0] = 0;
112 zFilename = PD("name","");
113 db_prepare(&q,
114 "SELECT substr(b.uuid,1,10), datetime(event.mtime,'localtime'),"
115 " coalesce(event.ecomment, event.comment),"
116 " coalesce(event.euser, event.user),"
117 " mlink.pid, mlink.fid, mlink.mid, mlink.fnid, ci.uuid"
 
 
 
 
 
 
 
 
 
118 " FROM mlink, blob b, event, blob ci"
119 " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
120 " AND b.rid=mlink.fid"
121 " AND event.objid=mlink.mid"
122 " AND event.objid=ci.rid"
123 " ORDER BY event.mtime DESC",
 
124 zFilename
125 );
126 blob_zero(&title);
127 blob_appendf(&title, "History of ");
128 hyperlinked_path(zFilename, &title);
129 @ <h2>%b(&title)</h2>
130 blob_reset(&title);
 
 
131 @ <table cellspacing=0 border=0 cellpadding=0>
132 while( db_step(&q)==SQLITE_ROW ){
133 const char *zUuid = db_column_text(&q, 0);
134 const char *zDate = db_column_text(&q, 1);
135 const char *zCom = db_column_text(&q, 2);
@@ -137,33 +150,46 @@
137 int fpid = db_column_int(&q, 4);
138 int frid = db_column_int(&q, 5);
139 int mid = db_column_int(&q, 6);
140 int fnid = db_column_int(&q, 7);
141 const char *zCkin = db_column_text(&q,8);
 
 
 
 
142 char zShort[20];
143 char zShortCkin[20];
 
 
144 if( memcmp(zDate, zPrevDate, 10) ){
145 sprintf(zPrevDate, "%.10s", zDate);
146 @ <tr><td colspan=3>
147 @ <div class="divider">%s(zPrevDate)</div>
148 @ </td></tr>
149 }
150 @ <tr><td valign="top">%s(&zDate[11])</td>
151 @ <td width="20"></td>
152 @ <td valign="top" align="left">
 
 
 
 
 
 
 
153 sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
154 sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
155 if( g.okHistory ){
156 @ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%s(zShort)]</a>
157 }else{
158 @ [%s(zShort)]
159 }
160 @ part of check-in
161 hyperlink_to_uuid(zShortCkin);
162 @ %h(zCom) (By:
163 hyperlink_to_user(zUser, zDate, " on");
164 hyperlink_to_date(zDate, ")");
165 if( g.okHistory ){
166 if( fpid ){
167 @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&amp;v2=%d(frid)">[diff]</a>
168 }
169 @ <a href="%s(g.zBaseURL)/annotate?mid=%d(mid)&amp;fnid=%d(fnid)">
@@ -170,8 +196,18 @@
170 @ [annotate]</a>
171 @ </td>
172 }
173 }
174 db_finalize(&q);
 
 
 
 
 
 
 
 
 
175 @ </table>
 
176 style_footer();
177 }
178
--- src/finfo.c
+++ src/finfo.c
@@ -100,36 +100,49 @@
100 void finfo_page(void){
101 Stmt q;
102 const char *zFilename;
103 char zPrevDate[20];
104 Blob title;
105 GraphContext *pGraph;
106
107 login_check_credentials();
108 if( !g.okRead ){ login_needed(); return; }
109 style_header("File History");
110 login_anonymous_available();
111
112 zPrevDate[0] = 0;
113 zFilename = PD("name","");
114 db_prepare(&q,
115 "SELECT"
116 " substr(b.uuid,1,10),"
117 " datetime(event.mtime,'localtime'),"
118 " coalesce(event.ecomment, event.comment),"
119 " coalesce(event.euser, event.user),"
120 " mlink.pid,"
121 " mlink.fid,"
122 " mlink.mid,"
123 " mlink.fnid,"
124 " ci.uuid,"
125 " event.bgcolor,"
126 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
127 " AND tagxref.rid=mlink.mid)"
128 " FROM mlink, blob b, event, blob ci"
129 " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
130 " AND b.rid=mlink.fid"
131 " AND event.objid=mlink.mid"
132 " AND event.objid=ci.rid"
133 " ORDER BY event.mtime DESC",
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 cellspacing=0 border=0 cellpadding=0>
145 while( db_step(&q)==SQLITE_ROW ){
146 const char *zUuid = db_column_text(&q, 0);
147 const char *zDate = db_column_text(&q, 1);
148 const char *zCom = db_column_text(&q, 2);
@@ -137,33 +150,46 @@
150 int fpid = db_column_int(&q, 4);
151 int frid = db_column_int(&q, 5);
152 int mid = db_column_int(&q, 6);
153 int fnid = db_column_int(&q, 7);
154 const char *zCkin = db_column_text(&q,8);
155 const char *zBgClr = db_column_text(&q, 9);
156 const char *zBr = db_column_text(&q, 10);
157 int gidx;
158 char zTime[10];
159 char zShort[20];
160 char zShortCkin[20];
161 if( zBr==0 ) zBr = "trunk";
162 gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr);
163 if( memcmp(zDate, zPrevDate, 10) ){
164 sprintf(zPrevDate, "%.10s", zDate);
165 @ <tr><td>
166 @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
167 @ </td></tr>
168 }
169 memcpy(zTime, &zDate[11], 5);
170 zTime[5] = 0;
171 @ <tr><td valign="top" align="right">
172 @ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
173 @ <td width="20" align="left" valign="top"><div id="m%d(gidx)"></div></td>
174 if( zBgClr && zBgClr[0] ){
175 @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
176 }else{
177 @ <td valign="top" align="left">
178 }
179 sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
180 sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
181 if( g.okHistory ){
182 @ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%s(zShort)]</a>
183 }else{
184 @ [%s(zShort)]
185 }
186 @ part of check-in
187 hyperlink_to_uuid(zShortCkin);
188 @ %h(zCom) (user:
189 hyperlink_to_user(zUser, zDate, "");
190 @ branch: %h(zBr))
191 if( g.okHistory ){
192 if( fpid ){
193 @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&amp;v2=%d(frid)">[diff]</a>
194 }
195 @ <a href="%s(g.zBaseURL)/annotate?mid=%d(mid)&amp;fnid=%d(fnid)">
@@ -170,8 +196,18 @@
196 @ [annotate]</a>
197 @ </td>
198 }
199 }
200 db_finalize(&q);
201 if( pGraph ){
202 graph_finish(pGraph, 1);
203 if( pGraph->nErr ){
204 graph_free(pGraph);
205 pGraph = 0;
206 }else{
207 @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
208 }
209 }
210 @ </table>
211 timeline_output_graph_javascript(pGraph);
212 style_footer();
213 }
214
+61 -38
--- src/graph.c
+++ src/graph.c
@@ -35,19 +35,19 @@
3535
/* The graph appears vertically beside a timeline. Each row in the
3636
** timeline corresponds to a row in the graph.
3737
*/
3838
struct GraphRow {
3939
int rid; /* The rid for the check-in */
40
- int isLeaf; /* True if the check-in is an open leaf */
4140
int nParent; /* Number of parents */
4241
int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/
4342
char *zBranch; /* Branch name */
4443
4544
GraphRow *pNext; /* Next row down in the list of all rows */
4645
GraphRow *pPrev; /* Previous row */
4746
4847
int idx; /* Row index. First is 1. 0 used for "none" */
48
+ int isLeaf; /* True if no direct child nodes */
4949
int iRail; /* Which rail this check-in appears on. 0-based.*/
5050
int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */
5151
int bDescender; /* Raiser from bottom of graph to here. */
5252
u32 mergeIn; /* Merge in from other rails */
5353
int mergeOut; /* Merge out to this rail */
@@ -63,11 +63,14 @@
6363
int mxRail; /* Number of rails required to render the graph */
6464
GraphRow *pFirst; /* First row in the list */
6565
GraphRow *pLast; /* Last row in the list */
6666
int nBranch; /* Number of distinct branches */
6767
char **azBranch; /* Names of the branches */
68
+ int nRow; /* Number of rows */
6869
int railMap[GR_MAX_RAIL]; /* Rail order mapping */
70
+ int nHash; /* Number of slots in apHash[] */
71
+ GraphRow **apHash; /* Hash table of rows */
6972
};
7073
7174
#endif
7275
7376
/*
@@ -99,12 +102,39 @@
99102
p->pFirst = pRow->pNext;
100103
free(pRow);
101104
}
102105
for(i=0; i<p->nBranch; i++) free(p->azBranch[i]);
103106
free(p->azBranch);
107
+ free(p->apHash);
104108
free(p);
105109
}
110
+
111
+/*
112
+** Insert a row into the hash table. If there is already another
113
+** row with the same rid, the other row is replaced.
114
+*/
115
+static void hashInsert(GraphContext *p, GraphRow *pRow){
116
+ int h;
117
+ h = pRow->rid % p->nHash;
118
+ while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){
119
+ h++;
120
+ if( h>=p->nHash ) h = 0;
121
+ }
122
+ p->apHash[h] = pRow;
123
+}
124
+
125
+/*
126
+** Look up the row with rid.
127
+*/
128
+static GraphRow *hashFind(GraphContext *p, int rid){
129
+ int h = rid % p->nHash;
130
+ while( p->apHash[h] && p->apHash[h]->rid!=rid ){
131
+ h++;
132
+ if( h>=p->nHash ) h = 0;
133
+ }
134
+ return p->apHash[h];
135
+}
106136
107137
/*
108138
** Return the canonical pointer for a given branch name.
109139
** Multiple calls to this routine with equivalent strings
110140
** will return the same pointer.
@@ -122,34 +152,35 @@
122152
}
123153
124154
/*
125155
** Add a new row t the graph context. Rows are added from top to bottom.
126156
*/
127
-void graph_add_row(
157
+int graph_add_row(
128158
GraphContext *p, /* The context to which the row is added */
129159
int rid, /* RID for the check-in */
130
- int isLeaf, /* True if the check-in is an leaf */
131160
int nParent, /* Number of parents */
132161
int *aParent, /* Array of parents */
133162
const char *zBranch /* Branch for this check-in */
134163
){
135164
GraphRow *pRow;
136165
137
- if( p->nErr ) return;
138
- if( nParent>GR_MAX_PARENT ){ p->nErr++; return; }
166
+ if( p->nErr ) return 0;
167
+ if( nParent>GR_MAX_PARENT ){ p->nErr++; return 0; }
139168
pRow = (GraphRow*)safeMalloc( sizeof(GraphRow) );
140169
pRow->rid = rid;
141
- pRow->isLeaf = isLeaf;
142170
pRow->nParent = nParent;
143171
pRow->zBranch = persistBranchName(p, zBranch);
144172
memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent);
145173
if( p->pFirst==0 ){
146174
p->pFirst = pRow;
147175
}else{
148176
p->pLast->pNext = pRow;
149177
}
150178
p->pLast = pRow;
179
+ p->nRow++;
180
+ pRow->idx = p->nRow;
181
+ return pRow->idx;
151182
}
152183
153184
/*
154185
** Return the index of a rail currently not in use for any row between
155186
** top and bottom, inclusive.
@@ -188,51 +219,59 @@
188219
/*
189220
** Compute the complete graph
190221
*/
191222
void graph_finish(GraphContext *p, int omitDescenders){
192223
GraphRow *pRow, *pDesc;
193
- Bag allRids;
194
- Bag notLeaf;
195224
int i;
196
- int nRow;
197225
u32 mask;
198226
u32 inUse;
199227
200228
if( p==0 || p->pFirst==0 || p->nErr ) return;
201229
202230
/* Initialize all rows */
203
- bag_init(&allRids);
204
- bag_init(&notLeaf);
205
- nRow = 0;
231
+ p->nHash = p->nRow*2 + 1;
232
+ p->apHash = safeMalloc( sizeof(p->apHash[0])*p->nHash );
206233
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
207234
if( pRow->pNext ) pRow->pNext->pPrev = pRow;
208
- pRow->idx = ++nRow;
209235
pRow->iRail = -1;
210236
pRow->mergeOut = -1;
211
- bag_insert(&allRids, pRow->rid);
237
+ hashInsert(p, pRow);
212238
}
213239
p->mxRail = -1;
214240
215241
/* Purge merge-parents that are out-of-graph
216242
*/
217243
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
218244
for(i=1; i<pRow->nParent; i++){
219
- if( !bag_find(&allRids, pRow->aParent[i]) ){
245
+ if( hashFind(p, pRow->aParent[i])==0 ){
220246
pRow->aParent[i] = pRow->aParent[--pRow->nParent];
221247
i--;
222248
}
223249
}
224
- if( pRow->nParent>0 && bag_find(&allRids, pRow->aParent[0]) ){
225
- bag_insert(&notLeaf, pRow->aParent[0]);
250
+ }
251
+
252
+ /* Figure out which nodes have no direct children (children on
253
+ ** the same rail). Mark such nodes is isLeaf.
254
+ */
255
+ memset(p->apHash, 0, sizeof(p->apHash[0])*p->nHash);
256
+ for(pRow=p->pLast; pRow; pRow=pRow->pPrev) pRow->isLeaf = 1;
257
+ for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
258
+ GraphRow *pParent;
259
+ hashInsert(p, pRow);
260
+ if( pRow->nParent>0
261
+ && (pParent = hashFind(p, pRow->aParent[0]))!=0
262
+ && pRow->zBranch==pParent->zBranch
263
+ ){
264
+ pParent->isLeaf = 0;
226265
}
227266
}
228267
229268
/* Identify rows where the primary parent is off screen. Assign
230269
** each to a rail and draw descenders to the bottom of the screen.
231270
*/
232271
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
233
- if( pRow->nParent==0 || !bag_find(&allRids,pRow->aParent[0]) ){
272
+ if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
234273
if( omitDescenders ){
235274
pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0);
236275
}else{
237276
pRow->iRail = ++p->mxRail;
238277
}
@@ -257,11 +296,10 @@
257296
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
258297
int parentRid;
259298
if( pRow->iRail>=0 ) continue;
260299
assert( pRow->nParent>0 );
261300
parentRid = pRow->aParent[0];
262
- assert( bag_find(&allRids, parentRid) );
263301
for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid; pDesc=pDesc->pNext){}
264302
if( pDesc==0 ){
265303
/* Time skew */
266304
pRow->iRail = ++p->mxRail;
267305
pRow->railInUse = 1<<pRow->iRail;
@@ -272,14 +310,14 @@
272310
}else{
273311
pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, 0);
274312
}
275313
pDesc->aiRaiser[pRow->iRail] = pRow->idx;
276314
mask = 1<<pRow->iRail;
277
- if( bag_find(&notLeaf, pRow->rid) ){
278
- inUse |= mask;
315
+ if( pRow->isLeaf ){
316
+ inUse &= ~mask;
279317
}else{
280
- inUse &= ~mask;
318
+ inUse |= mask;
281319
}
282320
for(pDesc = pRow; ; pDesc=pDesc->pNext){
283321
assert( pDesc!=0 );
284322
pDesc->railInUse |= mask;
285323
if( pDesc->rid==parentRid ) break;
@@ -309,29 +347,14 @@
309347
pRow->mergeIn |= 1<<pDesc->mergeOut;
310348
}
311349
}
312350
313351
/*
314
- ** Sort the rail numbers
352
+ ** Find the maximum rail number.
315353
*/
316
-#if 0
317
- p->mxRail = -1;
318
- mask = 0;
319
- for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
320
- if( (mask & (1<<pRow->iRail))==0 ){
321
- p->railMap[pRow->iRail] = ++p->mxRail;
322
- mask |= 1<<pRow->iRail;
323
- }
324
- if( pRow->mergeOut>=0 && (mask & (1<<pRow->mergeOut))==0 ){
325
- p->railMap[pRow->mergeOut] = ++p->mxRail;
326
- mask |= 1<<pRow->mergeOut;
327
- }
328
- }
329
-#else
330354
for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i;
331355
p->mxRail = 0;
332356
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
333357
if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
334358
if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
335359
}
336
-#endif
337360
}
338361
--- src/graph.c
+++ src/graph.c
@@ -35,19 +35,19 @@
35 /* The graph appears vertically beside a timeline. Each row in the
36 ** timeline corresponds to a row in the graph.
37 */
38 struct GraphRow {
39 int rid; /* The rid for the check-in */
40 int isLeaf; /* True if the check-in is an open leaf */
41 int nParent; /* Number of parents */
42 int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/
43 char *zBranch; /* Branch name */
44
45 GraphRow *pNext; /* Next row down in the list of all rows */
46 GraphRow *pPrev; /* Previous row */
47
48 int idx; /* Row index. First is 1. 0 used for "none" */
 
49 int iRail; /* Which rail this check-in appears on. 0-based.*/
50 int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */
51 int bDescender; /* Raiser from bottom of graph to here. */
52 u32 mergeIn; /* Merge in from other rails */
53 int mergeOut; /* Merge out to this rail */
@@ -63,11 +63,14 @@
63 int mxRail; /* Number of rails required to render the graph */
64 GraphRow *pFirst; /* First row in the list */
65 GraphRow *pLast; /* Last row in the list */
66 int nBranch; /* Number of distinct branches */
67 char **azBranch; /* Names of the branches */
 
68 int railMap[GR_MAX_RAIL]; /* Rail order mapping */
 
 
69 };
70
71 #endif
72
73 /*
@@ -99,12 +102,39 @@
99 p->pFirst = pRow->pNext;
100 free(pRow);
101 }
102 for(i=0; i<p->nBranch; i++) free(p->azBranch[i]);
103 free(p->azBranch);
 
104 free(p);
105 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
107 /*
108 ** Return the canonical pointer for a given branch name.
109 ** Multiple calls to this routine with equivalent strings
110 ** will return the same pointer.
@@ -122,34 +152,35 @@
122 }
123
124 /*
125 ** Add a new row t the graph context. Rows are added from top to bottom.
126 */
127 void graph_add_row(
128 GraphContext *p, /* The context to which the row is added */
129 int rid, /* RID for the check-in */
130 int isLeaf, /* True if the check-in is an leaf */
131 int nParent, /* Number of parents */
132 int *aParent, /* Array of parents */
133 const char *zBranch /* Branch for this check-in */
134 ){
135 GraphRow *pRow;
136
137 if( p->nErr ) return;
138 if( nParent>GR_MAX_PARENT ){ p->nErr++; return; }
139 pRow = (GraphRow*)safeMalloc( sizeof(GraphRow) );
140 pRow->rid = rid;
141 pRow->isLeaf = isLeaf;
142 pRow->nParent = nParent;
143 pRow->zBranch = persistBranchName(p, zBranch);
144 memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent);
145 if( p->pFirst==0 ){
146 p->pFirst = pRow;
147 }else{
148 p->pLast->pNext = pRow;
149 }
150 p->pLast = pRow;
 
 
 
151 }
152
153 /*
154 ** Return the index of a rail currently not in use for any row between
155 ** top and bottom, inclusive.
@@ -188,51 +219,59 @@
188 /*
189 ** Compute the complete graph
190 */
191 void graph_finish(GraphContext *p, int omitDescenders){
192 GraphRow *pRow, *pDesc;
193 Bag allRids;
194 Bag notLeaf;
195 int i;
196 int nRow;
197 u32 mask;
198 u32 inUse;
199
200 if( p==0 || p->pFirst==0 || p->nErr ) return;
201
202 /* Initialize all rows */
203 bag_init(&allRids);
204 bag_init(&notLeaf);
205 nRow = 0;
206 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
207 if( pRow->pNext ) pRow->pNext->pPrev = pRow;
208 pRow->idx = ++nRow;
209 pRow->iRail = -1;
210 pRow->mergeOut = -1;
211 bag_insert(&allRids, pRow->rid);
212 }
213 p->mxRail = -1;
214
215 /* Purge merge-parents that are out-of-graph
216 */
217 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
218 for(i=1; i<pRow->nParent; i++){
219 if( !bag_find(&allRids, pRow->aParent[i]) ){
220 pRow->aParent[i] = pRow->aParent[--pRow->nParent];
221 i--;
222 }
223 }
224 if( pRow->nParent>0 && bag_find(&allRids, pRow->aParent[0]) ){
225 bag_insert(&notLeaf, pRow->aParent[0]);
 
 
 
 
 
 
 
 
 
 
 
 
 
226 }
227 }
228
229 /* Identify rows where the primary parent is off screen. Assign
230 ** each to a rail and draw descenders to the bottom of the screen.
231 */
232 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
233 if( pRow->nParent==0 || !bag_find(&allRids,pRow->aParent[0]) ){
234 if( omitDescenders ){
235 pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0);
236 }else{
237 pRow->iRail = ++p->mxRail;
238 }
@@ -257,11 +296,10 @@
257 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
258 int parentRid;
259 if( pRow->iRail>=0 ) continue;
260 assert( pRow->nParent>0 );
261 parentRid = pRow->aParent[0];
262 assert( bag_find(&allRids, parentRid) );
263 for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid; pDesc=pDesc->pNext){}
264 if( pDesc==0 ){
265 /* Time skew */
266 pRow->iRail = ++p->mxRail;
267 pRow->railInUse = 1<<pRow->iRail;
@@ -272,14 +310,14 @@
272 }else{
273 pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, 0);
274 }
275 pDesc->aiRaiser[pRow->iRail] = pRow->idx;
276 mask = 1<<pRow->iRail;
277 if( bag_find(&notLeaf, pRow->rid) ){
278 inUse |= mask;
279 }else{
280 inUse &= ~mask;
281 }
282 for(pDesc = pRow; ; pDesc=pDesc->pNext){
283 assert( pDesc!=0 );
284 pDesc->railInUse |= mask;
285 if( pDesc->rid==parentRid ) break;
@@ -309,29 +347,14 @@
309 pRow->mergeIn |= 1<<pDesc->mergeOut;
310 }
311 }
312
313 /*
314 ** Sort the rail numbers
315 */
316 #if 0
317 p->mxRail = -1;
318 mask = 0;
319 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
320 if( (mask & (1<<pRow->iRail))==0 ){
321 p->railMap[pRow->iRail] = ++p->mxRail;
322 mask |= 1<<pRow->iRail;
323 }
324 if( pRow->mergeOut>=0 && (mask & (1<<pRow->mergeOut))==0 ){
325 p->railMap[pRow->mergeOut] = ++p->mxRail;
326 mask |= 1<<pRow->mergeOut;
327 }
328 }
329 #else
330 for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i;
331 p->mxRail = 0;
332 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
333 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
334 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
335 }
336 #endif
337 }
338
--- src/graph.c
+++ src/graph.c
@@ -35,19 +35,19 @@
35 /* The graph appears vertically beside a timeline. Each row in the
36 ** timeline corresponds to a row in the graph.
37 */
38 struct GraphRow {
39 int rid; /* The rid for the check-in */
 
40 int nParent; /* Number of parents */
41 int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/
42 char *zBranch; /* Branch name */
43
44 GraphRow *pNext; /* Next row down in the list of all rows */
45 GraphRow *pPrev; /* Previous row */
46
47 int idx; /* Row index. First is 1. 0 used for "none" */
48 int isLeaf; /* True if no direct child nodes */
49 int iRail; /* Which rail this check-in appears on. 0-based.*/
50 int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */
51 int bDescender; /* Raiser from bottom of graph to here. */
52 u32 mergeIn; /* Merge in from other rails */
53 int mergeOut; /* Merge out to this rail */
@@ -63,11 +63,14 @@
63 int mxRail; /* Number of rails required to render the graph */
64 GraphRow *pFirst; /* First row in the list */
65 GraphRow *pLast; /* Last row in the list */
66 int nBranch; /* Number of distinct branches */
67 char **azBranch; /* Names of the branches */
68 int nRow; /* Number of rows */
69 int railMap[GR_MAX_RAIL]; /* Rail order mapping */
70 int nHash; /* Number of slots in apHash[] */
71 GraphRow **apHash; /* Hash table of rows */
72 };
73
74 #endif
75
76 /*
@@ -99,12 +102,39 @@
102 p->pFirst = pRow->pNext;
103 free(pRow);
104 }
105 for(i=0; i<p->nBranch; i++) free(p->azBranch[i]);
106 free(p->azBranch);
107 free(p->apHash);
108 free(p);
109 }
110
111 /*
112 ** Insert a row into the hash table. If there is already another
113 ** row with the same rid, the other row is replaced.
114 */
115 static void hashInsert(GraphContext *p, GraphRow *pRow){
116 int h;
117 h = pRow->rid % p->nHash;
118 while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){
119 h++;
120 if( h>=p->nHash ) h = 0;
121 }
122 p->apHash[h] = pRow;
123 }
124
125 /*
126 ** Look up the row with rid.
127 */
128 static GraphRow *hashFind(GraphContext *p, int rid){
129 int h = rid % p->nHash;
130 while( p->apHash[h] && p->apHash[h]->rid!=rid ){
131 h++;
132 if( h>=p->nHash ) h = 0;
133 }
134 return p->apHash[h];
135 }
136
137 /*
138 ** Return the canonical pointer for a given branch name.
139 ** Multiple calls to this routine with equivalent strings
140 ** will return the same pointer.
@@ -122,34 +152,35 @@
152 }
153
154 /*
155 ** Add a new row t the graph context. Rows are added from top to bottom.
156 */
157 int graph_add_row(
158 GraphContext *p, /* The context to which the row is added */
159 int rid, /* RID for the check-in */
 
160 int nParent, /* Number of parents */
161 int *aParent, /* Array of parents */
162 const char *zBranch /* Branch for this check-in */
163 ){
164 GraphRow *pRow;
165
166 if( p->nErr ) return 0;
167 if( nParent>GR_MAX_PARENT ){ p->nErr++; return 0; }
168 pRow = (GraphRow*)safeMalloc( sizeof(GraphRow) );
169 pRow->rid = rid;
 
170 pRow->nParent = nParent;
171 pRow->zBranch = persistBranchName(p, zBranch);
172 memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent);
173 if( p->pFirst==0 ){
174 p->pFirst = pRow;
175 }else{
176 p->pLast->pNext = pRow;
177 }
178 p->pLast = pRow;
179 p->nRow++;
180 pRow->idx = p->nRow;
181 return pRow->idx;
182 }
183
184 /*
185 ** Return the index of a rail currently not in use for any row between
186 ** top and bottom, inclusive.
@@ -188,51 +219,59 @@
219 /*
220 ** Compute the complete graph
221 */
222 void graph_finish(GraphContext *p, int omitDescenders){
223 GraphRow *pRow, *pDesc;
 
 
224 int i;
 
225 u32 mask;
226 u32 inUse;
227
228 if( p==0 || p->pFirst==0 || p->nErr ) return;
229
230 /* Initialize all rows */
231 p->nHash = p->nRow*2 + 1;
232 p->apHash = safeMalloc( sizeof(p->apHash[0])*p->nHash );
 
233 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
234 if( pRow->pNext ) pRow->pNext->pPrev = pRow;
 
235 pRow->iRail = -1;
236 pRow->mergeOut = -1;
237 hashInsert(p, pRow);
238 }
239 p->mxRail = -1;
240
241 /* Purge merge-parents that are out-of-graph
242 */
243 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
244 for(i=1; i<pRow->nParent; i++){
245 if( hashFind(p, pRow->aParent[i])==0 ){
246 pRow->aParent[i] = pRow->aParent[--pRow->nParent];
247 i--;
248 }
249 }
250 }
251
252 /* Figure out which nodes have no direct children (children on
253 ** the same rail). Mark such nodes is isLeaf.
254 */
255 memset(p->apHash, 0, sizeof(p->apHash[0])*p->nHash);
256 for(pRow=p->pLast; pRow; pRow=pRow->pPrev) pRow->isLeaf = 1;
257 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
258 GraphRow *pParent;
259 hashInsert(p, pRow);
260 if( pRow->nParent>0
261 && (pParent = hashFind(p, pRow->aParent[0]))!=0
262 && pRow->zBranch==pParent->zBranch
263 ){
264 pParent->isLeaf = 0;
265 }
266 }
267
268 /* Identify rows where the primary parent is off screen. Assign
269 ** each to a rail and draw descenders to the bottom of the screen.
270 */
271 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
272 if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
273 if( omitDescenders ){
274 pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0);
275 }else{
276 pRow->iRail = ++p->mxRail;
277 }
@@ -257,11 +296,10 @@
296 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
297 int parentRid;
298 if( pRow->iRail>=0 ) continue;
299 assert( pRow->nParent>0 );
300 parentRid = pRow->aParent[0];
 
301 for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid; pDesc=pDesc->pNext){}
302 if( pDesc==0 ){
303 /* Time skew */
304 pRow->iRail = ++p->mxRail;
305 pRow->railInUse = 1<<pRow->iRail;
@@ -272,14 +310,14 @@
310 }else{
311 pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, 0);
312 }
313 pDesc->aiRaiser[pRow->iRail] = pRow->idx;
314 mask = 1<<pRow->iRail;
315 if( pRow->isLeaf ){
316 inUse &= ~mask;
317 }else{
318 inUse |= mask;
319 }
320 for(pDesc = pRow; ; pDesc=pDesc->pNext){
321 assert( pDesc!=0 );
322 pDesc->railInUse |= mask;
323 if( pDesc->rid==parentRid ) break;
@@ -309,29 +347,14 @@
347 pRow->mergeIn |= 1<<pDesc->mergeOut;
348 }
349 }
350
351 /*
352 ** Find the maximum rail number.
353 */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354 for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i;
355 p->mxRail = 0;
356 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
357 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
358 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
359 }
 
360 }
361
+42
--- src/info.c
+++ src/info.c
@@ -732,10 +732,52 @@
732732
}
733733
cnt++;
734734
}
735735
db_finalize(&q);
736736
}
737
+ db_prepare(&q,
738
+ "SELECT target, filename, datetime(mtime), user, src"
739
+ " FROM attachment"
740
+ " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)"
741
+ " ORDER BY mtime DESC",
742
+ rid
743
+ );
744
+ while( db_step(&q)==SQLITE_ROW ){
745
+ const char *zTarget = db_column_text(&q, 0);
746
+ const char *zFilename = db_column_text(&q, 1);
747
+ const char *zDate = db_column_text(&q, 2);
748
+ const char *zUser = db_column_text(&q, 3);
749
+ const char *zSrc = db_column_text(&q, 4);
750
+ if( cnt>0 ){
751
+ @ Also attachment "%h(zFilename)" to
752
+ }else{
753
+ @ Attachment "%h(zFilename)" to
754
+ }
755
+ if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
756
+ char zShort[20];
757
+ memcpy(zShort, zTarget, 10);
758
+ if( g.okHistory && g.okRdTkt ){
759
+ @ ticket [<a href="%s(g.zTop)/tktview?name=%s(zShort)">%s(zShort)</a>]
760
+ }else{
761
+ @ ticket [%s(zShort)]
762
+ }
763
+ }else{
764
+ if( g.okHistory && g.okRdWiki ){
765
+ @ wiki page [<a href="%s(g.zTop)/wiki?name=%t(zTarget)">%h(zTarget)</a>]
766
+ }else{
767
+ @ wiki page [%h(zTarget)]
768
+ }
769
+ }
770
+ @ added by
771
+ hyperlink_to_user(zUser,zDate," on");
772
+ hyperlink_to_date(zDate,".");
773
+ cnt++;
774
+ if( pDownloadName && blob_size(pDownloadName)==0 ){
775
+ blob_append(pDownloadName, zSrc, -1);
776
+ }
777
+ }
778
+ db_finalize(&q);
737779
if( cnt==0 ){
738780
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
739781
@ Control artifact.
740782
if( pDownloadName && blob_size(pDownloadName)==0 ){
741783
blob_append(pDownloadName, zUuid, -1);
742784
--- src/info.c
+++ src/info.c
@@ -732,10 +732,52 @@
732 }
733 cnt++;
734 }
735 db_finalize(&q);
736 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737 if( cnt==0 ){
738 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
739 @ Control artifact.
740 if( pDownloadName && blob_size(pDownloadName)==0 ){
741 blob_append(pDownloadName, zUuid, -1);
742
--- src/info.c
+++ src/info.c
@@ -732,10 +732,52 @@
732 }
733 cnt++;
734 }
735 db_finalize(&q);
736 }
737 db_prepare(&q,
738 "SELECT target, filename, datetime(mtime), user, src"
739 " FROM attachment"
740 " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)"
741 " ORDER BY mtime DESC",
742 rid
743 );
744 while( db_step(&q)==SQLITE_ROW ){
745 const char *zTarget = db_column_text(&q, 0);
746 const char *zFilename = db_column_text(&q, 1);
747 const char *zDate = db_column_text(&q, 2);
748 const char *zUser = db_column_text(&q, 3);
749 const char *zSrc = db_column_text(&q, 4);
750 if( cnt>0 ){
751 @ Also attachment "%h(zFilename)" to
752 }else{
753 @ Attachment "%h(zFilename)" to
754 }
755 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
756 char zShort[20];
757 memcpy(zShort, zTarget, 10);
758 if( g.okHistory && g.okRdTkt ){
759 @ ticket [<a href="%s(g.zTop)/tktview?name=%s(zShort)">%s(zShort)</a>]
760 }else{
761 @ ticket [%s(zShort)]
762 }
763 }else{
764 if( g.okHistory && g.okRdWiki ){
765 @ wiki page [<a href="%s(g.zTop)/wiki?name=%t(zTarget)">%h(zTarget)</a>]
766 }else{
767 @ wiki page [%h(zTarget)]
768 }
769 }
770 @ added by
771 hyperlink_to_user(zUser,zDate," on");
772 hyperlink_to_date(zDate,".");
773 cnt++;
774 if( pDownloadName && blob_size(pDownloadName)==0 ){
775 blob_append(pDownloadName, zSrc, -1);
776 }
777 }
778 db_finalize(&q);
779 if( cnt==0 ){
780 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
781 @ Control artifact.
782 if( pDownloadName && blob_size(pDownloadName)==0 ){
783 blob_append(pDownloadName, zUuid, -1);
784
+4 -2
--- src/login.c
+++ src/login.c
@@ -476,11 +476,12 @@
476476
case 's': g.okSetup = 1; /* Fall thru into Admin */
477477
case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
478478
g.okRdWiki = g.okWrWiki = g.okNewWiki =
479479
g.okApndWiki = g.okHistory = g.okClone =
480480
g.okNewTkt = g.okPassword = g.okRdAddr =
481
- g.okTktFmt = 1; /* Fall thru into Read/Write */
481
+ g.okTktFmt = g.okAttach = 1;
482
+ /* Fall thru into Read/Write */
482483
case 'i': g.okRead = g.okWrite = 1; break;
483484
case 'o': g.okRead = 1; break;
484485
case 'z': g.okZip = 1; break;
485486
486487
case 'd': g.okDelete = 1; break;
@@ -498,10 +499,11 @@
498499
case 'n': g.okNewTkt = 1; break;
499500
case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
500501
g.okApndTkt = 1; break;
501502
case 'c': g.okApndTkt = 1; break;
502503
case 't': g.okTktFmt = 1; break;
504
+ case 'b': g.okAttach = 1; break;
503505
504506
/* The "u" privileges is a little different. It recursively
505507
** inherits all privileges of the user named "reader" */
506508
case 'u': {
507509
if( zUser==0 ){
@@ -534,11 +536,11 @@
534536
int rc = 1;
535537
if( nCap<0 ) nCap = strlen(zCap);
536538
for(i=0; i<nCap && rc && zCap[i]; i++){
537539
switch( zCap[i] ){
538540
case 'a': rc = g.okAdmin; break;
539
- /* case 'b': */
541
+ case 'b': rc = g.okAttach; break;
540542
case 'c': rc = g.okApndTkt; break;
541543
case 'd': rc = g.okDelete; break;
542544
case 'e': rc = g.okRdAddr; break;
543545
case 'f': rc = g.okNewWiki; break;
544546
case 'g': rc = g.okClone; break;
545547
--- src/login.c
+++ src/login.c
@@ -476,11 +476,12 @@
476 case 's': g.okSetup = 1; /* Fall thru into Admin */
477 case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
478 g.okRdWiki = g.okWrWiki = g.okNewWiki =
479 g.okApndWiki = g.okHistory = g.okClone =
480 g.okNewTkt = g.okPassword = g.okRdAddr =
481 g.okTktFmt = 1; /* Fall thru into Read/Write */
 
482 case 'i': g.okRead = g.okWrite = 1; break;
483 case 'o': g.okRead = 1; break;
484 case 'z': g.okZip = 1; break;
485
486 case 'd': g.okDelete = 1; break;
@@ -498,10 +499,11 @@
498 case 'n': g.okNewTkt = 1; break;
499 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
500 g.okApndTkt = 1; break;
501 case 'c': g.okApndTkt = 1; break;
502 case 't': g.okTktFmt = 1; break;
 
503
504 /* The "u" privileges is a little different. It recursively
505 ** inherits all privileges of the user named "reader" */
506 case 'u': {
507 if( zUser==0 ){
@@ -534,11 +536,11 @@
534 int rc = 1;
535 if( nCap<0 ) nCap = strlen(zCap);
536 for(i=0; i<nCap && rc && zCap[i]; i++){
537 switch( zCap[i] ){
538 case 'a': rc = g.okAdmin; break;
539 /* case 'b': */
540 case 'c': rc = g.okApndTkt; break;
541 case 'd': rc = g.okDelete; break;
542 case 'e': rc = g.okRdAddr; break;
543 case 'f': rc = g.okNewWiki; break;
544 case 'g': rc = g.okClone; break;
545
--- src/login.c
+++ src/login.c
@@ -476,11 +476,12 @@
476 case 's': g.okSetup = 1; /* Fall thru into Admin */
477 case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
478 g.okRdWiki = g.okWrWiki = g.okNewWiki =
479 g.okApndWiki = g.okHistory = g.okClone =
480 g.okNewTkt = g.okPassword = g.okRdAddr =
481 g.okTktFmt = g.okAttach = 1;
482 /* Fall thru into Read/Write */
483 case 'i': g.okRead = g.okWrite = 1; break;
484 case 'o': g.okRead = 1; break;
485 case 'z': g.okZip = 1; break;
486
487 case 'd': g.okDelete = 1; break;
@@ -498,10 +499,11 @@
499 case 'n': g.okNewTkt = 1; break;
500 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
501 g.okApndTkt = 1; break;
502 case 'c': g.okApndTkt = 1; break;
503 case 't': g.okTktFmt = 1; break;
504 case 'b': g.okAttach = 1; break;
505
506 /* The "u" privileges is a little different. It recursively
507 ** inherits all privileges of the user named "reader" */
508 case 'u': {
509 if( zUser==0 ){
@@ -534,11 +536,11 @@
536 int rc = 1;
537 if( nCap<0 ) nCap = strlen(zCap);
538 for(i=0; i<nCap && rc && zCap[i]; i++){
539 switch( zCap[i] ){
540 case 'a': rc = g.okAdmin; break;
541 case 'b': rc = g.okAttach; break;
542 case 'c': rc = g.okApndTkt; break;
543 case 'd': rc = g.okDelete; break;
544 case 'e': rc = g.okRdAddr; break;
545 case 'f': rc = g.okNewWiki; break;
546 case 'g': rc = g.okClone; break;
547
+1
--- src/main.c
+++ src/main.c
@@ -130,10 +130,11 @@
130130
int okWrWiki; /* k: edit wiki via web */
131131
int okRdTkt; /* r: view tickets via web */
132132
int okNewTkt; /* n: create new tickets */
133133
int okApndTkt; /* c: append to tickets via the web */
134134
int okWrTkt; /* w: make changes to tickets via web */
135
+ int okAttach; /* b: add attachments */
135136
int okTktFmt; /* t: create new ticket report formats */
136137
int okRdAddr; /* e: read email addresses or other private data */
137138
int okZip; /* z: download zipped artifact via /zip URL */
138139
139140
/* For defense against Cross-site Request Forgery attacks */
140141
--- src/main.c
+++ src/main.c
@@ -130,10 +130,11 @@
130 int okWrWiki; /* k: edit wiki via web */
131 int okRdTkt; /* r: view tickets via web */
132 int okNewTkt; /* n: create new tickets */
133 int okApndTkt; /* c: append to tickets via the web */
134 int okWrTkt; /* w: make changes to tickets via web */
 
135 int okTktFmt; /* t: create new ticket report formats */
136 int okRdAddr; /* e: read email addresses or other private data */
137 int okZip; /* z: download zipped artifact via /zip URL */
138
139 /* For defense against Cross-site Request Forgery attacks */
140
--- src/main.c
+++ src/main.c
@@ -130,10 +130,11 @@
130 int okWrWiki; /* k: edit wiki via web */
131 int okRdTkt; /* r: view tickets via web */
132 int okNewTkt; /* n: create new tickets */
133 int okApndTkt; /* c: append to tickets via the web */
134 int okWrTkt; /* w: make changes to tickets via web */
135 int okAttach; /* b: add attachments */
136 int okTktFmt; /* t: create new ticket report formats */
137 int okRdAddr; /* e: read email addresses or other private data */
138 int okZip; /* z: download zipped artifact via /zip URL */
139
140 /* For defense against Cross-site Request Forgery attacks */
141
+12 -2
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
1313
1414
1515
SRC = \
1616
$(SRCDIR)/add.c \
1717
$(SRCDIR)/allrepo.c \
18
+ $(SRCDIR)/attach.c \
1819
$(SRCDIR)/bag.c \
1920
$(SRCDIR)/blob.c \
2021
$(SRCDIR)/branch.c \
2122
$(SRCDIR)/browse.c \
2223
$(SRCDIR)/captcha.c \
@@ -83,10 +84,11 @@
8384
$(SRCDIR)/zip.c
8485
8586
TRANS_SRC = \
8687
add_.c \
8788
allrepo_.c \
89
+ attach_.c \
8890
bag_.c \
8991
blob_.c \
9092
branch_.c \
9193
browse_.c \
9294
captcha_.c \
@@ -153,10 +155,11 @@
153155
zip_.c
154156
155157
OBJ = \
156158
$(OBJDIR)/add.o \
157159
$(OBJDIR)/allrepo.o \
160
+ $(OBJDIR)/attach.o \
158161
$(OBJDIR)/bag.o \
159162
$(OBJDIR)/blob.o \
160163
$(OBJDIR)/branch.o \
161164
$(OBJDIR)/browse.o \
162165
$(OBJDIR)/captcha.o \
@@ -264,16 +267,16 @@
264267
# noop
265268
266269
clean:
267270
rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
268271
rm -f translate makeheaders mkindex page_index.h headers
269
- rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
272
+ rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
270273
271274
page_index.h: $(TRANS_SRC) mkindex
272275
./mkindex $(TRANS_SRC) >$@
273276
headers: page_index.h makeheaders VERSION.h
274
- ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
277
+ ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
275278
touch headers
276279
headers: Makefile
277280
Makefile:
278281
add_.c: $(SRCDIR)/add.c translate
279282
./translate $(SRCDIR)/add.c >add_.c
@@ -287,10 +290,17 @@
287290
288291
$(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
289292
$(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
290293
291294
allrepo.h: headers
295
+attach_.c: $(SRCDIR)/attach.c translate
296
+ ./translate $(SRCDIR)/attach.c >attach_.c
297
+
298
+$(OBJDIR)/attach.o: attach_.c attach.h $(SRCDIR)/config.h
299
+ $(XTCC) -o $(OBJDIR)/attach.o -c attach_.c
300
+
301
+attach.h: headers
292302
bag_.c: $(SRCDIR)/bag.c translate
293303
./translate $(SRCDIR)/bag.c >bag_.c
294304
295305
$(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
296306
$(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
297307
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
13
14
15 SRC = \
16 $(SRCDIR)/add.c \
17 $(SRCDIR)/allrepo.c \
 
18 $(SRCDIR)/bag.c \
19 $(SRCDIR)/blob.c \
20 $(SRCDIR)/branch.c \
21 $(SRCDIR)/browse.c \
22 $(SRCDIR)/captcha.c \
@@ -83,10 +84,11 @@
83 $(SRCDIR)/zip.c
84
85 TRANS_SRC = \
86 add_.c \
87 allrepo_.c \
 
88 bag_.c \
89 blob_.c \
90 branch_.c \
91 browse_.c \
92 captcha_.c \
@@ -153,10 +155,11 @@
153 zip_.c
154
155 OBJ = \
156 $(OBJDIR)/add.o \
157 $(OBJDIR)/allrepo.o \
 
158 $(OBJDIR)/bag.o \
159 $(OBJDIR)/blob.o \
160 $(OBJDIR)/branch.o \
161 $(OBJDIR)/browse.o \
162 $(OBJDIR)/captcha.o \
@@ -264,16 +267,16 @@
264 # noop
265
266 clean:
267 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
268 rm -f translate makeheaders mkindex page_index.h headers
269 rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
270
271 page_index.h: $(TRANS_SRC) mkindex
272 ./mkindex $(TRANS_SRC) >$@
273 headers: page_index.h makeheaders VERSION.h
274 ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
275 touch headers
276 headers: Makefile
277 Makefile:
278 add_.c: $(SRCDIR)/add.c translate
279 ./translate $(SRCDIR)/add.c >add_.c
@@ -287,10 +290,17 @@
287
288 $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
289 $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
290
291 allrepo.h: headers
 
 
 
 
 
 
 
292 bag_.c: $(SRCDIR)/bag.c translate
293 ./translate $(SRCDIR)/bag.c >bag_.c
294
295 $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
296 $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
297
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
13
14
15 SRC = \
16 $(SRCDIR)/add.c \
17 $(SRCDIR)/allrepo.c \
18 $(SRCDIR)/attach.c \
19 $(SRCDIR)/bag.c \
20 $(SRCDIR)/blob.c \
21 $(SRCDIR)/branch.c \
22 $(SRCDIR)/browse.c \
23 $(SRCDIR)/captcha.c \
@@ -83,10 +84,11 @@
84 $(SRCDIR)/zip.c
85
86 TRANS_SRC = \
87 add_.c \
88 allrepo_.c \
89 attach_.c \
90 bag_.c \
91 blob_.c \
92 branch_.c \
93 browse_.c \
94 captcha_.c \
@@ -153,10 +155,11 @@
155 zip_.c
156
157 OBJ = \
158 $(OBJDIR)/add.o \
159 $(OBJDIR)/allrepo.o \
160 $(OBJDIR)/attach.o \
161 $(OBJDIR)/bag.o \
162 $(OBJDIR)/blob.o \
163 $(OBJDIR)/branch.o \
164 $(OBJDIR)/browse.o \
165 $(OBJDIR)/captcha.o \
@@ -264,16 +267,16 @@
267 # noop
268
269 clean:
270 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
271 rm -f translate makeheaders mkindex page_index.h headers
272 rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
273
274 page_index.h: $(TRANS_SRC) mkindex
275 ./mkindex $(TRANS_SRC) >$@
276 headers: page_index.h makeheaders VERSION.h
277 ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
278 touch headers
279 headers: Makefile
280 Makefile:
281 add_.c: $(SRCDIR)/add.c translate
282 ./translate $(SRCDIR)/add.c >add_.c
@@ -287,10 +290,17 @@
290
291 $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
292 $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
293
294 allrepo.h: headers
295 attach_.c: $(SRCDIR)/attach.c translate
296 ./translate $(SRCDIR)/attach.c >attach_.c
297
298 $(OBJDIR)/attach.o: attach_.c attach.h $(SRCDIR)/config.h
299 $(XTCC) -o $(OBJDIR)/attach.o -c attach_.c
300
301 attach.h: headers
302 bag_.c: $(SRCDIR)/bag.c translate
303 ./translate $(SRCDIR)/bag.c >bag_.c
304
305 $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
306 $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
307
+12 -2
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
1313
1414
1515
SRC = \
1616
$(SRCDIR)/add.c \
1717
$(SRCDIR)/allrepo.c \
18
+ $(SRCDIR)/attach.c \
1819
$(SRCDIR)/bag.c \
1920
$(SRCDIR)/blob.c \
2021
$(SRCDIR)/branch.c \
2122
$(SRCDIR)/browse.c \
2223
$(SRCDIR)/captcha.c \
@@ -83,10 +84,11 @@
8384
$(SRCDIR)/zip.c
8485
8586
TRANS_SRC = \
8687
add_.c \
8788
allrepo_.c \
89
+ attach_.c \
8890
bag_.c \
8991
blob_.c \
9092
branch_.c \
9193
browse_.c \
9294
captcha_.c \
@@ -153,10 +155,11 @@
153155
zip_.c
154156
155157
OBJ = \
156158
$(OBJDIR)/add.o \
157159
$(OBJDIR)/allrepo.o \
160
+ $(OBJDIR)/attach.o \
158161
$(OBJDIR)/bag.o \
159162
$(OBJDIR)/blob.o \
160163
$(OBJDIR)/branch.o \
161164
$(OBJDIR)/browse.o \
162165
$(OBJDIR)/captcha.o \
@@ -264,16 +267,16 @@
264267
# noop
265268
266269
clean:
267270
rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
268271
rm -f translate makeheaders mkindex page_index.h headers
269
- rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
272
+ rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
270273
271274
page_index.h: $(TRANS_SRC) mkindex
272275
./mkindex $(TRANS_SRC) >$@
273276
headers: page_index.h makeheaders VERSION.h
274
- ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
277
+ ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
275278
touch headers
276279
headers: Makefile
277280
Makefile:
278281
add_.c: $(SRCDIR)/add.c translate
279282
./translate $(SRCDIR)/add.c >add_.c
@@ -287,10 +290,17 @@
287290
288291
$(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
289292
$(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
290293
291294
allrepo.h: headers
295
+attach_.c: $(SRCDIR)/attach.c translate
296
+ ./translate $(SRCDIR)/attach.c >attach_.c
297
+
298
+$(OBJDIR)/attach.o: attach_.c attach.h $(SRCDIR)/config.h
299
+ $(XTCC) -o $(OBJDIR)/attach.o -c attach_.c
300
+
301
+attach.h: headers
292302
bag_.c: $(SRCDIR)/bag.c translate
293303
./translate $(SRCDIR)/bag.c >bag_.c
294304
295305
$(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
296306
$(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
297307
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
13
14
15 SRC = \
16 $(SRCDIR)/add.c \
17 $(SRCDIR)/allrepo.c \
 
18 $(SRCDIR)/bag.c \
19 $(SRCDIR)/blob.c \
20 $(SRCDIR)/branch.c \
21 $(SRCDIR)/browse.c \
22 $(SRCDIR)/captcha.c \
@@ -83,10 +84,11 @@
83 $(SRCDIR)/zip.c
84
85 TRANS_SRC = \
86 add_.c \
87 allrepo_.c \
 
88 bag_.c \
89 blob_.c \
90 branch_.c \
91 browse_.c \
92 captcha_.c \
@@ -153,10 +155,11 @@
153 zip_.c
154
155 OBJ = \
156 $(OBJDIR)/add.o \
157 $(OBJDIR)/allrepo.o \
 
158 $(OBJDIR)/bag.o \
159 $(OBJDIR)/blob.o \
160 $(OBJDIR)/branch.o \
161 $(OBJDIR)/browse.o \
162 $(OBJDIR)/captcha.o \
@@ -264,16 +267,16 @@
264 # noop
265
266 clean:
267 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
268 rm -f translate makeheaders mkindex page_index.h headers
269 rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
270
271 page_index.h: $(TRANS_SRC) mkindex
272 ./mkindex $(TRANS_SRC) >$@
273 headers: page_index.h makeheaders VERSION.h
274 ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
275 touch headers
276 headers: Makefile
277 Makefile:
278 add_.c: $(SRCDIR)/add.c translate
279 ./translate $(SRCDIR)/add.c >add_.c
@@ -287,10 +290,17 @@
287
288 $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
289 $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
290
291 allrepo.h: headers
 
 
 
 
 
 
 
292 bag_.c: $(SRCDIR)/bag.c translate
293 ./translate $(SRCDIR)/bag.c >bag_.c
294
295 $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
296 $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
297
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
13
14
15 SRC = \
16 $(SRCDIR)/add.c \
17 $(SRCDIR)/allrepo.c \
18 $(SRCDIR)/attach.c \
19 $(SRCDIR)/bag.c \
20 $(SRCDIR)/blob.c \
21 $(SRCDIR)/branch.c \
22 $(SRCDIR)/browse.c \
23 $(SRCDIR)/captcha.c \
@@ -83,10 +84,11 @@
84 $(SRCDIR)/zip.c
85
86 TRANS_SRC = \
87 add_.c \
88 allrepo_.c \
89 attach_.c \
90 bag_.c \
91 blob_.c \
92 branch_.c \
93 browse_.c \
94 captcha_.c \
@@ -153,10 +155,11 @@
155 zip_.c
156
157 OBJ = \
158 $(OBJDIR)/add.o \
159 $(OBJDIR)/allrepo.o \
160 $(OBJDIR)/attach.o \
161 $(OBJDIR)/bag.o \
162 $(OBJDIR)/blob.o \
163 $(OBJDIR)/branch.o \
164 $(OBJDIR)/browse.o \
165 $(OBJDIR)/captcha.o \
@@ -264,16 +267,16 @@
267 # noop
268
269 clean:
270 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
271 rm -f translate makeheaders mkindex page_index.h headers
272 rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
273
274 page_index.h: $(TRANS_SRC) mkindex
275 ./mkindex $(TRANS_SRC) >$@
276 headers: page_index.h makeheaders VERSION.h
277 ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
278 touch headers
279 headers: Makefile
280 Makefile:
281 add_.c: $(SRCDIR)/add.c translate
282 ./translate $(SRCDIR)/add.c >add_.c
@@ -287,10 +290,17 @@
290
291 $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
292 $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
293
294 allrepo.h: headers
295 attach_.c: $(SRCDIR)/attach.c translate
296 ./translate $(SRCDIR)/attach.c >attach_.c
297
298 $(OBJDIR)/attach.o: attach_.c attach.h $(SRCDIR)/config.h
299 $(XTCC) -o $(OBJDIR)/attach.o -c attach_.c
300
301 attach.h: headers
302 bag_.c: $(SRCDIR)/bag.c translate
303 ./translate $(SRCDIR)/bag.c >bag_.c
304
305 $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
306 $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
307
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
77
# "translate" and "makeheaders"
88
#
99
set src {
1010
add
1111
allrepo
12
+ attach
1213
bag
1314
blob
1415
branch
1516
browse
1617
captcha
1718
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
7 # "translate" and "makeheaders"
8 #
9 set src {
10 add
11 allrepo
 
12 bag
13 blob
14 branch
15 browse
16 captcha
17
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
7 # "translate" and "makeheaders"
8 #
9 set src {
10 add
11 allrepo
12 attach
13 bag
14 blob
15 branch
16 browse
17 captcha
18
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
77
# "translate" and "makeheaders"
88
#
99
set src {
1010
add
1111
allrepo
12
+ attach
1213
bag
1314
blob
1415
branch
1516
browse
1617
captcha
1718
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
7 # "translate" and "makeheaders"
8 #
9 set src {
10 add
11 allrepo
 
12 bag
13 blob
14 branch
15 browse
16 captcha
17
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
7 # "translate" and "makeheaders"
8 #
9 set src {
10 add
11 allrepo
12 attach
13 bag
14 blob
15 branch
16 browse
17 captcha
18
+122 -91
--- src/manifest.c
+++ src/manifest.c
@@ -37,67 +37,56 @@
3737
#define CFTYPE_MANIFEST 1
3838
#define CFTYPE_CLUSTER 2
3939
#define CFTYPE_CONTROL 3
4040
#define CFTYPE_WIKI 4
4141
#define CFTYPE_TICKET 5
42
-
43
-/*
44
-** Mode parameter values
45
-*/
46
-#define CFMODE_READ 1
47
-#define CFMODE_APPEND 2
48
-#define CFMODE_WRITE 3
42
+#define CFTYPE_ATTACHMENT 6
4943
5044
/*
5145
** A parsed manifest or cluster.
5246
*/
5347
struct Manifest {
5448
Blob content; /* The original content blob */
55
- int type; /* Type of file */
56
- int mode; /* Access mode */
57
- char *zComment; /* Decoded comment */
58
- double rDate; /* Time in the "D" line */
59
- char *zUser; /* Name of the user */
60
- char *zRepoCksum; /* MD5 checksum of the baseline content */
61
- char *zWiki; /* Text of the wiki page */
62
- char *zWikiTitle; /* Name of the wiki page */
63
- char *zTicketUuid; /* UUID for a ticket */
64
- int nFile; /* Number of F lines */
49
+ int type; /* Type of artifact. One of CFTYPE_xxxxx */
50
+ char *zComment; /* Decoded comment. The C card. */
51
+ double rDate; /* Date and time from D card. 0.0 if no D card. */
52
+ char *zUser; /* Name of the user from the U card. */
53
+ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
54
+ char *zWiki; /* Text of the wiki page. W card. */
55
+ char *zWikiTitle; /* Name of the wiki page. L card. */
56
+ char *zTicketUuid; /* UUID for a ticket. K card. */
57
+ char *zAttachName; /* Filename of an attachment. A card. */
58
+ char *zAttachSrc; /* UUID of document being attached. A card. */
59
+ char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
60
+ int nFile; /* Number of F cards */
6561
int nFileAlloc; /* Slots allocated in aFile[] */
6662
struct {
6763
char *zName; /* Name of a file */
6864
char *zUuid; /* UUID of the file */
6965
char *zPerm; /* File permissions */
7066
char *zPrior; /* Prior name if the name was changed */
7167
int iRename; /* index of renamed name in prior/next manifest */
72
- } *aFile;
73
- int nParent; /* Number of parents */
68
+ } *aFile; /* One entry for each F card */
69
+ int nParent; /* Number of parents. */
7470
int nParentAlloc; /* Slots allocated in azParent[] */
75
- char **azParent; /* UUIDs of parents */
71
+ char **azParent; /* UUIDs of parents. One for each P card argument */
7672
int nCChild; /* Number of cluster children */
7773
int nCChildAlloc; /* Number of closts allocated in azCChild[] */
78
- char **azCChild; /* UUIDs of referenced objects in a cluster */
79
- int nTag; /* Number of T lines */
74
+ char **azCChild; /* UUIDs of referenced objects in a cluster. M cards */
75
+ int nTag; /* Number of T Cards */
8076
int nTagAlloc; /* Slots allocated in aTag[] */
8177
struct {
8278
char *zName; /* Name of the tag */
8379
char *zUuid; /* UUID that the tag is applied to */
8480
char *zValue; /* Value if the tag is really a property */
85
- } *aTag;
86
- int nField; /* Number of J lines */
81
+ } *aTag; /* One for each T card */
82
+ int nField; /* Number of J cards */
8783
int nFieldAlloc; /* Slots allocated in aField[] */
8884
struct {
8985
char *zName; /* Key or field name */
9086
char *zValue; /* Value of the field */
91
- } *aField;
92
- int nAttach; /* Number of A lines */
93
- int nAttachAlloc; /* Slots allocated in aAttach[] */
94
- struct {
95
- char *zUuid; /* UUID of the attachment */
96
- char *zName; /* Name of the attachment */
97
- char *zDesc; /* Description of the attachment */
98
- } *aAttach;
87
+ } *aField; /* One for each J card */
9988
};
10089
#endif
10190
10291
10392
/*
@@ -108,11 +97,10 @@
10897
free(p->aFile);
10998
free(p->azParent);
11099
free(p->azCChild);
111100
free(p->aTag);
112101
free(p->aField);
113
- free(p->aAttach);
114102
memset(p, 0, sizeof(*p));
115103
}
116104
117105
/*
118106
** Parse a blob into a Manifest object. The Manifest object
@@ -178,44 +166,43 @@
178166
cPrevType = z[0];
179167
seenHeader = 1;
180168
if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
181169
switch( z[0] ){
182170
/*
183
- ** A <uuid> <filename> <description>
171
+ ** A <filename> <target> ?<source>?
184172
**
185173
** Identifies an attachment to either a wiki page or a ticket.
186
- ** <uuid> is the artifact that is the attachment.
174
+ ** <source> is the artifact that is the attachment. <source>
175
+ ** is omitted to delete an attachment. <target> is the name of
176
+ ** a wiki page or ticket to which that attachment is connected.
187177
*/
188178
case 'A': {
189
- char *zName, *zUuid, *zDesc;
179
+ char *zName, *zTarget, *zSrc;
190180
md5sum_step_text(blob_buffer(&line), blob_size(&line));
191181
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
192182
if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
193
- if( blob_token(&line, &a3)==0 ) goto manifest_syntax_error;
194
- zUuid = blob_terminate(&a1);
195
- zName = blob_terminate(&a2);
196
- zDesc = blob_terminate(&a3);
197
- if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
198
- if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
183
+ if( p->zAttachName!=0 ) goto manifest_syntax_error;
184
+ zName = blob_terminate(&a1);
185
+ zTarget = blob_terminate(&a2);
186
+ blob_token(&line, &a3);
187
+ zSrc = blob_terminate(&a3);
199188
defossilize(zName);
200189
if( !file_is_simple_pathname(zName) ){
201190
goto manifest_syntax_error;
202191
}
203
- defossilize(zDesc);
204
- if( p->nAttach>=p->nAttachAlloc ){
205
- p->nAttachAlloc = p->nAttachAlloc*2 + 10;
206
- p->aAttach = realloc(p->aAttach,
207
- p->nAttachAlloc*sizeof(p->aAttach[0]) );
208
- if( p->aAttach==0 ) fossil_panic("out of memory");
209
- }
210
- i = p->nAttach++;
211
- p->aAttach[i].zUuid = zUuid;
212
- p->aAttach[i].zName = zName;
213
- p->aAttach[i].zDesc = zDesc;
214
- if( i>0 && strcmp(p->aAttach[i-1].zUuid, zUuid)>=0 ){
192
+ defossilize(zTarget);
193
+ if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
194
+ && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
195
+ goto manifest_syntax_error;
196
+ }
197
+ if( blob_size(&a3)>0
198
+ && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
215199
goto manifest_syntax_error;
216200
}
201
+ p->zAttachName = (char*)file_tail(zName);
202
+ p->zAttachSrc = zSrc;
203
+ p->zAttachTarget = zTarget;
217204
break;
218205
}
219206
220207
/*
221208
** C <comment>
@@ -247,33 +234,10 @@
247234
if( p->rDate!=0.0 ) goto manifest_syntax_error;
248235
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
249236
if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
250237
zDate = blob_terminate(&a1);
251238
p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
252
- break;
253
- }
254
-
255
- /*
256
- ** E <mode>
257
- **
258
- ** Access mode. <mode> can be one of "read", "append",
259
- ** or "write".
260
- */
261
- case 'E': {
262
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
263
- if( p->mode!=0 ) goto manifest_syntax_error;
264
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
265
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
266
- if( blob_eq(&a1, "write") ){
267
- p->mode = CFMODE_WRITE;
268
- }else if( blob_eq(&a1, "append") ){
269
- p->mode = CFMODE_APPEND;
270
- }else if( blob_eq(&a1, "read") ){
271
- p->mode = CFMODE_READ;
272
- }else{
273
- goto manifest_syntax_error;
274
- }
275239
break;
276240
}
277241
278242
/*
279243
** F <filename> <uuid> ?<permissions>? ?<old-name>?
@@ -608,72 +572,75 @@
608572
if( p->nFile>0 || p->zRepoCksum!=0 ){
609573
if( p->nCChild>0 ) goto manifest_syntax_error;
610574
if( p->rDate==0.0 ) goto manifest_syntax_error;
611575
if( p->nField>0 ) goto manifest_syntax_error;
612576
if( p->zTicketUuid ) goto manifest_syntax_error;
613
- if( p->nAttach>0 ) goto manifest_syntax_error;
614577
if( p->zWiki ) goto manifest_syntax_error;
615578
if( p->zWikiTitle ) goto manifest_syntax_error;
616579
if( p->zTicketUuid ) goto manifest_syntax_error;
580
+ if( p->zAttachName ) goto manifest_syntax_error;
617581
p->type = CFTYPE_MANIFEST;
618582
}else if( p->nCChild>0 ){
619583
if( p->rDate>0.0 ) goto manifest_syntax_error;
620584
if( p->zComment!=0 ) goto manifest_syntax_error;
621585
if( p->zUser!=0 ) goto manifest_syntax_error;
622586
if( p->nTag>0 ) goto manifest_syntax_error;
623587
if( p->nParent>0 ) goto manifest_syntax_error;
624
- if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
625588
if( p->nField>0 ) goto manifest_syntax_error;
626589
if( p->zTicketUuid ) goto manifest_syntax_error;
627
- if( p->nAttach>0 ) goto manifest_syntax_error;
628590
if( p->zWiki ) goto manifest_syntax_error;
629591
if( p->zWikiTitle ) goto manifest_syntax_error;
592
+ if( p->zAttachName ) goto manifest_syntax_error;
630593
if( !seenZ ) goto manifest_syntax_error;
631594
p->type = CFTYPE_CLUSTER;
632595
}else if( p->nField>0 ){
633596
if( p->rDate==0.0 ) goto manifest_syntax_error;
634
- if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
635597
if( p->zWiki ) goto manifest_syntax_error;
636598
if( p->zWikiTitle ) goto manifest_syntax_error;
637599
if( p->nCChild>0 ) goto manifest_syntax_error;
638600
if( p->nTag>0 ) goto manifest_syntax_error;
639601
if( p->zTicketUuid==0 ) goto manifest_syntax_error;
640602
if( p->zUser==0 ) goto manifest_syntax_error;
603
+ if( p->zAttachName ) goto manifest_syntax_error;
641604
if( !seenZ ) goto manifest_syntax_error;
642605
p->type = CFTYPE_TICKET;
643606
}else if( p->zWiki!=0 ){
644607
if( p->rDate==0.0 ) goto manifest_syntax_error;
645
- if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
646608
if( p->nCChild>0 ) goto manifest_syntax_error;
647609
if( p->nTag>0 ) goto manifest_syntax_error;
648610
if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
649611
if( p->zWikiTitle==0 ) goto manifest_syntax_error;
612
+ if( p->zAttachName ) goto manifest_syntax_error;
650613
if( !seenZ ) goto manifest_syntax_error;
651614
p->type = CFTYPE_WIKI;
652615
}else if( p->nTag>0 ){
653616
if( p->rDate<=0.0 ) goto manifest_syntax_error;
654
- if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
655617
if( p->nParent>0 ) goto manifest_syntax_error;
656
- if( p->nAttach>0 ) goto manifest_syntax_error;
657
- if( p->nField>0 ) goto manifest_syntax_error;
658
- if( p->zWiki ) goto manifest_syntax_error;
659618
if( p->zWikiTitle ) goto manifest_syntax_error;
660619
if( p->zTicketUuid ) goto manifest_syntax_error;
620
+ if( p->zAttachName ) goto manifest_syntax_error;
661621
if( !seenZ ) goto manifest_syntax_error;
662622
p->type = CFTYPE_CONTROL;
663
- }else{
623
+ }else if( p->zAttachName ){
664624
if( p->nCChild>0 ) goto manifest_syntax_error;
665625
if( p->rDate==0.0 ) goto manifest_syntax_error;
626
+ if( p->zTicketUuid ) goto manifest_syntax_error;
627
+ if( p->zWikiTitle ) goto manifest_syntax_error;
628
+ if( !seenZ ) goto manifest_syntax_error;
629
+ p->type = CFTYPE_ATTACHMENT;
630
+ }else{
631
+ if( p->nCChild>0 ) goto manifest_syntax_error;
632
+ if( p->rDate<=0.0 ) goto manifest_syntax_error;
633
+ if( p->nParent>0 ) goto manifest_syntax_error;
666634
if( p->nField>0 ) goto manifest_syntax_error;
667635
if( p->zTicketUuid ) goto manifest_syntax_error;
668
- if( p->nAttach>0 ) goto manifest_syntax_error;
669636
if( p->zWiki ) goto manifest_syntax_error;
670637
if( p->zWikiTitle ) goto manifest_syntax_error;
671638
if( p->zTicketUuid ) goto manifest_syntax_error;
639
+ if( p->zAttachName ) goto manifest_syntax_error;
672640
p->type = CFTYPE_MANIFEST;
673641
}
674
-
675642
md5sum_init();
676643
return 1;
677644
678645
manifest_syntax_error:
679646
/*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
@@ -1032,10 +999,11 @@
1032999
return 0;
10331000
}
10341001
db_begin_transaction();
10351002
if( m.type==CFTYPE_MANIFEST ){
10361003
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1004
+ char *zCom;
10371005
for(i=0; i<m.nParent; i++){
10381006
int pid = uuid_to_rid(m.azParent[i], 1);
10391007
db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
10401008
"VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
10411009
if( i==0 ){
@@ -1065,10 +1033,14 @@
10651033
rid, m.zUser, m.zComment,
10661034
TAG_BGCOLOR, rid,
10671035
TAG_USER, rid,
10681036
TAG_COMMENT, rid
10691037
);
1038
+ zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
1039
+ " WHERE rowid=last_insert_rowid()");
1040
+ wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE);
1041
+ free(zCom);
10701042
}
10711043
}
10721044
if( m.type==CFTYPE_CLUSTER ){
10731045
tag_insert("cluster", 1, 0, rid, m.rDate, rid);
10741046
for(i=0; i<m.nCChild; i++){
@@ -1108,11 +1080,16 @@
11081080
if( m.type==CFTYPE_WIKI ){
11091081
char *zTag = mprintf("wiki-%s", m.zWikiTitle);
11101082
int tagid = tag_findid(zTag, 1);
11111083
int prior;
11121084
char *zComment;
1113
- tag_insert(zTag, 1, 0, rid, m.rDate, rid);
1085
+ int nWiki;
1086
+ char zLength[40];
1087
+ while( isspace(m.zWiki[0]) ) m.zWiki++;
1088
+ nWiki = strlen(m.zWiki);
1089
+ sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1090
+ tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
11141091
free(zTag);
11151092
prior = db_int(0,
11161093
"SELECT rid FROM tagxref"
11171094
" WHERE tagid=%d AND mtime<%.17g"
11181095
" ORDER BY mtime DESC",
@@ -1119,11 +1096,15 @@
11191096
tagid, m.rDate
11201097
);
11211098
if( prior ){
11221099
content_deltify(prior, rid, 0);
11231100
}
1124
- zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1101
+ if( nWiki>0 ){
1102
+ zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1103
+ }else{
1104
+ zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
1105
+ }
11251106
db_multi_exec(
11261107
"REPLACE INTO event(type,mtime,objid,user,comment,"
11271108
" bgcolor,euser,ecomment)"
11281109
"VALUES('w',%.17g,%d,%Q,%Q,"
11291110
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1144,10 +1125,60 @@
11441125
zTag = mprintf("tkt-%s", m.zTicketUuid);
11451126
tag_insert(zTag, 1, 0, rid, m.rDate, rid);
11461127
free(zTag);
11471128
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
11481129
m.zTicketUuid);
1130
+ }
1131
+ if( m.type==CFTYPE_ATTACHMENT ){
1132
+ db_multi_exec(
1133
+ "INSERT INTO attachment(attachid, mtime, src, target,"
1134
+ "filename, comment, user)"
1135
+ "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
1136
+ rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
1137
+ (m.zComment ? m.zComment : ""), m.zUser
1138
+ );
1139
+ db_multi_exec(
1140
+ "UPDATE attachment SET isLatest = (mtime=="
1141
+ "(SELECT max(mtime) FROM attachment"
1142
+ " WHERE target=%Q AND filename=%Q))"
1143
+ " WHERE target=%Q AND filename=%Q",
1144
+ m.zAttachTarget, m.zAttachName,
1145
+ m.zAttachTarget, m.zAttachName
1146
+ );
1147
+ if( strlen(m.zAttachTarget)!=UUID_SIZE
1148
+ || !validate16(m.zAttachTarget, UUID_SIZE)
1149
+ ){
1150
+ char *zComment;
1151
+ if( m.zAttachSrc && m.zAttachSrc[0] ){
1152
+ zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
1153
+ m.zAttachName, m.zAttachTarget);
1154
+ }else{
1155
+ zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
1156
+ m.zAttachName, m.zAttachTarget);
1157
+ }
1158
+ db_multi_exec(
1159
+ "REPLACE INTO event(type,mtime,objid,user,comment)"
1160
+ "VALUES('w',%.17g,%d,%Q,%Q)",
1161
+ m.rDate, rid, m.zUser, zComment
1162
+ );
1163
+ free(zComment);
1164
+ }else{
1165
+ char *zComment;
1166
+ if( m.zAttachSrc && m.zAttachSrc[0] ){
1167
+ zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
1168
+ m.zAttachName, m.zAttachTarget);
1169
+ }else{
1170
+ zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
1171
+ m.zAttachName, m.zAttachTarget);
1172
+ }
1173
+ db_multi_exec(
1174
+ "REPLACE INTO event(type,mtime,objid,user,comment)"
1175
+ "VALUES('t',%.17g,%d,%Q,%Q)",
1176
+ m.rDate, rid, m.zUser, zComment
1177
+ );
1178
+ free(zComment);
1179
+ }
11491180
}
11501181
db_end_transaction(0);
11511182
manifest_clear(&m);
11521183
return 1;
11531184
}
11541185
--- src/manifest.c
+++ src/manifest.c
@@ -37,67 +37,56 @@
37 #define CFTYPE_MANIFEST 1
38 #define CFTYPE_CLUSTER 2
39 #define CFTYPE_CONTROL 3
40 #define CFTYPE_WIKI 4
41 #define CFTYPE_TICKET 5
42
43 /*
44 ** Mode parameter values
45 */
46 #define CFMODE_READ 1
47 #define CFMODE_APPEND 2
48 #define CFMODE_WRITE 3
49
50 /*
51 ** A parsed manifest or cluster.
52 */
53 struct Manifest {
54 Blob content; /* The original content blob */
55 int type; /* Type of file */
56 int mode; /* Access mode */
57 char *zComment; /* Decoded comment */
58 double rDate; /* Time in the "D" line */
59 char *zUser; /* Name of the user */
60 char *zRepoCksum; /* MD5 checksum of the baseline content */
61 char *zWiki; /* Text of the wiki page */
62 char *zWikiTitle; /* Name of the wiki page */
63 char *zTicketUuid; /* UUID for a ticket */
64 int nFile; /* Number of F lines */
 
 
65 int nFileAlloc; /* Slots allocated in aFile[] */
66 struct {
67 char *zName; /* Name of a file */
68 char *zUuid; /* UUID of the file */
69 char *zPerm; /* File permissions */
70 char *zPrior; /* Prior name if the name was changed */
71 int iRename; /* index of renamed name in prior/next manifest */
72 } *aFile;
73 int nParent; /* Number of parents */
74 int nParentAlloc; /* Slots allocated in azParent[] */
75 char **azParent; /* UUIDs of parents */
76 int nCChild; /* Number of cluster children */
77 int nCChildAlloc; /* Number of closts allocated in azCChild[] */
78 char **azCChild; /* UUIDs of referenced objects in a cluster */
79 int nTag; /* Number of T lines */
80 int nTagAlloc; /* Slots allocated in aTag[] */
81 struct {
82 char *zName; /* Name of the tag */
83 char *zUuid; /* UUID that the tag is applied to */
84 char *zValue; /* Value if the tag is really a property */
85 } *aTag;
86 int nField; /* Number of J lines */
87 int nFieldAlloc; /* Slots allocated in aField[] */
88 struct {
89 char *zName; /* Key or field name */
90 char *zValue; /* Value of the field */
91 } *aField;
92 int nAttach; /* Number of A lines */
93 int nAttachAlloc; /* Slots allocated in aAttach[] */
94 struct {
95 char *zUuid; /* UUID of the attachment */
96 char *zName; /* Name of the attachment */
97 char *zDesc; /* Description of the attachment */
98 } *aAttach;
99 };
100 #endif
101
102
103 /*
@@ -108,11 +97,10 @@
108 free(p->aFile);
109 free(p->azParent);
110 free(p->azCChild);
111 free(p->aTag);
112 free(p->aField);
113 free(p->aAttach);
114 memset(p, 0, sizeof(*p));
115 }
116
117 /*
118 ** Parse a blob into a Manifest object. The Manifest object
@@ -178,44 +166,43 @@
178 cPrevType = z[0];
179 seenHeader = 1;
180 if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
181 switch( z[0] ){
182 /*
183 ** A <uuid> <filename> <description>
184 **
185 ** Identifies an attachment to either a wiki page or a ticket.
186 ** <uuid> is the artifact that is the attachment.
 
 
187 */
188 case 'A': {
189 char *zName, *zUuid, *zDesc;
190 md5sum_step_text(blob_buffer(&line), blob_size(&line));
191 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
192 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
193 if( blob_token(&line, &a3)==0 ) goto manifest_syntax_error;
194 zUuid = blob_terminate(&a1);
195 zName = blob_terminate(&a2);
196 zDesc = blob_terminate(&a3);
197 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
198 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
199 defossilize(zName);
200 if( !file_is_simple_pathname(zName) ){
201 goto manifest_syntax_error;
202 }
203 defossilize(zDesc);
204 if( p->nAttach>=p->nAttachAlloc ){
205 p->nAttachAlloc = p->nAttachAlloc*2 + 10;
206 p->aAttach = realloc(p->aAttach,
207 p->nAttachAlloc*sizeof(p->aAttach[0]) );
208 if( p->aAttach==0 ) fossil_panic("out of memory");
209 }
210 i = p->nAttach++;
211 p->aAttach[i].zUuid = zUuid;
212 p->aAttach[i].zName = zName;
213 p->aAttach[i].zDesc = zDesc;
214 if( i>0 && strcmp(p->aAttach[i-1].zUuid, zUuid)>=0 ){
215 goto manifest_syntax_error;
216 }
 
 
 
217 break;
218 }
219
220 /*
221 ** C <comment>
@@ -247,33 +234,10 @@
247 if( p->rDate!=0.0 ) goto manifest_syntax_error;
248 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
249 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
250 zDate = blob_terminate(&a1);
251 p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
252 break;
253 }
254
255 /*
256 ** E <mode>
257 **
258 ** Access mode. <mode> can be one of "read", "append",
259 ** or "write".
260 */
261 case 'E': {
262 md5sum_step_text(blob_buffer(&line), blob_size(&line));
263 if( p->mode!=0 ) goto manifest_syntax_error;
264 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
265 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
266 if( blob_eq(&a1, "write") ){
267 p->mode = CFMODE_WRITE;
268 }else if( blob_eq(&a1, "append") ){
269 p->mode = CFMODE_APPEND;
270 }else if( blob_eq(&a1, "read") ){
271 p->mode = CFMODE_READ;
272 }else{
273 goto manifest_syntax_error;
274 }
275 break;
276 }
277
278 /*
279 ** F <filename> <uuid> ?<permissions>? ?<old-name>?
@@ -608,72 +572,75 @@
608 if( p->nFile>0 || p->zRepoCksum!=0 ){
609 if( p->nCChild>0 ) goto manifest_syntax_error;
610 if( p->rDate==0.0 ) goto manifest_syntax_error;
611 if( p->nField>0 ) goto manifest_syntax_error;
612 if( p->zTicketUuid ) goto manifest_syntax_error;
613 if( p->nAttach>0 ) goto manifest_syntax_error;
614 if( p->zWiki ) goto manifest_syntax_error;
615 if( p->zWikiTitle ) goto manifest_syntax_error;
616 if( p->zTicketUuid ) goto manifest_syntax_error;
 
617 p->type = CFTYPE_MANIFEST;
618 }else if( p->nCChild>0 ){
619 if( p->rDate>0.0 ) goto manifest_syntax_error;
620 if( p->zComment!=0 ) goto manifest_syntax_error;
621 if( p->zUser!=0 ) goto manifest_syntax_error;
622 if( p->nTag>0 ) goto manifest_syntax_error;
623 if( p->nParent>0 ) goto manifest_syntax_error;
624 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
625 if( p->nField>0 ) goto manifest_syntax_error;
626 if( p->zTicketUuid ) goto manifest_syntax_error;
627 if( p->nAttach>0 ) goto manifest_syntax_error;
628 if( p->zWiki ) goto manifest_syntax_error;
629 if( p->zWikiTitle ) goto manifest_syntax_error;
 
630 if( !seenZ ) goto manifest_syntax_error;
631 p->type = CFTYPE_CLUSTER;
632 }else if( p->nField>0 ){
633 if( p->rDate==0.0 ) goto manifest_syntax_error;
634 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
635 if( p->zWiki ) goto manifest_syntax_error;
636 if( p->zWikiTitle ) goto manifest_syntax_error;
637 if( p->nCChild>0 ) goto manifest_syntax_error;
638 if( p->nTag>0 ) goto manifest_syntax_error;
639 if( p->zTicketUuid==0 ) goto manifest_syntax_error;
640 if( p->zUser==0 ) goto manifest_syntax_error;
 
641 if( !seenZ ) goto manifest_syntax_error;
642 p->type = CFTYPE_TICKET;
643 }else if( p->zWiki!=0 ){
644 if( p->rDate==0.0 ) goto manifest_syntax_error;
645 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
646 if( p->nCChild>0 ) goto manifest_syntax_error;
647 if( p->nTag>0 ) goto manifest_syntax_error;
648 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
649 if( p->zWikiTitle==0 ) goto manifest_syntax_error;
 
650 if( !seenZ ) goto manifest_syntax_error;
651 p->type = CFTYPE_WIKI;
652 }else if( p->nTag>0 ){
653 if( p->rDate<=0.0 ) goto manifest_syntax_error;
654 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
655 if( p->nParent>0 ) goto manifest_syntax_error;
656 if( p->nAttach>0 ) goto manifest_syntax_error;
657 if( p->nField>0 ) goto manifest_syntax_error;
658 if( p->zWiki ) goto manifest_syntax_error;
659 if( p->zWikiTitle ) goto manifest_syntax_error;
660 if( p->zTicketUuid ) goto manifest_syntax_error;
 
661 if( !seenZ ) goto manifest_syntax_error;
662 p->type = CFTYPE_CONTROL;
663 }else{
664 if( p->nCChild>0 ) goto manifest_syntax_error;
665 if( p->rDate==0.0 ) goto manifest_syntax_error;
 
 
 
 
 
 
 
 
666 if( p->nField>0 ) goto manifest_syntax_error;
667 if( p->zTicketUuid ) goto manifest_syntax_error;
668 if( p->nAttach>0 ) goto manifest_syntax_error;
669 if( p->zWiki ) goto manifest_syntax_error;
670 if( p->zWikiTitle ) goto manifest_syntax_error;
671 if( p->zTicketUuid ) goto manifest_syntax_error;
 
672 p->type = CFTYPE_MANIFEST;
673 }
674
675 md5sum_init();
676 return 1;
677
678 manifest_syntax_error:
679 /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
@@ -1032,10 +999,11 @@
1032 return 0;
1033 }
1034 db_begin_transaction();
1035 if( m.type==CFTYPE_MANIFEST ){
1036 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
 
1037 for(i=0; i<m.nParent; i++){
1038 int pid = uuid_to_rid(m.azParent[i], 1);
1039 db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
1040 "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
1041 if( i==0 ){
@@ -1065,10 +1033,14 @@
1065 rid, m.zUser, m.zComment,
1066 TAG_BGCOLOR, rid,
1067 TAG_USER, rid,
1068 TAG_COMMENT, rid
1069 );
 
 
 
 
1070 }
1071 }
1072 if( m.type==CFTYPE_CLUSTER ){
1073 tag_insert("cluster", 1, 0, rid, m.rDate, rid);
1074 for(i=0; i<m.nCChild; i++){
@@ -1108,11 +1080,16 @@
1108 if( m.type==CFTYPE_WIKI ){
1109 char *zTag = mprintf("wiki-%s", m.zWikiTitle);
1110 int tagid = tag_findid(zTag, 1);
1111 int prior;
1112 char *zComment;
1113 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
 
 
 
 
 
1114 free(zTag);
1115 prior = db_int(0,
1116 "SELECT rid FROM tagxref"
1117 " WHERE tagid=%d AND mtime<%.17g"
1118 " ORDER BY mtime DESC",
@@ -1119,11 +1096,15 @@
1119 tagid, m.rDate
1120 );
1121 if( prior ){
1122 content_deltify(prior, rid, 0);
1123 }
1124 zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
 
 
 
 
1125 db_multi_exec(
1126 "REPLACE INTO event(type,mtime,objid,user,comment,"
1127 " bgcolor,euser,ecomment)"
1128 "VALUES('w',%.17g,%d,%Q,%Q,"
1129 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1144,10 +1125,60 @@
1144 zTag = mprintf("tkt-%s", m.zTicketUuid);
1145 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
1146 free(zTag);
1147 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
1148 m.zTicketUuid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1149 }
1150 db_end_transaction(0);
1151 manifest_clear(&m);
1152 return 1;
1153 }
1154
--- src/manifest.c
+++ src/manifest.c
@@ -37,67 +37,56 @@
37 #define CFTYPE_MANIFEST 1
38 #define CFTYPE_CLUSTER 2
39 #define CFTYPE_CONTROL 3
40 #define CFTYPE_WIKI 4
41 #define CFTYPE_TICKET 5
42 #define CFTYPE_ATTACHMENT 6
 
 
 
 
 
 
43
44 /*
45 ** A parsed manifest or cluster.
46 */
47 struct Manifest {
48 Blob content; /* The original content blob */
49 int type; /* Type of artifact. One of CFTYPE_xxxxx */
50 char *zComment; /* Decoded comment. The C card. */
51 double rDate; /* Date and time from D card. 0.0 if no D card. */
52 char *zUser; /* Name of the user from the U card. */
53 char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
54 char *zWiki; /* Text of the wiki page. W card. */
55 char *zWikiTitle; /* Name of the wiki page. L card. */
56 char *zTicketUuid; /* UUID for a ticket. K card. */
57 char *zAttachName; /* Filename of an attachment. A card. */
58 char *zAttachSrc; /* UUID of document being attached. A card. */
59 char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
60 int nFile; /* Number of F cards */
61 int nFileAlloc; /* Slots allocated in aFile[] */
62 struct {
63 char *zName; /* Name of a file */
64 char *zUuid; /* UUID of the file */
65 char *zPerm; /* File permissions */
66 char *zPrior; /* Prior name if the name was changed */
67 int iRename; /* index of renamed name in prior/next manifest */
68 } *aFile; /* One entry for each F card */
69 int nParent; /* Number of parents. */
70 int nParentAlloc; /* Slots allocated in azParent[] */
71 char **azParent; /* UUIDs of parents. One for each P card argument */
72 int nCChild; /* Number of cluster children */
73 int nCChildAlloc; /* Number of closts allocated in azCChild[] */
74 char **azCChild; /* UUIDs of referenced objects in a cluster. M cards */
75 int nTag; /* Number of T Cards */
76 int nTagAlloc; /* Slots allocated in aTag[] */
77 struct {
78 char *zName; /* Name of the tag */
79 char *zUuid; /* UUID that the tag is applied to */
80 char *zValue; /* Value if the tag is really a property */
81 } *aTag; /* One for each T card */
82 int nField; /* Number of J cards */
83 int nFieldAlloc; /* Slots allocated in aField[] */
84 struct {
85 char *zName; /* Key or field name */
86 char *zValue; /* Value of the field */
87 } *aField; /* One for each J card */
 
 
 
 
 
 
 
88 };
89 #endif
90
91
92 /*
@@ -108,11 +97,10 @@
97 free(p->aFile);
98 free(p->azParent);
99 free(p->azCChild);
100 free(p->aTag);
101 free(p->aField);
 
102 memset(p, 0, sizeof(*p));
103 }
104
105 /*
106 ** Parse a blob into a Manifest object. The Manifest object
@@ -178,44 +166,43 @@
166 cPrevType = z[0];
167 seenHeader = 1;
168 if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
169 switch( z[0] ){
170 /*
171 ** A <filename> <target> ?<source>?
172 **
173 ** Identifies an attachment to either a wiki page or a ticket.
174 ** <source> is the artifact that is the attachment. <source>
175 ** is omitted to delete an attachment. <target> is the name of
176 ** a wiki page or ticket to which that attachment is connected.
177 */
178 case 'A': {
179 char *zName, *zTarget, *zSrc;
180 md5sum_step_text(blob_buffer(&line), blob_size(&line));
181 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
182 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
183 if( p->zAttachName!=0 ) goto manifest_syntax_error;
184 zName = blob_terminate(&a1);
185 zTarget = blob_terminate(&a2);
186 blob_token(&line, &a3);
187 zSrc = blob_terminate(&a3);
 
188 defossilize(zName);
189 if( !file_is_simple_pathname(zName) ){
190 goto manifest_syntax_error;
191 }
192 defossilize(zTarget);
193 if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
194 && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
195 goto manifest_syntax_error;
196 }
197 if( blob_size(&a3)>0
198 && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
 
 
 
 
 
199 goto manifest_syntax_error;
200 }
201 p->zAttachName = (char*)file_tail(zName);
202 p->zAttachSrc = zSrc;
203 p->zAttachTarget = zTarget;
204 break;
205 }
206
207 /*
208 ** C <comment>
@@ -247,33 +234,10 @@
234 if( p->rDate!=0.0 ) goto manifest_syntax_error;
235 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
236 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
237 zDate = blob_terminate(&a1);
238 p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239 break;
240 }
241
242 /*
243 ** F <filename> <uuid> ?<permissions>? ?<old-name>?
@@ -608,72 +572,75 @@
572 if( p->nFile>0 || p->zRepoCksum!=0 ){
573 if( p->nCChild>0 ) goto manifest_syntax_error;
574 if( p->rDate==0.0 ) goto manifest_syntax_error;
575 if( p->nField>0 ) goto manifest_syntax_error;
576 if( p->zTicketUuid ) goto manifest_syntax_error;
 
577 if( p->zWiki ) goto manifest_syntax_error;
578 if( p->zWikiTitle ) goto manifest_syntax_error;
579 if( p->zTicketUuid ) goto manifest_syntax_error;
580 if( p->zAttachName ) goto manifest_syntax_error;
581 p->type = CFTYPE_MANIFEST;
582 }else if( p->nCChild>0 ){
583 if( p->rDate>0.0 ) goto manifest_syntax_error;
584 if( p->zComment!=0 ) goto manifest_syntax_error;
585 if( p->zUser!=0 ) goto manifest_syntax_error;
586 if( p->nTag>0 ) goto manifest_syntax_error;
587 if( p->nParent>0 ) goto manifest_syntax_error;
 
588 if( p->nField>0 ) goto manifest_syntax_error;
589 if( p->zTicketUuid ) goto manifest_syntax_error;
 
590 if( p->zWiki ) goto manifest_syntax_error;
591 if( p->zWikiTitle ) goto manifest_syntax_error;
592 if( p->zAttachName ) goto manifest_syntax_error;
593 if( !seenZ ) goto manifest_syntax_error;
594 p->type = CFTYPE_CLUSTER;
595 }else if( p->nField>0 ){
596 if( p->rDate==0.0 ) goto manifest_syntax_error;
 
597 if( p->zWiki ) goto manifest_syntax_error;
598 if( p->zWikiTitle ) goto manifest_syntax_error;
599 if( p->nCChild>0 ) goto manifest_syntax_error;
600 if( p->nTag>0 ) goto manifest_syntax_error;
601 if( p->zTicketUuid==0 ) goto manifest_syntax_error;
602 if( p->zUser==0 ) goto manifest_syntax_error;
603 if( p->zAttachName ) goto manifest_syntax_error;
604 if( !seenZ ) goto manifest_syntax_error;
605 p->type = CFTYPE_TICKET;
606 }else if( p->zWiki!=0 ){
607 if( p->rDate==0.0 ) goto manifest_syntax_error;
 
608 if( p->nCChild>0 ) goto manifest_syntax_error;
609 if( p->nTag>0 ) goto manifest_syntax_error;
610 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
611 if( p->zWikiTitle==0 ) goto manifest_syntax_error;
612 if( p->zAttachName ) goto manifest_syntax_error;
613 if( !seenZ ) goto manifest_syntax_error;
614 p->type = CFTYPE_WIKI;
615 }else if( p->nTag>0 ){
616 if( p->rDate<=0.0 ) goto manifest_syntax_error;
 
617 if( p->nParent>0 ) goto manifest_syntax_error;
 
 
 
618 if( p->zWikiTitle ) goto manifest_syntax_error;
619 if( p->zTicketUuid ) goto manifest_syntax_error;
620 if( p->zAttachName ) goto manifest_syntax_error;
621 if( !seenZ ) goto manifest_syntax_error;
622 p->type = CFTYPE_CONTROL;
623 }else if( p->zAttachName ){
624 if( p->nCChild>0 ) goto manifest_syntax_error;
625 if( p->rDate==0.0 ) goto manifest_syntax_error;
626 if( p->zTicketUuid ) goto manifest_syntax_error;
627 if( p->zWikiTitle ) goto manifest_syntax_error;
628 if( !seenZ ) goto manifest_syntax_error;
629 p->type = CFTYPE_ATTACHMENT;
630 }else{
631 if( p->nCChild>0 ) goto manifest_syntax_error;
632 if( p->rDate<=0.0 ) goto manifest_syntax_error;
633 if( p->nParent>0 ) goto manifest_syntax_error;
634 if( p->nField>0 ) goto manifest_syntax_error;
635 if( p->zTicketUuid ) goto manifest_syntax_error;
 
636 if( p->zWiki ) goto manifest_syntax_error;
637 if( p->zWikiTitle ) goto manifest_syntax_error;
638 if( p->zTicketUuid ) goto manifest_syntax_error;
639 if( p->zAttachName ) goto manifest_syntax_error;
640 p->type = CFTYPE_MANIFEST;
641 }
 
642 md5sum_init();
643 return 1;
644
645 manifest_syntax_error:
646 /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
@@ -1032,10 +999,11 @@
999 return 0;
1000 }
1001 db_begin_transaction();
1002 if( m.type==CFTYPE_MANIFEST ){
1003 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1004 char *zCom;
1005 for(i=0; i<m.nParent; i++){
1006 int pid = uuid_to_rid(m.azParent[i], 1);
1007 db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
1008 "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
1009 if( i==0 ){
@@ -1065,10 +1033,14 @@
1033 rid, m.zUser, m.zComment,
1034 TAG_BGCOLOR, rid,
1035 TAG_USER, rid,
1036 TAG_COMMENT, rid
1037 );
1038 zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
1039 " WHERE rowid=last_insert_rowid()");
1040 wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE);
1041 free(zCom);
1042 }
1043 }
1044 if( m.type==CFTYPE_CLUSTER ){
1045 tag_insert("cluster", 1, 0, rid, m.rDate, rid);
1046 for(i=0; i<m.nCChild; i++){
@@ -1108,11 +1080,16 @@
1080 if( m.type==CFTYPE_WIKI ){
1081 char *zTag = mprintf("wiki-%s", m.zWikiTitle);
1082 int tagid = tag_findid(zTag, 1);
1083 int prior;
1084 char *zComment;
1085 int nWiki;
1086 char zLength[40];
1087 while( isspace(m.zWiki[0]) ) m.zWiki++;
1088 nWiki = strlen(m.zWiki);
1089 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1090 tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
1091 free(zTag);
1092 prior = db_int(0,
1093 "SELECT rid FROM tagxref"
1094 " WHERE tagid=%d AND mtime<%.17g"
1095 " ORDER BY mtime DESC",
@@ -1119,11 +1096,15 @@
1096 tagid, m.rDate
1097 );
1098 if( prior ){
1099 content_deltify(prior, rid, 0);
1100 }
1101 if( nWiki>0 ){
1102 zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1103 }else{
1104 zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
1105 }
1106 db_multi_exec(
1107 "REPLACE INTO event(type,mtime,objid,user,comment,"
1108 " bgcolor,euser,ecomment)"
1109 "VALUES('w',%.17g,%d,%Q,%Q,"
1110 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1144,10 +1125,60 @@
1125 zTag = mprintf("tkt-%s", m.zTicketUuid);
1126 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
1127 free(zTag);
1128 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
1129 m.zTicketUuid);
1130 }
1131 if( m.type==CFTYPE_ATTACHMENT ){
1132 db_multi_exec(
1133 "INSERT INTO attachment(attachid, mtime, src, target,"
1134 "filename, comment, user)"
1135 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
1136 rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
1137 (m.zComment ? m.zComment : ""), m.zUser
1138 );
1139 db_multi_exec(
1140 "UPDATE attachment SET isLatest = (mtime=="
1141 "(SELECT max(mtime) FROM attachment"
1142 " WHERE target=%Q AND filename=%Q))"
1143 " WHERE target=%Q AND filename=%Q",
1144 m.zAttachTarget, m.zAttachName,
1145 m.zAttachTarget, m.zAttachName
1146 );
1147 if( strlen(m.zAttachTarget)!=UUID_SIZE
1148 || !validate16(m.zAttachTarget, UUID_SIZE)
1149 ){
1150 char *zComment;
1151 if( m.zAttachSrc && m.zAttachSrc[0] ){
1152 zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
1153 m.zAttachName, m.zAttachTarget);
1154 }else{
1155 zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
1156 m.zAttachName, m.zAttachTarget);
1157 }
1158 db_multi_exec(
1159 "REPLACE INTO event(type,mtime,objid,user,comment)"
1160 "VALUES('w',%.17g,%d,%Q,%Q)",
1161 m.rDate, rid, m.zUser, zComment
1162 );
1163 free(zComment);
1164 }else{
1165 char *zComment;
1166 if( m.zAttachSrc && m.zAttachSrc[0] ){
1167 zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
1168 m.zAttachName, m.zAttachTarget);
1169 }else{
1170 zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
1171 m.zAttachName, m.zAttachTarget);
1172 }
1173 db_multi_exec(
1174 "REPLACE INTO event(type,mtime,objid,user,comment)"
1175 "VALUES('t',%.17g,%d,%Q,%Q)",
1176 m.rDate, rid, m.zUser, zComment
1177 );
1178 free(zComment);
1179 }
1180 }
1181 db_end_transaction(0);
1182 manifest_clear(&m);
1183 return 1;
1184 }
1185
+36 -9
--- src/merge.c
+++ src/merge.c
@@ -30,36 +30,51 @@
3030
3131
3232
/*
3333
** COMMAND: merge
3434
**
35
-** Usage: %fossil merge [--cherrypick] VERSION
35
+** Usage: %fossil merge [--cherrypick] [--backout] VERSION
3636
**
3737
** The argument is a version that should be merged into the current
3838
** checkout. All changes from VERSION back to the nearest common
39
-** ancestor are merged. Except, if the --cherrypick option is used
40
-** only the changes associated with the single check-in VERSION are
41
-** merged.
39
+** ancestor are merged. Except, if either of the --cherrypick or
40
+** --backout options are used only the changes associated with the
41
+** single check-in VERSION are merged. The --backout option causes
42
+** the changes associated with VERSION to be removed from the current
43
+** checkout rather than added.
4244
**
4345
** Only file content is merged. The result continues to use the
44
-** file and directory names from the current check-out even if those
46
+** file and directory names from the current checkout even if those
4547
** names might have been changed in the branch being merged in.
48
+**
49
+** Other options:
50
+**
51
+** --detail Show additional details of the merge
52
+**
53
+** --binary GLOBPATTERN Treat files that match GLOBPATTERN as binary
54
+** and do not try to merge parallel changes. This
55
+** option overrides the "binary-glob" setting.
4656
*/
4757
void merge_cmd(void){
4858
int vid; /* Current version */
4959
int mid; /* Version we are merging against */
5060
int pid; /* The pivot version - most recent common ancestor */
5161
int detailFlag; /* True if the --detail option is present */
5262
int pickFlag; /* True if the --cherrypick option is present */
63
+ int backoutFlag; /* True if the --backout optioni is present */
64
+ const char *zBinGlob; /* The value of --binary */
5365
Stmt q;
5466
5567
detailFlag = find_option("detail",0,0)!=0;
5668
pickFlag = find_option("cherrypick",0,0)!=0;
69
+ backoutFlag = find_option("backout",0,0)!=0;
70
+ zBinGlob = find_option("binary",0,1);
5771
if( g.argc!=3 ){
5872
usage("VERSION");
5973
}
6074
db_must_be_within_tree();
75
+ if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
6176
vid = db_lget_int("checkout", 0);
6277
if( vid==0 ){
6378
fossil_fatal("nothing is checked out");
6479
}
6580
mid = name_to_rid(g.argv[2]);
@@ -67,14 +82,19 @@
6782
fossil_fatal("not a version: %s", g.argv[2]);
6883
}
6984
if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){
7085
fossil_fatal("not a version: %s", g.argv[2]);
7186
}
72
- if( pickFlag ){
87
+ if( pickFlag || backoutFlag ){
7388
pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
7489
if( pid<=0 ){
7590
fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
91
+ }
92
+ if( backoutFlag ){
93
+ int t = pid;
94
+ pid = mid;
95
+ mid = t;
7696
}
7797
}else{
7898
pivot_set_primary(mid);
7999
pivot_set_secondary(vid);
80100
db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
@@ -229,19 +249,21 @@
229249
230250
/*
231251
** Do a three-way merge on files that have changes pid->mid and pid->vid
232252
*/
233253
db_prepare(&q,
234
- "SELECT ridm, idv, ridp, ridv FROM fv"
254
+ "SELECT ridm, idv, ridp, ridv, %s FROM fv"
235255
" WHERE idp>0 AND idv>0 AND idm>0"
236
- " AND ridm!=ridp AND (ridv!=ridp OR chnged)"
256
+ " AND ridm!=ridp AND (ridv!=ridp OR chnged)",
257
+ glob_expr("fv.fn", zBinGlob)
237258
);
238259
while( db_step(&q)==SQLITE_ROW ){
239260
int ridm = db_column_int(&q, 0);
240261
int idv = db_column_int(&q, 1);
241262
int ridp = db_column_int(&q, 2);
242263
int ridv = db_column_int(&q, 3);
264
+ int isBinary = db_column_int(&q, 4);
243265
int rc;
244266
char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
245267
char *zFullPath;
246268
Blob m, p, v, r;
247269
/* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
@@ -254,11 +276,16 @@
254276
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
255277
content_get(ridp, &p);
256278
content_get(ridm, &m);
257279
blob_zero(&v);
258280
blob_read_from_file(&v, zFullPath);
259
- rc = blob_merge(&p, &m, &v, &r);
281
+ if( isBinary ){
282
+ rc = -1;
283
+ blob_zero(&r);
284
+ }else{
285
+ rc = blob_merge(&p, &m, &v, &r);
286
+ }
260287
if( rc>=0 ){
261288
blob_write_to_file(&r, zFullPath);
262289
if( rc>0 ){
263290
printf("***** %d merge conflicts in %s\n", rc, zName);
264291
}
265292
--- src/merge.c
+++ src/merge.c
@@ -30,36 +30,51 @@
30
31
32 /*
33 ** COMMAND: merge
34 **
35 ** Usage: %fossil merge [--cherrypick] VERSION
36 **
37 ** The argument is a version that should be merged into the current
38 ** checkout. All changes from VERSION back to the nearest common
39 ** ancestor are merged. Except, if the --cherrypick option is used
40 ** only the changes associated with the single check-in VERSION are
41 ** merged.
 
 
42 **
43 ** Only file content is merged. The result continues to use the
44 ** file and directory names from the current check-out even if those
45 ** names might have been changed in the branch being merged in.
 
 
 
 
 
 
 
 
46 */
47 void merge_cmd(void){
48 int vid; /* Current version */
49 int mid; /* Version we are merging against */
50 int pid; /* The pivot version - most recent common ancestor */
51 int detailFlag; /* True if the --detail option is present */
52 int pickFlag; /* True if the --cherrypick option is present */
 
 
53 Stmt q;
54
55 detailFlag = find_option("detail",0,0)!=0;
56 pickFlag = find_option("cherrypick",0,0)!=0;
 
 
57 if( g.argc!=3 ){
58 usage("VERSION");
59 }
60 db_must_be_within_tree();
 
61 vid = db_lget_int("checkout", 0);
62 if( vid==0 ){
63 fossil_fatal("nothing is checked out");
64 }
65 mid = name_to_rid(g.argv[2]);
@@ -67,14 +82,19 @@
67 fossil_fatal("not a version: %s", g.argv[2]);
68 }
69 if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){
70 fossil_fatal("not a version: %s", g.argv[2]);
71 }
72 if( pickFlag ){
73 pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
74 if( pid<=0 ){
75 fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
 
 
 
 
 
76 }
77 }else{
78 pivot_set_primary(mid);
79 pivot_set_secondary(vid);
80 db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
@@ -229,19 +249,21 @@
229
230 /*
231 ** Do a three-way merge on files that have changes pid->mid and pid->vid
232 */
233 db_prepare(&q,
234 "SELECT ridm, idv, ridp, ridv FROM fv"
235 " WHERE idp>0 AND idv>0 AND idm>0"
236 " AND ridm!=ridp AND (ridv!=ridp OR chnged)"
 
237 );
238 while( db_step(&q)==SQLITE_ROW ){
239 int ridm = db_column_int(&q, 0);
240 int idv = db_column_int(&q, 1);
241 int ridp = db_column_int(&q, 2);
242 int ridv = db_column_int(&q, 3);
 
243 int rc;
244 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
245 char *zFullPath;
246 Blob m, p, v, r;
247 /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
@@ -254,11 +276,16 @@
254 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
255 content_get(ridp, &p);
256 content_get(ridm, &m);
257 blob_zero(&v);
258 blob_read_from_file(&v, zFullPath);
259 rc = blob_merge(&p, &m, &v, &r);
 
 
 
 
 
260 if( rc>=0 ){
261 blob_write_to_file(&r, zFullPath);
262 if( rc>0 ){
263 printf("***** %d merge conflicts in %s\n", rc, zName);
264 }
265
--- src/merge.c
+++ src/merge.c
@@ -30,36 +30,51 @@
30
31
32 /*
33 ** COMMAND: merge
34 **
35 ** Usage: %fossil merge [--cherrypick] [--backout] VERSION
36 **
37 ** The argument is a version that should be merged into the current
38 ** checkout. All changes from VERSION back to the nearest common
39 ** ancestor are merged. Except, if either of the --cherrypick or
40 ** --backout options are used only the changes associated with the
41 ** single check-in VERSION are merged. The --backout option causes
42 ** the changes associated with VERSION to be removed from the current
43 ** checkout rather than added.
44 **
45 ** Only file content is merged. The result continues to use the
46 ** file and directory names from the current checkout even if those
47 ** names might have been changed in the branch being merged in.
48 **
49 ** Other options:
50 **
51 ** --detail Show additional details of the merge
52 **
53 ** --binary GLOBPATTERN Treat files that match GLOBPATTERN as binary
54 ** and do not try to merge parallel changes. This
55 ** option overrides the "binary-glob" setting.
56 */
57 void merge_cmd(void){
58 int vid; /* Current version */
59 int mid; /* Version we are merging against */
60 int pid; /* The pivot version - most recent common ancestor */
61 int detailFlag; /* True if the --detail option is present */
62 int pickFlag; /* True if the --cherrypick option is present */
63 int backoutFlag; /* True if the --backout optioni is present */
64 const char *zBinGlob; /* The value of --binary */
65 Stmt q;
66
67 detailFlag = find_option("detail",0,0)!=0;
68 pickFlag = find_option("cherrypick",0,0)!=0;
69 backoutFlag = find_option("backout",0,0)!=0;
70 zBinGlob = find_option("binary",0,1);
71 if( g.argc!=3 ){
72 usage("VERSION");
73 }
74 db_must_be_within_tree();
75 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
76 vid = db_lget_int("checkout", 0);
77 if( vid==0 ){
78 fossil_fatal("nothing is checked out");
79 }
80 mid = name_to_rid(g.argv[2]);
@@ -67,14 +82,19 @@
82 fossil_fatal("not a version: %s", g.argv[2]);
83 }
84 if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){
85 fossil_fatal("not a version: %s", g.argv[2]);
86 }
87 if( pickFlag || backoutFlag ){
88 pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
89 if( pid<=0 ){
90 fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
91 }
92 if( backoutFlag ){
93 int t = pid;
94 pid = mid;
95 mid = t;
96 }
97 }else{
98 pivot_set_primary(mid);
99 pivot_set_secondary(vid);
100 db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
@@ -229,19 +249,21 @@
249
250 /*
251 ** Do a three-way merge on files that have changes pid->mid and pid->vid
252 */
253 db_prepare(&q,
254 "SELECT ridm, idv, ridp, ridv, %s FROM fv"
255 " WHERE idp>0 AND idv>0 AND idm>0"
256 " AND ridm!=ridp AND (ridv!=ridp OR chnged)",
257 glob_expr("fv.fn", zBinGlob)
258 );
259 while( db_step(&q)==SQLITE_ROW ){
260 int ridm = db_column_int(&q, 0);
261 int idv = db_column_int(&q, 1);
262 int ridp = db_column_int(&q, 2);
263 int ridv = db_column_int(&q, 3);
264 int isBinary = db_column_int(&q, 4);
265 int rc;
266 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
267 char *zFullPath;
268 Blob m, p, v, r;
269 /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
@@ -254,11 +276,16 @@
276 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
277 content_get(ridp, &p);
278 content_get(ridm, &m);
279 blob_zero(&v);
280 blob_read_from_file(&v, zFullPath);
281 if( isBinary ){
282 rc = -1;
283 blob_zero(&r);
284 }else{
285 rc = blob_merge(&p, &m, &v, &r);
286 }
287 if( rc>=0 ){
288 blob_write_to_file(&r, zFullPath);
289 if( rc>0 ){
290 printf("***** %d merge conflicts in %s\n", rc, zName);
291 }
292
+65 -1
--- src/name.c
+++ src/name.c
@@ -111,16 +111,42 @@
111111
}else{
112112
rc = 0;
113113
}
114114
return rc;
115115
}
116
+
117
+/*
118
+** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD.
119
+*/
120
+static int is_date(const char *z){
121
+ if( !isdigit(z[0]) ) return 0;
122
+ if( !isdigit(z[1]) ) return 0;
123
+ if( !isdigit(z[2]) ) return 0;
124
+ if( !isdigit(z[3]) ) return 0;
125
+ if( z[4]!='-') return 0;
126
+ if( !isdigit(z[5]) ) return 0;
127
+ if( !isdigit(z[6]) ) return 0;
128
+ if( z[7]!='-') return 0;
129
+ if( !isdigit(z[8]) ) return 0;
130
+ if( !isdigit(z[9]) ) return 0;
131
+ return 1;
132
+}
116133
117134
/*
118135
** Convert a symbolic tag name into the UUID of a check-in that contains
119136
** that tag. If the tag appears on multiple check-ins, return the UUID
120137
** of the most recent check-in with the tag.
121138
**
139
+** If the input string is of the form:
140
+**
141
+** tag:date
142
+**
143
+** Then return the UUID of the oldest check-in with that tag that is
144
+** not older than 'date'.
145
+**
146
+** An input of "tip" returns the most recent check-in.
147
+**
122148
** Memory to hold the returned string comes from malloc() and needs to
123149
** be freed by the caller.
124150
*/
125151
char *tag_to_uuid(const char *zTag){
126152
char *zUuid =
@@ -132,10 +158,48 @@
132158
" AND event.objid=tagxref.rid "
133159
" AND blob.rid=event.objid "
134160
" ORDER BY event.mtime DESC ",
135161
zTag
136162
);
163
+ if( zUuid==0 ){
164
+ int nTag = strlen(zTag);
165
+ int i;
166
+ for(i=0; i<nTag-10; i++){
167
+ if( zTag[i]==':' && is_date(&zTag[i+1]) ){
168
+ char *zDate = mprintf("%s", &zTag[i+1]);
169
+ char *zTagBase = mprintf("%.*s", i, zTag);
170
+ int nDate = strlen(zDate);
171
+ int useUtc = 0;
172
+ if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
173
+ nDate -= 3;
174
+ zDate[nDate] = 0;
175
+ useUtc = 1;
176
+ }
177
+ zUuid = db_text(0,
178
+ "SELECT blob.uuid"
179
+ " FROM tag, tagxref, event, blob"
180
+ " WHERE tag.tagname='sym-'||%Q "
181
+ " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
182
+ " AND event.objid=tagxref.rid "
183
+ " AND blob.rid=event.objid "
184
+ " AND event.mtime<=julianday(%Q %s)"
185
+ " ORDER BY event.mtime DESC ",
186
+ zTagBase, zDate, (useUtc ? "" : ",'utc'")
187
+ );
188
+ break;
189
+ }
190
+ }
191
+ if( zUuid==0 && strcmp(zTag, "tip")==0 ){
192
+ zUuid = db_text(0,
193
+ "SELECT blob.uuid"
194
+ " FROM event, blob"
195
+ " WHERE event.type='ci'"
196
+ " AND blob.rid=event.objid"
197
+ " ORDER BY event.mtime DESC"
198
+ );
199
+ }
200
+ }
137201
return zUuid;
138202
}
139203
140204
/*
141205
** Convert a date/time string into a UUID.
@@ -162,11 +226,11 @@
162226
}else if( memcmp(zDate, "utc:", 4)==0 ){
163227
zDate += 4;
164228
useUtc = 1;
165229
}
166230
n = strlen(zDate);
167
- if( n<10 || zDate[4]!='-' || zDate[7]!='-' ) return 0;
231
+ if( n<10 || !is_date(zDate) ) return 0;
168232
if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){
169233
zCopy = mprintf("%s", zDate);
170234
zCopy[n-3] = 0;
171235
zDate = zCopy;
172236
n -= 3;
173237
--- src/name.c
+++ src/name.c
@@ -111,16 +111,42 @@
111 }else{
112 rc = 0;
113 }
114 return rc;
115 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
117 /*
118 ** Convert a symbolic tag name into the UUID of a check-in that contains
119 ** that tag. If the tag appears on multiple check-ins, return the UUID
120 ** of the most recent check-in with the tag.
121 **
 
 
 
 
 
 
 
 
 
122 ** Memory to hold the returned string comes from malloc() and needs to
123 ** be freed by the caller.
124 */
125 char *tag_to_uuid(const char *zTag){
126 char *zUuid =
@@ -132,10 +158,48 @@
132 " AND event.objid=tagxref.rid "
133 " AND blob.rid=event.objid "
134 " ORDER BY event.mtime DESC ",
135 zTag
136 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137 return zUuid;
138 }
139
140 /*
141 ** Convert a date/time string into a UUID.
@@ -162,11 +226,11 @@
162 }else if( memcmp(zDate, "utc:", 4)==0 ){
163 zDate += 4;
164 useUtc = 1;
165 }
166 n = strlen(zDate);
167 if( n<10 || zDate[4]!='-' || zDate[7]!='-' ) return 0;
168 if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){
169 zCopy = mprintf("%s", zDate);
170 zCopy[n-3] = 0;
171 zDate = zCopy;
172 n -= 3;
173
--- src/name.c
+++ src/name.c
@@ -111,16 +111,42 @@
111 }else{
112 rc = 0;
113 }
114 return rc;
115 }
116
117 /*
118 ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD.
119 */
120 static int is_date(const char *z){
121 if( !isdigit(z[0]) ) return 0;
122 if( !isdigit(z[1]) ) return 0;
123 if( !isdigit(z[2]) ) return 0;
124 if( !isdigit(z[3]) ) return 0;
125 if( z[4]!='-') return 0;
126 if( !isdigit(z[5]) ) return 0;
127 if( !isdigit(z[6]) ) return 0;
128 if( z[7]!='-') return 0;
129 if( !isdigit(z[8]) ) return 0;
130 if( !isdigit(z[9]) ) return 0;
131 return 1;
132 }
133
134 /*
135 ** Convert a symbolic tag name into the UUID of a check-in that contains
136 ** that tag. If the tag appears on multiple check-ins, return the UUID
137 ** of the most recent check-in with the tag.
138 **
139 ** If the input string is of the form:
140 **
141 ** tag:date
142 **
143 ** Then return the UUID of the oldest check-in with that tag that is
144 ** not older than 'date'.
145 **
146 ** An input of "tip" returns the most recent check-in.
147 **
148 ** Memory to hold the returned string comes from malloc() and needs to
149 ** be freed by the caller.
150 */
151 char *tag_to_uuid(const char *zTag){
152 char *zUuid =
@@ -132,10 +158,48 @@
158 " AND event.objid=tagxref.rid "
159 " AND blob.rid=event.objid "
160 " ORDER BY event.mtime DESC ",
161 zTag
162 );
163 if( zUuid==0 ){
164 int nTag = strlen(zTag);
165 int i;
166 for(i=0; i<nTag-10; i++){
167 if( zTag[i]==':' && is_date(&zTag[i+1]) ){
168 char *zDate = mprintf("%s", &zTag[i+1]);
169 char *zTagBase = mprintf("%.*s", i, zTag);
170 int nDate = strlen(zDate);
171 int useUtc = 0;
172 if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
173 nDate -= 3;
174 zDate[nDate] = 0;
175 useUtc = 1;
176 }
177 zUuid = db_text(0,
178 "SELECT blob.uuid"
179 " FROM tag, tagxref, event, blob"
180 " WHERE tag.tagname='sym-'||%Q "
181 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
182 " AND event.objid=tagxref.rid "
183 " AND blob.rid=event.objid "
184 " AND event.mtime<=julianday(%Q %s)"
185 " ORDER BY event.mtime DESC ",
186 zTagBase, zDate, (useUtc ? "" : ",'utc'")
187 );
188 break;
189 }
190 }
191 if( zUuid==0 && strcmp(zTag, "tip")==0 ){
192 zUuid = db_text(0,
193 "SELECT blob.uuid"
194 " FROM event, blob"
195 " WHERE event.type='ci'"
196 " AND blob.rid=event.objid"
197 " ORDER BY event.mtime DESC"
198 );
199 }
200 }
201 return zUuid;
202 }
203
204 /*
205 ** Convert a date/time string into a UUID.
@@ -162,11 +226,11 @@
226 }else if( memcmp(zDate, "utc:", 4)==0 ){
227 zDate += 4;
228 useUtc = 1;
229 }
230 n = strlen(zDate);
231 if( n<10 || !is_date(zDate) ) return 0;
232 if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){
233 zCopy = mprintf("%s", zDate);
234 zCopy[n-3] = 0;
235 zDate = zCopy;
236 n -= 3;
237
+31
--- src/schema.c
+++ src/schema.c
@@ -309,10 +309,40 @@
309309
@ rid INTEGER REFERENCE blob, -- Artifact tag is applied to
310310
@ UNIQUE(rid, tagid)
311311
@ );
312312
@ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
313313
@
314
+@ -- When a hyperlink occurs from one artifact to another (for example
315
+@ -- when a check-in comment refers to a ticket) an entry is made in
316
+@ -- the following table for that hyperlink. This table is used to
317
+@ -- facilitate the display of "back links".
318
+@ --
319
+@ CREATE TABLE backlink(
320
+@ target TEXT, -- Where the hyperlink points to
321
+@ srctype INT, -- 0: check-in 1: ticket 2: wiki
322
+@ srcid INT, -- rid for checkin or wiki. tkt_id for ticket.
323
+@ mtime TIMESTAMP, -- time that the hyperlink was added
324
+@ UNIQUE(target, srctype, srcid)
325
+@ );
326
+@ CREATE INDEX backlink_src ON backlink(srcid, srctype);
327
+@
328
+@ -- Each attachment is an entry in the following table. Only
329
+@ -- the most recent attachment (identified by the D card) is saved.
330
+@ --
331
+@ CREATE TABLE attachment(
332
+@ attachid INTEGER PRIMARY KEY, -- Local id for this attachment
333
+@ isLatest BOOLEAN DEFAULT 0, -- True if this is the one to use
334
+@ mtime TIMESTAMP, -- Time when attachment last changed
335
+@ src TEXT, -- UUID of the attachment. NULL to delete
336
+@ target TEXT, -- Object attached to. Wikiname or Tkt UUID
337
+@ filename TEXT, -- Filename for the attachment
338
+@ comment TEXT, -- Comment associated with this attachment
339
+@ user TEXT -- Name of user adding attachment
340
+@ );
341
+@ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);
342
+@ CREATE INDEX attachment_idx2 ON attachment(src);
343
+@
314344
@ -- Template for the TICKET table
315345
@ --
316346
@ -- NB: when changing the schema of the TICKET table here, also make the
317347
@ -- same change in tktsetup.c.
318348
@ --
@@ -393,10 +423,11 @@
393423
@ CREATE TABLE vfile(
394424
@ id INTEGER PRIMARY KEY, -- ID of the checked out file
395425
@ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
396426
@ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add
397427
@ deleted BOOLEAN DEFAULT 0, -- True if deleted
428
+@ isexe BOOLEAN, -- True if file should be executable
398429
@ rid INTEGER, -- Originally from this repository record
399430
@ mrid INTEGER, -- Based on this record due to a merge
400431
@ mtime INTEGER, -- Modification time of file on disk
401432
@ pathname TEXT, -- Full pathname relative to root
402433
@ origname TEXT, -- Original pathname. NULL if unchanged
403434
--- src/schema.c
+++ src/schema.c
@@ -309,10 +309,40 @@
309 @ rid INTEGER REFERENCE blob, -- Artifact tag is applied to
310 @ UNIQUE(rid, tagid)
311 @ );
312 @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
313 @
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314 @ -- Template for the TICKET table
315 @ --
316 @ -- NB: when changing the schema of the TICKET table here, also make the
317 @ -- same change in tktsetup.c.
318 @ --
@@ -393,10 +423,11 @@
393 @ CREATE TABLE vfile(
394 @ id INTEGER PRIMARY KEY, -- ID of the checked out file
395 @ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
396 @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add
397 @ deleted BOOLEAN DEFAULT 0, -- True if deleted
 
398 @ rid INTEGER, -- Originally from this repository record
399 @ mrid INTEGER, -- Based on this record due to a merge
400 @ mtime INTEGER, -- Modification time of file on disk
401 @ pathname TEXT, -- Full pathname relative to root
402 @ origname TEXT, -- Original pathname. NULL if unchanged
403
--- src/schema.c
+++ src/schema.c
@@ -309,10 +309,40 @@
309 @ rid INTEGER REFERENCE blob, -- Artifact tag is applied to
310 @ UNIQUE(rid, tagid)
311 @ );
312 @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
313 @
314 @ -- When a hyperlink occurs from one artifact to another (for example
315 @ -- when a check-in comment refers to a ticket) an entry is made in
316 @ -- the following table for that hyperlink. This table is used to
317 @ -- facilitate the display of "back links".
318 @ --
319 @ CREATE TABLE backlink(
320 @ target TEXT, -- Where the hyperlink points to
321 @ srctype INT, -- 0: check-in 1: ticket 2: wiki
322 @ srcid INT, -- rid for checkin or wiki. tkt_id for ticket.
323 @ mtime TIMESTAMP, -- time that the hyperlink was added
324 @ UNIQUE(target, srctype, srcid)
325 @ );
326 @ CREATE INDEX backlink_src ON backlink(srcid, srctype);
327 @
328 @ -- Each attachment is an entry in the following table. Only
329 @ -- the most recent attachment (identified by the D card) is saved.
330 @ --
331 @ CREATE TABLE attachment(
332 @ attachid INTEGER PRIMARY KEY, -- Local id for this attachment
333 @ isLatest BOOLEAN DEFAULT 0, -- True if this is the one to use
334 @ mtime TIMESTAMP, -- Time when attachment last changed
335 @ src TEXT, -- UUID of the attachment. NULL to delete
336 @ target TEXT, -- Object attached to. Wikiname or Tkt UUID
337 @ filename TEXT, -- Filename for the attachment
338 @ comment TEXT, -- Comment associated with this attachment
339 @ user TEXT -- Name of user adding attachment
340 @ );
341 @ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);
342 @ CREATE INDEX attachment_idx2 ON attachment(src);
343 @
344 @ -- Template for the TICKET table
345 @ --
346 @ -- NB: when changing the schema of the TICKET table here, also make the
347 @ -- same change in tktsetup.c.
348 @ --
@@ -393,10 +423,11 @@
423 @ CREATE TABLE vfile(
424 @ id INTEGER PRIMARY KEY, -- ID of the checked out file
425 @ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
426 @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add
427 @ deleted BOOLEAN DEFAULT 0, -- True if deleted
428 @ isexe BOOLEAN, -- True if file should be executable
429 @ rid INTEGER, -- Originally from this repository record
430 @ mrid INTEGER, -- Based on this record due to a merge
431 @ mtime INTEGER, -- Modification time of file on disk
432 @ pathname TEXT, -- Full pathname relative to root
433 @ origname TEXT, -- Original pathname. NULL if unchanged
434
+21 -10
--- src/setup.c
+++ src/setup.c
@@ -145,10 +145,12 @@
145145
@ <ol>
146146
@ <li><p>The permission flags are as follows:</p>
147147
@ <table>
148148
@ <tr><td valign="top"><b>a</b></td>
149149
@ <td><i>Admin:</i> Create and delete users</td></tr>
150
+ @ <tr><td valign="top"><b>b</b></td>
151
+ @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
150152
@ <tr><td valign="top"><b>c</b></td>
151153
@ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
152154
@ <tr><td valign="top"><b>d</b></td>
153155
@ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
154156
@ <tr><td valign="top"><b>e</b></td>
@@ -237,11 +239,11 @@
237239
*/
238240
void user_edit(void){
239241
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
240242
char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
241243
char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
242
- char *oat, *oau, *oav, *oaz;
244
+ char *oat, *oau, *oav, *oab, *oaz;
243245
const char *inherit[128];
244246
int doWrite;
245247
int uid;
246248
int higherUser = 0; /* True if user being edited is SETUP and the */
247249
/* user doing the editing is ADMIN. Disallow editing */
@@ -274,10 +276,11 @@
274276
doWrite = cgi_all("login","info","pw") && !higherUser;
275277
if( doWrite ){
276278
char zCap[50];
277279
int i = 0;
278280
int aa = P("aa")!=0;
281
+ int ab = P("ab")!=0;
279282
int ad = P("ad")!=0;
280283
int ae = P("ae")!=0;
281284
int ai = P("ai")!=0;
282285
int aj = P("aj")!=0;
283286
int ak = P("ak")!=0;
@@ -295,10 +298,11 @@
295298
int at = P("at")!=0;
296299
int au = P("au")!=0;
297300
int av = P("av")!=0;
298301
int az = P("az")!=0;
299302
if( aa ){ zCap[i++] = 'a'; }
303
+ if( ab ){ zCap[i++] = 'b'; }
300304
if( ac ){ zCap[i++] = 'c'; }
301305
if( ad ){ zCap[i++] = 'd'; }
302306
if( ae ){ zCap[i++] = 'e'; }
303307
if( af ){ zCap[i++] = 'f'; }
304308
if( ah ){ zCap[i++] = 'h'; }
@@ -351,18 +355,19 @@
351355
*/
352356
zLogin = "";
353357
zInfo = "";
354358
zCap = "";
355359
zPw = "";
356
- oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
360
+ oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
357361
oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
358362
if( uid ){
359363
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
360364
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
361365
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
362366
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
363367
if( strchr(zCap, 'a') ) oaa = " checked";
368
+ if( strchr(zCap, 'b') ) oab = " checked";
364369
if( strchr(zCap, 'c') ) oac = " checked";
365370
if( strchr(zCap, 'd') ) oad = " checked";
366371
if( strchr(zCap, 'e') ) oae = " checked";
367372
if( strchr(zCap, 'f') ) oaf = " checked";
368373
if( strchr(zCap, 'g') ) oag = " checked";
@@ -465,15 +470,16 @@
465470
@ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
466471
@ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
467472
@ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
468473
@ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
469474
@ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
470
- @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Tkt<br>
471
- @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Tkt<br>
472
- @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Tkt<br>
473
- @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
474
- @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
475
+ @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
476
+ @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
477
+ @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
478
+ @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
479
+ @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
480
+ @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
475481
@ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
476482
@ </td>
477483
@ </tr>
478484
@ <tr>
479485
@ <td align="right">Password:</td>
@@ -562,13 +568,13 @@
562568
@ </li><p>
563569
@
564570
@ <li><p>
565571
@ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
566572
@ <b>Write Wiki</b> privileges control access to wiki pages. The
567
- @ <b>Read Tkt</b>, <b>New Tkt</b>, <b>Append Tkt</b>, and
568
- @ <b>Write Tkt</b> privileges control access to trouble tickets.
569
- @ The <b>Tkt Report</b> privilege allows the user to create or edit
573
+ @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
574
+ @ <b>Write Ticket</b> privileges control access to trouble tickets.
575
+ @ The <b>Ticket Report</b> privilege allows the user to create or edit
570576
@ ticket report formats.
571577
@ </p></li>
572578
@
573579
@ <li><p>
574580
@ Users with the <b>Password</b> privilege are allowed to change their
@@ -581,10 +587,15 @@
581587
@ such as the email address of users and contact information on tickets.
582588
@ Recommended OFF for "anonymous" and for "nobody" but ON for
583589
@ "developer".
584590
@ </p></li>
585591
@
592
+ @ <li><p>
593
+ @ The <b>Attachment</b> privilege is needed in order to add attachments
594
+ @ to tickets or wiki. Write privilege on the ticket or wiki is also
595
+ @ required.</p></li>
596
+ @
586597
@ <li><p>
587598
@ Login is prohibited if the password is an empty string.
588599
@ </p></li>
589600
@ </ul>
590601
@
591602
--- src/setup.c
+++ src/setup.c
@@ -145,10 +145,12 @@
145 @ <ol>
146 @ <li><p>The permission flags are as follows:</p>
147 @ <table>
148 @ <tr><td valign="top"><b>a</b></td>
149 @ <td><i>Admin:</i> Create and delete users</td></tr>
 
 
150 @ <tr><td valign="top"><b>c</b></td>
151 @ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
152 @ <tr><td valign="top"><b>d</b></td>
153 @ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
154 @ <tr><td valign="top"><b>e</b></td>
@@ -237,11 +239,11 @@
237 */
238 void user_edit(void){
239 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
240 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
241 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
242 char *oat, *oau, *oav, *oaz;
243 const char *inherit[128];
244 int doWrite;
245 int uid;
246 int higherUser = 0; /* True if user being edited is SETUP and the */
247 /* user doing the editing is ADMIN. Disallow editing */
@@ -274,10 +276,11 @@
274 doWrite = cgi_all("login","info","pw") && !higherUser;
275 if( doWrite ){
276 char zCap[50];
277 int i = 0;
278 int aa = P("aa")!=0;
 
279 int ad = P("ad")!=0;
280 int ae = P("ae")!=0;
281 int ai = P("ai")!=0;
282 int aj = P("aj")!=0;
283 int ak = P("ak")!=0;
@@ -295,10 +298,11 @@
295 int at = P("at")!=0;
296 int au = P("au")!=0;
297 int av = P("av")!=0;
298 int az = P("az")!=0;
299 if( aa ){ zCap[i++] = 'a'; }
 
300 if( ac ){ zCap[i++] = 'c'; }
301 if( ad ){ zCap[i++] = 'd'; }
302 if( ae ){ zCap[i++] = 'e'; }
303 if( af ){ zCap[i++] = 'f'; }
304 if( ah ){ zCap[i++] = 'h'; }
@@ -351,18 +355,19 @@
351 */
352 zLogin = "";
353 zInfo = "";
354 zCap = "";
355 zPw = "";
356 oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
357 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
358 if( uid ){
359 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
360 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
361 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
362 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
363 if( strchr(zCap, 'a') ) oaa = " checked";
 
364 if( strchr(zCap, 'c') ) oac = " checked";
365 if( strchr(zCap, 'd') ) oad = " checked";
366 if( strchr(zCap, 'e') ) oae = " checked";
367 if( strchr(zCap, 'f') ) oaf = " checked";
368 if( strchr(zCap, 'g') ) oag = " checked";
@@ -465,15 +470,16 @@
465 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
466 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
467 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
468 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
469 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
470 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Tkt<br>
471 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Tkt<br>
472 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Tkt<br>
473 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
474 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
 
475 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
476 @ </td>
477 @ </tr>
478 @ <tr>
479 @ <td align="right">Password:</td>
@@ -562,13 +568,13 @@
562 @ </li><p>
563 @
564 @ <li><p>
565 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
566 @ <b>Write Wiki</b> privileges control access to wiki pages. The
567 @ <b>Read Tkt</b>, <b>New Tkt</b>, <b>Append Tkt</b>, and
568 @ <b>Write Tkt</b> privileges control access to trouble tickets.
569 @ The <b>Tkt Report</b> privilege allows the user to create or edit
570 @ ticket report formats.
571 @ </p></li>
572 @
573 @ <li><p>
574 @ Users with the <b>Password</b> privilege are allowed to change their
@@ -581,10 +587,15 @@
581 @ such as the email address of users and contact information on tickets.
582 @ Recommended OFF for "anonymous" and for "nobody" but ON for
583 @ "developer".
584 @ </p></li>
585 @
 
 
 
 
 
586 @ <li><p>
587 @ Login is prohibited if the password is an empty string.
588 @ </p></li>
589 @ </ul>
590 @
591
--- src/setup.c
+++ src/setup.c
@@ -145,10 +145,12 @@
145 @ <ol>
146 @ <li><p>The permission flags are as follows:</p>
147 @ <table>
148 @ <tr><td valign="top"><b>a</b></td>
149 @ <td><i>Admin:</i> Create and delete users</td></tr>
150 @ <tr><td valign="top"><b>b</b></td>
151 @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
152 @ <tr><td valign="top"><b>c</b></td>
153 @ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
154 @ <tr><td valign="top"><b>d</b></td>
155 @ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
156 @ <tr><td valign="top"><b>e</b></td>
@@ -237,11 +239,11 @@
239 */
240 void user_edit(void){
241 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
242 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
243 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
244 char *oat, *oau, *oav, *oab, *oaz;
245 const char *inherit[128];
246 int doWrite;
247 int uid;
248 int higherUser = 0; /* True if user being edited is SETUP and the */
249 /* user doing the editing is ADMIN. Disallow editing */
@@ -274,10 +276,11 @@
276 doWrite = cgi_all("login","info","pw") && !higherUser;
277 if( doWrite ){
278 char zCap[50];
279 int i = 0;
280 int aa = P("aa")!=0;
281 int ab = P("ab")!=0;
282 int ad = P("ad")!=0;
283 int ae = P("ae")!=0;
284 int ai = P("ai")!=0;
285 int aj = P("aj")!=0;
286 int ak = P("ak")!=0;
@@ -295,10 +298,11 @@
298 int at = P("at")!=0;
299 int au = P("au")!=0;
300 int av = P("av")!=0;
301 int az = P("az")!=0;
302 if( aa ){ zCap[i++] = 'a'; }
303 if( ab ){ zCap[i++] = 'b'; }
304 if( ac ){ zCap[i++] = 'c'; }
305 if( ad ){ zCap[i++] = 'd'; }
306 if( ae ){ zCap[i++] = 'e'; }
307 if( af ){ zCap[i++] = 'f'; }
308 if( ah ){ zCap[i++] = 'h'; }
@@ -351,18 +355,19 @@
355 */
356 zLogin = "";
357 zInfo = "";
358 zCap = "";
359 zPw = "";
360 oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
361 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
362 if( uid ){
363 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
364 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
365 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
366 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
367 if( strchr(zCap, 'a') ) oaa = " checked";
368 if( strchr(zCap, 'b') ) oab = " checked";
369 if( strchr(zCap, 'c') ) oac = " checked";
370 if( strchr(zCap, 'd') ) oad = " checked";
371 if( strchr(zCap, 'e') ) oae = " checked";
372 if( strchr(zCap, 'f') ) oaf = " checked";
373 if( strchr(zCap, 'g') ) oag = " checked";
@@ -465,15 +470,16 @@
470 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
471 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
472 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
473 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
474 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
475 @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
476 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
477 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
478 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
479 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
480 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
481 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
482 @ </td>
483 @ </tr>
484 @ <tr>
485 @ <td align="right">Password:</td>
@@ -562,13 +568,13 @@
568 @ </li><p>
569 @
570 @ <li><p>
571 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
572 @ <b>Write Wiki</b> privileges control access to wiki pages. The
573 @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
574 @ <b>Write Ticket</b> privileges control access to trouble tickets.
575 @ The <b>Ticket Report</b> privilege allows the user to create or edit
576 @ ticket report formats.
577 @ </p></li>
578 @
579 @ <li><p>
580 @ Users with the <b>Password</b> privilege are allowed to change their
@@ -581,10 +587,15 @@
587 @ such as the email address of users and contact information on tickets.
588 @ Recommended OFF for "anonymous" and for "nobody" but ON for
589 @ "developer".
590 @ </p></li>
591 @
592 @ <li><p>
593 @ The <b>Attachment</b> privilege is needed in order to add attachments
594 @ to tickets or wiki. Write privilege on the ticket or wiki is also
595 @ required.</p></li>
596 @
597 @ <li><p>
598 @ Login is prohibited if the password is an empty string.
599 @ </p></li>
600 @ </ul>
601 @
602
+21 -10
--- src/setup.c
+++ src/setup.c
@@ -145,10 +145,12 @@
145145
@ <ol>
146146
@ <li><p>The permission flags are as follows:</p>
147147
@ <table>
148148
@ <tr><td valign="top"><b>a</b></td>
149149
@ <td><i>Admin:</i> Create and delete users</td></tr>
150
+ @ <tr><td valign="top"><b>b</b></td>
151
+ @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
150152
@ <tr><td valign="top"><b>c</b></td>
151153
@ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
152154
@ <tr><td valign="top"><b>d</b></td>
153155
@ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
154156
@ <tr><td valign="top"><b>e</b></td>
@@ -237,11 +239,11 @@
237239
*/
238240
void user_edit(void){
239241
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
240242
char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
241243
char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
242
- char *oat, *oau, *oav, *oaz;
244
+ char *oat, *oau, *oav, *oab, *oaz;
243245
const char *inherit[128];
244246
int doWrite;
245247
int uid;
246248
int higherUser = 0; /* True if user being edited is SETUP and the */
247249
/* user doing the editing is ADMIN. Disallow editing */
@@ -274,10 +276,11 @@
274276
doWrite = cgi_all("login","info","pw") && !higherUser;
275277
if( doWrite ){
276278
char zCap[50];
277279
int i = 0;
278280
int aa = P("aa")!=0;
281
+ int ab = P("ab")!=0;
279282
int ad = P("ad")!=0;
280283
int ae = P("ae")!=0;
281284
int ai = P("ai")!=0;
282285
int aj = P("aj")!=0;
283286
int ak = P("ak")!=0;
@@ -295,10 +298,11 @@
295298
int at = P("at")!=0;
296299
int au = P("au")!=0;
297300
int av = P("av")!=0;
298301
int az = P("az")!=0;
299302
if( aa ){ zCap[i++] = 'a'; }
303
+ if( ab ){ zCap[i++] = 'b'; }
300304
if( ac ){ zCap[i++] = 'c'; }
301305
if( ad ){ zCap[i++] = 'd'; }
302306
if( ae ){ zCap[i++] = 'e'; }
303307
if( af ){ zCap[i++] = 'f'; }
304308
if( ah ){ zCap[i++] = 'h'; }
@@ -351,18 +355,19 @@
351355
*/
352356
zLogin = "";
353357
zInfo = "";
354358
zCap = "";
355359
zPw = "";
356
- oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
360
+ oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
357361
oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
358362
if( uid ){
359363
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
360364
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
361365
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
362366
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
363367
if( strchr(zCap, 'a') ) oaa = " checked";
368
+ if( strchr(zCap, 'b') ) oab = " checked";
364369
if( strchr(zCap, 'c') ) oac = " checked";
365370
if( strchr(zCap, 'd') ) oad = " checked";
366371
if( strchr(zCap, 'e') ) oae = " checked";
367372
if( strchr(zCap, 'f') ) oaf = " checked";
368373
if( strchr(zCap, 'g') ) oag = " checked";
@@ -465,15 +470,16 @@
465470
@ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
466471
@ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
467472
@ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
468473
@ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
469474
@ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
470
- @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Tkt<br>
471
- @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Tkt<br>
472
- @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Tkt<br>
473
- @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
474
- @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
475
+ @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
476
+ @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
477
+ @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
478
+ @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
479
+ @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
480
+ @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
475481
@ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
476482
@ </td>
477483
@ </tr>
478484
@ <tr>
479485
@ <td align="right">Password:</td>
@@ -562,13 +568,13 @@
562568
@ </li><p>
563569
@
564570
@ <li><p>
565571
@ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
566572
@ <b>Write Wiki</b> privileges control access to wiki pages. The
567
- @ <b>Read Tkt</b>, <b>New Tkt</b>, <b>Append Tkt</b>, and
568
- @ <b>Write Tkt</b> privileges control access to trouble tickets.
569
- @ The <b>Tkt Report</b> privilege allows the user to create or edit
573
+ @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
574
+ @ <b>Write Ticket</b> privileges control access to trouble tickets.
575
+ @ The <b>Ticket Report</b> privilege allows the user to create or edit
570576
@ ticket report formats.
571577
@ </p></li>
572578
@
573579
@ <li><p>
574580
@ Users with the <b>Password</b> privilege are allowed to change their
@@ -581,10 +587,15 @@
581587
@ such as the email address of users and contact information on tickets.
582588
@ Recommended OFF for "anonymous" and for "nobody" but ON for
583589
@ "developer".
584590
@ </p></li>
585591
@
592
+ @ <li><p>
593
+ @ The <b>Attachment</b> privilege is needed in order to add attachments
594
+ @ to tickets or wiki. Write privilege on the ticket or wiki is also
595
+ @ required.</p></li>
596
+ @
586597
@ <li><p>
587598
@ Login is prohibited if the password is an empty string.
588599
@ </p></li>
589600
@ </ul>
590601
@
591602
--- src/setup.c
+++ src/setup.c
@@ -145,10 +145,12 @@
145 @ <ol>
146 @ <li><p>The permission flags are as follows:</p>
147 @ <table>
148 @ <tr><td valign="top"><b>a</b></td>
149 @ <td><i>Admin:</i> Create and delete users</td></tr>
 
 
150 @ <tr><td valign="top"><b>c</b></td>
151 @ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
152 @ <tr><td valign="top"><b>d</b></td>
153 @ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
154 @ <tr><td valign="top"><b>e</b></td>
@@ -237,11 +239,11 @@
237 */
238 void user_edit(void){
239 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
240 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
241 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
242 char *oat, *oau, *oav, *oaz;
243 const char *inherit[128];
244 int doWrite;
245 int uid;
246 int higherUser = 0; /* True if user being edited is SETUP and the */
247 /* user doing the editing is ADMIN. Disallow editing */
@@ -274,10 +276,11 @@
274 doWrite = cgi_all("login","info","pw") && !higherUser;
275 if( doWrite ){
276 char zCap[50];
277 int i = 0;
278 int aa = P("aa")!=0;
 
279 int ad = P("ad")!=0;
280 int ae = P("ae")!=0;
281 int ai = P("ai")!=0;
282 int aj = P("aj")!=0;
283 int ak = P("ak")!=0;
@@ -295,10 +298,11 @@
295 int at = P("at")!=0;
296 int au = P("au")!=0;
297 int av = P("av")!=0;
298 int az = P("az")!=0;
299 if( aa ){ zCap[i++] = 'a'; }
 
300 if( ac ){ zCap[i++] = 'c'; }
301 if( ad ){ zCap[i++] = 'd'; }
302 if( ae ){ zCap[i++] = 'e'; }
303 if( af ){ zCap[i++] = 'f'; }
304 if( ah ){ zCap[i++] = 'h'; }
@@ -351,18 +355,19 @@
351 */
352 zLogin = "";
353 zInfo = "";
354 zCap = "";
355 zPw = "";
356 oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
357 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
358 if( uid ){
359 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
360 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
361 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
362 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
363 if( strchr(zCap, 'a') ) oaa = " checked";
 
364 if( strchr(zCap, 'c') ) oac = " checked";
365 if( strchr(zCap, 'd') ) oad = " checked";
366 if( strchr(zCap, 'e') ) oae = " checked";
367 if( strchr(zCap, 'f') ) oaf = " checked";
368 if( strchr(zCap, 'g') ) oag = " checked";
@@ -465,15 +470,16 @@
465 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
466 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
467 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
468 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
469 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
470 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Tkt<br>
471 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Tkt<br>
472 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Tkt<br>
473 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
474 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
 
475 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
476 @ </td>
477 @ </tr>
478 @ <tr>
479 @ <td align="right">Password:</td>
@@ -562,13 +568,13 @@
562 @ </li><p>
563 @
564 @ <li><p>
565 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
566 @ <b>Write Wiki</b> privileges control access to wiki pages. The
567 @ <b>Read Tkt</b>, <b>New Tkt</b>, <b>Append Tkt</b>, and
568 @ <b>Write Tkt</b> privileges control access to trouble tickets.
569 @ The <b>Tkt Report</b> privilege allows the user to create or edit
570 @ ticket report formats.
571 @ </p></li>
572 @
573 @ <li><p>
574 @ Users with the <b>Password</b> privilege are allowed to change their
@@ -581,10 +587,15 @@
581 @ such as the email address of users and contact information on tickets.
582 @ Recommended OFF for "anonymous" and for "nobody" but ON for
583 @ "developer".
584 @ </p></li>
585 @
 
 
 
 
 
586 @ <li><p>
587 @ Login is prohibited if the password is an empty string.
588 @ </p></li>
589 @ </ul>
590 @
591
--- src/setup.c
+++ src/setup.c
@@ -145,10 +145,12 @@
145 @ <ol>
146 @ <li><p>The permission flags are as follows:</p>
147 @ <table>
148 @ <tr><td valign="top"><b>a</b></td>
149 @ <td><i>Admin:</i> Create and delete users</td></tr>
150 @ <tr><td valign="top"><b>b</b></td>
151 @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
152 @ <tr><td valign="top"><b>c</b></td>
153 @ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
154 @ <tr><td valign="top"><b>d</b></td>
155 @ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
156 @ <tr><td valign="top"><b>e</b></td>
@@ -237,11 +239,11 @@
239 */
240 void user_edit(void){
241 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
242 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
243 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
244 char *oat, *oau, *oav, *oab, *oaz;
245 const char *inherit[128];
246 int doWrite;
247 int uid;
248 int higherUser = 0; /* True if user being edited is SETUP and the */
249 /* user doing the editing is ADMIN. Disallow editing */
@@ -274,10 +276,11 @@
276 doWrite = cgi_all("login","info","pw") && !higherUser;
277 if( doWrite ){
278 char zCap[50];
279 int i = 0;
280 int aa = P("aa")!=0;
281 int ab = P("ab")!=0;
282 int ad = P("ad")!=0;
283 int ae = P("ae")!=0;
284 int ai = P("ai")!=0;
285 int aj = P("aj")!=0;
286 int ak = P("ak")!=0;
@@ -295,10 +298,11 @@
298 int at = P("at")!=0;
299 int au = P("au")!=0;
300 int av = P("av")!=0;
301 int az = P("az")!=0;
302 if( aa ){ zCap[i++] = 'a'; }
303 if( ab ){ zCap[i++] = 'b'; }
304 if( ac ){ zCap[i++] = 'c'; }
305 if( ad ){ zCap[i++] = 'd'; }
306 if( ae ){ zCap[i++] = 'e'; }
307 if( af ){ zCap[i++] = 'f'; }
308 if( ah ){ zCap[i++] = 'h'; }
@@ -351,18 +355,19 @@
355 */
356 zLogin = "";
357 zInfo = "";
358 zCap = "";
359 zPw = "";
360 oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
361 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
362 if( uid ){
363 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
364 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
365 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
366 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
367 if( strchr(zCap, 'a') ) oaa = " checked";
368 if( strchr(zCap, 'b') ) oab = " checked";
369 if( strchr(zCap, 'c') ) oac = " checked";
370 if( strchr(zCap, 'd') ) oad = " checked";
371 if( strchr(zCap, 'e') ) oae = " checked";
372 if( strchr(zCap, 'f') ) oaf = " checked";
373 if( strchr(zCap, 'g') ) oag = " checked";
@@ -465,15 +470,16 @@
470 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
471 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
472 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
473 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
474 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
475 @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
476 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
477 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
478 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
479 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
480 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
481 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
482 @ </td>
483 @ </tr>
484 @ <tr>
485 @ <td align="right">Password:</td>
@@ -562,13 +568,13 @@
568 @ </li><p>
569 @
570 @ <li><p>
571 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
572 @ <b>Write Wiki</b> privileges control access to wiki pages. The
573 @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
574 @ <b>Write Ticket</b> privileges control access to trouble tickets.
575 @ The <b>Ticket Report</b> privilege allows the user to create or edit
576 @ ticket report formats.
577 @ </p></li>
578 @
579 @ <li><p>
580 @ Users with the <b>Password</b> privilege are allowed to change their
@@ -581,10 +587,15 @@
587 @ such as the email address of users and contact information on tickets.
588 @ Recommended OFF for "anonymous" and for "nobody" but ON for
589 @ "developer".
590 @ </p></li>
591 @
592 @ <li><p>
593 @ The <b>Attachment</b> privilege is needed in order to add attachments
594 @ to tickets or wiki. Write privilege on the ticket or wiki is also
595 @ required.</p></li>
596 @
597 @ <li><p>
598 @ Login is prohibited if the password is an empty string.
599 @ </p></li>
600 @ </ul>
601 @
602
+40 -40
--- src/skins.c
+++ src/skins.c
@@ -162,11 +162,11 @@
162162
@ REPLACE INTO config VALUES('header','<html>
163163
@ <head>
164164
@ <title>$<project_name>: $<title></title>
165165
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
166166
@ href="$baseurl/timeline.rss">
167
-@ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
167
+@ <link rel="stylesheet" href="$baseurl/style.css?blackwhite" type="text/css"
168168
@ media="screen">
169169
@ </head>
170170
@ <body>
171171
@ <div class="header">
172172
@ <div class="logo">
@@ -182,37 +182,37 @@
182182
@ puts "Not logged in"
183183
@ }
184184
@ </th1></nobr></div>
185185
@ </div>
186186
@ <div class="mainmenu"><th1>
187
-@ html "<a href="$baseurl$index_page">Home</a> "
187
+@ html "<a href=''$baseurl$index_page''>Home</a> "
188188
@ if {[anycap jor]} {
189
-@ html "<a href="$baseurl/timeline">Timeline</a> "
189
+@ html "<a href=''$baseurl/timeline''>Timeline</a> "
190190
@ }
191191
@ if {[hascap oh]} {
192
-@ html "<a href="$baseurl/dir">Files</a> "
192
+@ html "<a href=''$baseurl/dir?ci=tip''>Files</a> "
193193
@ }
194194
@ if {[hascap o]} {
195
-@ html "<a href="$baseurl/leaves">Leaves</a> "
196
-@ html "<a href="$baseurl/brlist">Branches</a> "
197
-@ html "<a href="$baseurl/taglist">Tags</a> "
195
+@ html "<a href=''$baseurl/leaves''>Leaves</a> "
196
+@ html "<a href=''$baseurl/brlist''>Branches</a> "
197
+@ html "<a href=''$baseurl/taglist''>Tags</a> "
198198
@ }
199199
@ if {[hascap r]} {
200
-@ html "<a href="$baseurl/reportlist">Tickets</a> "
200
+@ html "<a href=''$baseurl/reportlist''>Tickets</a> "
201201
@ }
202202
@ if {[hascap j]} {
203
-@ html "<a href="$baseurl/wiki">Wiki</a> "
203
+@ html "<a href=''$baseurl/wiki''>Wiki</a> "
204204
@ }
205205
@ if {[hascap s]} {
206
-@ html "<a href="$baseurl/setup">Admin</a> "
206
+@ html "<a href=''$baseurl/setup''>Admin</a> "
207207
@ } elseif {[hascap a]} {
208
-@ html "<a href="$baseurl/setup_ulist">Users</a> "
208
+@ html "<a href=''$baseurl/setup_ulist''>Users</a> "
209209
@ }
210210
@ if {[info exists login]} {
211
-@ html "<a href="$baseurl/login">Logout</a> "
211
+@ html "<a href=''$baseurl/login''>Logout</a> "
212212
@ } else {
213
-@ html "<a href="$baseurl/login">Login</a> "
213
+@ html "<a href=''$baseurl/login''>Login</a> "
214214
@ }
215215
@ </th1></div>
216216
@ ');
217217
@ REPLACE INTO config VALUES('footer','<div class="footer">
218218
@ Fossil version $manifest_version $manifest_date
@@ -367,11 +367,11 @@
367367
@ REPLACE INTO config VALUES('header','<html>
368368
@ <head>
369369
@ <title>$<project_name>: $<title></title>
370370
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
371371
@ href="$baseurl/timeline.rss">
372
-@ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
372
+@ <link rel="stylesheet" href="$baseurl/style.css?tan" type="text/css"
373373
@ media="screen">
374374
@ </head>
375375
@ <body>
376376
@ <div class="header">
377377
@ <div class="title">$<title></div>
@@ -384,37 +384,37 @@
384384
@ puts "Not logged in"
385385
@ }
386386
@ </th1></nobr></div>
387387
@ </div>
388388
@ <div class="mainmenu"><th1>
389
-@ html "<a href="$baseurl$index_page">Home</a> "
389
+@ html "<a href=''$baseurl$index_page''>Home</a> "
390390
@ if {[anycap jor]} {
391
-@ html "<a href="$baseurl/timeline">Timeline</a> "
391
+@ html "<a href=''$baseurl/timeline''>Timeline</a> "
392392
@ }
393393
@ if {[hascap oh]} {
394
-@ html "<a href="$baseurl/dir">Files</a> "
394
+@ html "<a href=''$baseurl/dir?ci=tip''>Files</a> "
395395
@ }
396396
@ if {[hascap o]} {
397
-@ html "<a href="$baseurl/leaves">Leaves</a> "
398
-@ html "<a href="$baseurl/brlist">Branches</a> "
399
-@ html "<a href="$baseurl/taglist">Tags</a> "
397
+@ html "<a href=''$baseurl/leaves''>Leaves</a> "
398
+@ html "<a href=''$baseurl/brlist''>Branches</a> "
399
+@ html "<a href=''$baseurl/taglist''>Tags</a> "
400400
@ }
401401
@ if {[hascap r]} {
402
-@ html "<a href="$baseurl/reportlist">Tickets</a> "
402
+@ html "<a href=''$baseurl/reportlist''>Tickets</a> "
403403
@ }
404404
@ if {[hascap j]} {
405
-@ html "<a href="$baseurl/wiki">Wiki</a> "
405
+@ html "<a href=''$baseurl/wiki''>Wiki</a> "
406406
@ }
407407
@ if {[hascap s]} {
408
-@ html "<a href="$baseurl/setup">Admin</a> "
408
+@ html "<a href=''$baseurl/setup''>Admin</a> "
409409
@ } elseif {[hascap a]} {
410
-@ html "<a href="$baseurl/setup_ulist">Users</a> "
410
+@ html "<a href=''$baseurl/setup_ulist''>Users</a> "
411411
@ }
412412
@ if {[info exists login]} {
413
-@ html "<a href="$baseurl/login">Logout</a> "
413
+@ html "<a href=''$baseurl/login''>Logout</a> "
414414
@ } else {
415
-@ html "<a href="$baseurl/login">Login</a> "
415
+@ html "<a href=''$baseurl/login''>Login</a> "
416416
@ }
417417
@ </th1></div>
418418
@ ');
419419
@ REPLACE INTO config VALUES('footer','<div class="footer">
420420
@ Fossil version $manifest_version $manifest_date
@@ -514,11 +514,11 @@
514514
@ div.mainmenu a:hover {
515515
@ color: #eee;
516516
@ background-color: #333;
517517
@ }
518518
@
519
-@ /* Container for the sub-menu and content so they don"t spread
519
+@ /* Container for the sub-menu and content so they don''t spread
520520
@ ** out underneath the main menu */
521521
@ #container {
522522
@ padding-left: 9em;
523523
@ }
524524
@
@@ -600,11 +600,11 @@
600600
@ REPLACE INTO config VALUES('header','<html>
601601
@ <head>
602602
@ <title>$<project_name>: $<title></title>
603603
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
604604
@ href="$baseurl/timeline.rss">
605
-@ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
605
+@ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css"
606606
@ media="screen">
607607
@ </head>
608608
@ <body>
609609
@ <div class="header">
610610
@ <div class="logo">
@@ -619,37 +619,37 @@
619619
@ puts "Not logged in"
620620
@ }
621621
@ </th1></nobr></div>
622622
@ </div>
623623
@ <div class="mainmenu"><ul><th1>
624
-@ html "<li><a href="$baseurl$index_page">Home</a></li>"
624
+@ html "<li><a href=''$baseurl$index_page''>Home</a></li>"
625625
@ if {[anycap jor]} {
626
-@ html "<li><a href="$baseurl/timeline">Timeline</a></li>"
626
+@ html "<li><a href=''$baseurl/timeline''>Timeline</a></li>"
627627
@ }
628628
@ if {[hascap oh]} {
629
-@ html "<li><a href="$baseurl/dir">Files</a></li>"
629
+@ html "<li><a href=''$baseurl/dir?ci=tip''>Files</a></li>"
630630
@ }
631631
@ if {[hascap o]} {
632
-@ html "<li><a href="$baseurl/leaves">Leaves</a></li>"
633
-@ html "<li><a href="$baseurl/brlist">Branches</a></li>"
634
-@ html "<li><a href="$baseurl/taglist">Tags</a></li>"
632
+@ html "<li><a href=''$baseurl/leaves''>Leaves</a></li>"
633
+@ html "<li><a href=''$baseurl/brlist''>Branches</a></li>"
634
+@ html "<li><a href=''$baseurl/taglist''>Tags</a></li>"
635635
@ }
636636
@ if {[hascap r]} {
637
-@ html "<li><a href="$baseurl/reportlist">Tickets</a></li>"
637
+@ html "<li><a href=''$baseurl/reportlist''>Tickets</a></li>"
638638
@ }
639639
@ if {[hascap j]} {
640
-@ html "<li><a href="$baseurl/wiki">Wiki</a></li>"
640
+@ html "<li><a href=''$baseurl/wiki''>Wiki</a></li>"
641641
@ }
642642
@ if {[hascap s]} {
643
-@ html "<li><a href="$baseurl/setup">Admin</a></li>"
643
+@ html "<li><a href=''$baseurl/setup''>Admin</a></li>"
644644
@ } elseif {[hascap a]} {
645
-@ html "<li><a href="$baseurl/setup_ulist">Users</a></li>"
645
+@ html "<li><a href=''$baseurl/setup_ulist''>Users</a></li>"
646646
@ }
647647
@ if {[info exists login]} {
648
-@ html "<li><a href="$baseurl/login">Logout</a></li>"
648
+@ html "<li><a href=''$baseurl/login''>Logout</a></li>"
649649
@ } else {
650
-@ html "<li><a href="$baseurl/login">Login</a></li>"
650
+@ html "<li><a href=''$baseurl/login''>Login</a></li>"
651651
@ }
652652
@ </th1></ul></div>
653653
@ <div id="container">
654654
@ ');
655655
@ REPLACE INTO config VALUES('footer','</div>
656656
--- src/skins.c
+++ src/skins.c
@@ -162,11 +162,11 @@
162 @ REPLACE INTO config VALUES('header','<html>
163 @ <head>
164 @ <title>$<project_name>: $<title></title>
165 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
166 @ href="$baseurl/timeline.rss">
167 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
168 @ media="screen">
169 @ </head>
170 @ <body>
171 @ <div class="header">
172 @ <div class="logo">
@@ -182,37 +182,37 @@
182 @ puts "Not logged in"
183 @ }
184 @ </th1></nobr></div>
185 @ </div>
186 @ <div class="mainmenu"><th1>
187 @ html "<a href="$baseurl$index_page">Home</a> "
188 @ if {[anycap jor]} {
189 @ html "<a href="$baseurl/timeline">Timeline</a> "
190 @ }
191 @ if {[hascap oh]} {
192 @ html "<a href="$baseurl/dir">Files</a> "
193 @ }
194 @ if {[hascap o]} {
195 @ html "<a href="$baseurl/leaves">Leaves</a> "
196 @ html "<a href="$baseurl/brlist">Branches</a> "
197 @ html "<a href="$baseurl/taglist">Tags</a> "
198 @ }
199 @ if {[hascap r]} {
200 @ html "<a href="$baseurl/reportlist">Tickets</a> "
201 @ }
202 @ if {[hascap j]} {
203 @ html "<a href="$baseurl/wiki">Wiki</a> "
204 @ }
205 @ if {[hascap s]} {
206 @ html "<a href="$baseurl/setup">Admin</a> "
207 @ } elseif {[hascap a]} {
208 @ html "<a href="$baseurl/setup_ulist">Users</a> "
209 @ }
210 @ if {[info exists login]} {
211 @ html "<a href="$baseurl/login">Logout</a> "
212 @ } else {
213 @ html "<a href="$baseurl/login">Login</a> "
214 @ }
215 @ </th1></div>
216 @ ');
217 @ REPLACE INTO config VALUES('footer','<div class="footer">
218 @ Fossil version $manifest_version $manifest_date
@@ -367,11 +367,11 @@
367 @ REPLACE INTO config VALUES('header','<html>
368 @ <head>
369 @ <title>$<project_name>: $<title></title>
370 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
371 @ href="$baseurl/timeline.rss">
372 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
373 @ media="screen">
374 @ </head>
375 @ <body>
376 @ <div class="header">
377 @ <div class="title">$<title></div>
@@ -384,37 +384,37 @@
384 @ puts "Not logged in"
385 @ }
386 @ </th1></nobr></div>
387 @ </div>
388 @ <div class="mainmenu"><th1>
389 @ html "<a href="$baseurl$index_page">Home</a> "
390 @ if {[anycap jor]} {
391 @ html "<a href="$baseurl/timeline">Timeline</a> "
392 @ }
393 @ if {[hascap oh]} {
394 @ html "<a href="$baseurl/dir">Files</a> "
395 @ }
396 @ if {[hascap o]} {
397 @ html "<a href="$baseurl/leaves">Leaves</a> "
398 @ html "<a href="$baseurl/brlist">Branches</a> "
399 @ html "<a href="$baseurl/taglist">Tags</a> "
400 @ }
401 @ if {[hascap r]} {
402 @ html "<a href="$baseurl/reportlist">Tickets</a> "
403 @ }
404 @ if {[hascap j]} {
405 @ html "<a href="$baseurl/wiki">Wiki</a> "
406 @ }
407 @ if {[hascap s]} {
408 @ html "<a href="$baseurl/setup">Admin</a> "
409 @ } elseif {[hascap a]} {
410 @ html "<a href="$baseurl/setup_ulist">Users</a> "
411 @ }
412 @ if {[info exists login]} {
413 @ html "<a href="$baseurl/login">Logout</a> "
414 @ } else {
415 @ html "<a href="$baseurl/login">Login</a> "
416 @ }
417 @ </th1></div>
418 @ ');
419 @ REPLACE INTO config VALUES('footer','<div class="footer">
420 @ Fossil version $manifest_version $manifest_date
@@ -514,11 +514,11 @@
514 @ div.mainmenu a:hover {
515 @ color: #eee;
516 @ background-color: #333;
517 @ }
518 @
519 @ /* Container for the sub-menu and content so they don"t spread
520 @ ** out underneath the main menu */
521 @ #container {
522 @ padding-left: 9em;
523 @ }
524 @
@@ -600,11 +600,11 @@
600 @ REPLACE INTO config VALUES('header','<html>
601 @ <head>
602 @ <title>$<project_name>: $<title></title>
603 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
604 @ href="$baseurl/timeline.rss">
605 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
606 @ media="screen">
607 @ </head>
608 @ <body>
609 @ <div class="header">
610 @ <div class="logo">
@@ -619,37 +619,37 @@
619 @ puts "Not logged in"
620 @ }
621 @ </th1></nobr></div>
622 @ </div>
623 @ <div class="mainmenu"><ul><th1>
624 @ html "<li><a href="$baseurl$index_page">Home</a></li>"
625 @ if {[anycap jor]} {
626 @ html "<li><a href="$baseurl/timeline">Timeline</a></li>"
627 @ }
628 @ if {[hascap oh]} {
629 @ html "<li><a href="$baseurl/dir">Files</a></li>"
630 @ }
631 @ if {[hascap o]} {
632 @ html "<li><a href="$baseurl/leaves">Leaves</a></li>"
633 @ html "<li><a href="$baseurl/brlist">Branches</a></li>"
634 @ html "<li><a href="$baseurl/taglist">Tags</a></li>"
635 @ }
636 @ if {[hascap r]} {
637 @ html "<li><a href="$baseurl/reportlist">Tickets</a></li>"
638 @ }
639 @ if {[hascap j]} {
640 @ html "<li><a href="$baseurl/wiki">Wiki</a></li>"
641 @ }
642 @ if {[hascap s]} {
643 @ html "<li><a href="$baseurl/setup">Admin</a></li>"
644 @ } elseif {[hascap a]} {
645 @ html "<li><a href="$baseurl/setup_ulist">Users</a></li>"
646 @ }
647 @ if {[info exists login]} {
648 @ html "<li><a href="$baseurl/login">Logout</a></li>"
649 @ } else {
650 @ html "<li><a href="$baseurl/login">Login</a></li>"
651 @ }
652 @ </th1></ul></div>
653 @ <div id="container">
654 @ ');
655 @ REPLACE INTO config VALUES('footer','</div>
656
--- src/skins.c
+++ src/skins.c
@@ -162,11 +162,11 @@
162 @ REPLACE INTO config VALUES('header','<html>
163 @ <head>
164 @ <title>$<project_name>: $<title></title>
165 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
166 @ href="$baseurl/timeline.rss">
167 @ <link rel="stylesheet" href="$baseurl/style.css?blackwhite" type="text/css"
168 @ media="screen">
169 @ </head>
170 @ <body>
171 @ <div class="header">
172 @ <div class="logo">
@@ -182,37 +182,37 @@
182 @ puts "Not logged in"
183 @ }
184 @ </th1></nobr></div>
185 @ </div>
186 @ <div class="mainmenu"><th1>
187 @ html "<a href=''$baseurl$index_page''>Home</a> "
188 @ if {[anycap jor]} {
189 @ html "<a href=''$baseurl/timeline''>Timeline</a> "
190 @ }
191 @ if {[hascap oh]} {
192 @ html "<a href=''$baseurl/dir?ci=tip''>Files</a> "
193 @ }
194 @ if {[hascap o]} {
195 @ html "<a href=''$baseurl/leaves''>Leaves</a> "
196 @ html "<a href=''$baseurl/brlist''>Branches</a> "
197 @ html "<a href=''$baseurl/taglist''>Tags</a> "
198 @ }
199 @ if {[hascap r]} {
200 @ html "<a href=''$baseurl/reportlist''>Tickets</a> "
201 @ }
202 @ if {[hascap j]} {
203 @ html "<a href=''$baseurl/wiki''>Wiki</a> "
204 @ }
205 @ if {[hascap s]} {
206 @ html "<a href=''$baseurl/setup''>Admin</a> "
207 @ } elseif {[hascap a]} {
208 @ html "<a href=''$baseurl/setup_ulist''>Users</a> "
209 @ }
210 @ if {[info exists login]} {
211 @ html "<a href=''$baseurl/login''>Logout</a> "
212 @ } else {
213 @ html "<a href=''$baseurl/login''>Login</a> "
214 @ }
215 @ </th1></div>
216 @ ');
217 @ REPLACE INTO config VALUES('footer','<div class="footer">
218 @ Fossil version $manifest_version $manifest_date
@@ -367,11 +367,11 @@
367 @ REPLACE INTO config VALUES('header','<html>
368 @ <head>
369 @ <title>$<project_name>: $<title></title>
370 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
371 @ href="$baseurl/timeline.rss">
372 @ <link rel="stylesheet" href="$baseurl/style.css?tan" type="text/css"
373 @ media="screen">
374 @ </head>
375 @ <body>
376 @ <div class="header">
377 @ <div class="title">$<title></div>
@@ -384,37 +384,37 @@
384 @ puts "Not logged in"
385 @ }
386 @ </th1></nobr></div>
387 @ </div>
388 @ <div class="mainmenu"><th1>
389 @ html "<a href=''$baseurl$index_page''>Home</a> "
390 @ if {[anycap jor]} {
391 @ html "<a href=''$baseurl/timeline''>Timeline</a> "
392 @ }
393 @ if {[hascap oh]} {
394 @ html "<a href=''$baseurl/dir?ci=tip''>Files</a> "
395 @ }
396 @ if {[hascap o]} {
397 @ html "<a href=''$baseurl/leaves''>Leaves</a> "
398 @ html "<a href=''$baseurl/brlist''>Branches</a> "
399 @ html "<a href=''$baseurl/taglist''>Tags</a> "
400 @ }
401 @ if {[hascap r]} {
402 @ html "<a href=''$baseurl/reportlist''>Tickets</a> "
403 @ }
404 @ if {[hascap j]} {
405 @ html "<a href=''$baseurl/wiki''>Wiki</a> "
406 @ }
407 @ if {[hascap s]} {
408 @ html "<a href=''$baseurl/setup''>Admin</a> "
409 @ } elseif {[hascap a]} {
410 @ html "<a href=''$baseurl/setup_ulist''>Users</a> "
411 @ }
412 @ if {[info exists login]} {
413 @ html "<a href=''$baseurl/login''>Logout</a> "
414 @ } else {
415 @ html "<a href=''$baseurl/login''>Login</a> "
416 @ }
417 @ </th1></div>
418 @ ');
419 @ REPLACE INTO config VALUES('footer','<div class="footer">
420 @ Fossil version $manifest_version $manifest_date
@@ -514,11 +514,11 @@
514 @ div.mainmenu a:hover {
515 @ color: #eee;
516 @ background-color: #333;
517 @ }
518 @
519 @ /* Container for the sub-menu and content so they don''t spread
520 @ ** out underneath the main menu */
521 @ #container {
522 @ padding-left: 9em;
523 @ }
524 @
@@ -600,11 +600,11 @@
600 @ REPLACE INTO config VALUES('header','<html>
601 @ <head>
602 @ <title>$<project_name>: $<title></title>
603 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
604 @ href="$baseurl/timeline.rss">
605 @ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css"
606 @ media="screen">
607 @ </head>
608 @ <body>
609 @ <div class="header">
610 @ <div class="logo">
@@ -619,37 +619,37 @@
619 @ puts "Not logged in"
620 @ }
621 @ </th1></nobr></div>
622 @ </div>
623 @ <div class="mainmenu"><ul><th1>
624 @ html "<li><a href=''$baseurl$index_page''>Home</a></li>"
625 @ if {[anycap jor]} {
626 @ html "<li><a href=''$baseurl/timeline''>Timeline</a></li>"
627 @ }
628 @ if {[hascap oh]} {
629 @ html "<li><a href=''$baseurl/dir?ci=tip''>Files</a></li>"
630 @ }
631 @ if {[hascap o]} {
632 @ html "<li><a href=''$baseurl/leaves''>Leaves</a></li>"
633 @ html "<li><a href=''$baseurl/brlist''>Branches</a></li>"
634 @ html "<li><a href=''$baseurl/taglist''>Tags</a></li>"
635 @ }
636 @ if {[hascap r]} {
637 @ html "<li><a href=''$baseurl/reportlist''>Tickets</a></li>"
638 @ }
639 @ if {[hascap j]} {
640 @ html "<li><a href=''$baseurl/wiki''>Wiki</a></li>"
641 @ }
642 @ if {[hascap s]} {
643 @ html "<li><a href=''$baseurl/setup''>Admin</a></li>"
644 @ } elseif {[hascap a]} {
645 @ html "<li><a href=''$baseurl/setup_ulist''>Users</a></li>"
646 @ }
647 @ if {[info exists login]} {
648 @ html "<li><a href=''$baseurl/login''>Logout</a></li>"
649 @ } else {
650 @ html "<li><a href=''$baseurl/login''>Login</a></li>"
651 @ }
652 @ </th1></ul></div>
653 @ <div id="container">
654 @ ');
655 @ REPLACE INTO config VALUES('footer','</div>
656
+2 -2
--- src/style.c
+++ src/style.c
@@ -188,11 +188,11 @@
188188
@ <html>
189189
@ <head>
190190
@ <title>$<project_name>: $<title></title>
191191
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
192192
@ href="$baseurl/timeline.rss">
193
-@ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
193
+@ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
194194
@ media="screen">
195195
@ </head>
196196
@ <body>
197197
@ <div class="header">
198198
@ <div class="logo">
@@ -212,11 +212,11 @@
212212
@ html "<a href='$baseurl$index_page'>Home</a> "
213213
@ if {[anycap jor]} {
214214
@ html "<a href='$baseurl/timeline'>Timeline</a> "
215215
@ }
216216
@ if {[hascap oh]} {
217
-@ html "<a href='$baseurl/dir'>Files</a> "
217
+@ html "<a href='$baseurl/dir?ci=tip'>Files</a> "
218218
@ }
219219
@ if {[hascap o]} {
220220
@ html "<a href='$baseurl/leaves'>Leaves</a> "
221221
@ html "<a href='$baseurl/brlist'>Branches</a> "
222222
@ html "<a href='$baseurl/taglist'>Tags</a> "
223223
--- src/style.c
+++ src/style.c
@@ -188,11 +188,11 @@
188 @ <html>
189 @ <head>
190 @ <title>$<project_name>: $<title></title>
191 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
192 @ href="$baseurl/timeline.rss">
193 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
194 @ media="screen">
195 @ </head>
196 @ <body>
197 @ <div class="header">
198 @ <div class="logo">
@@ -212,11 +212,11 @@
212 @ html "<a href='$baseurl$index_page'>Home</a> "
213 @ if {[anycap jor]} {
214 @ html "<a href='$baseurl/timeline'>Timeline</a> "
215 @ }
216 @ if {[hascap oh]} {
217 @ html "<a href='$baseurl/dir'>Files</a> "
218 @ }
219 @ if {[hascap o]} {
220 @ html "<a href='$baseurl/leaves'>Leaves</a> "
221 @ html "<a href='$baseurl/brlist'>Branches</a> "
222 @ html "<a href='$baseurl/taglist'>Tags</a> "
223
--- src/style.c
+++ src/style.c
@@ -188,11 +188,11 @@
188 @ <html>
189 @ <head>
190 @ <title>$<project_name>: $<title></title>
191 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
192 @ href="$baseurl/timeline.rss">
193 @ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
194 @ media="screen">
195 @ </head>
196 @ <body>
197 @ <div class="header">
198 @ <div class="logo">
@@ -212,11 +212,11 @@
212 @ html "<a href='$baseurl$index_page'>Home</a> "
213 @ if {[anycap jor]} {
214 @ html "<a href='$baseurl/timeline'>Timeline</a> "
215 @ }
216 @ if {[hascap oh]} {
217 @ html "<a href='$baseurl/dir?ci=tip'>Files</a> "
218 @ }
219 @ if {[hascap o]} {
220 @ html "<a href='$baseurl/leaves'>Leaves</a> "
221 @ html "<a href='$baseurl/brlist'>Branches</a> "
222 @ html "<a href='$baseurl/taglist'>Tags</a> "
223
+6 -1
--- src/tag.c
+++ src/tag.c
@@ -200,10 +200,15 @@
200200
break;
201201
}
202202
}
203203
if( zCol ){
204204
db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
205
+ if( tagid==TAG_COMMENT ){
206
+ char *zCopy = mprintf("%s", zValue);
207
+ wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
208
+ free(zCopy);
209
+ }
205210
}
206211
if( tagid==TAG_DATE ){
207212
db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d",
208213
zValue, rid);
209214
}
@@ -563,11 +568,11 @@
563568
if( !g.okRead ){ login_needed(); return; }
564569
565570
style_header("Tagged Check-ins");
566571
style_submenu_element("List", "List", "taglist");
567572
login_anonymous_available();
568
- @ <h2>Check-ins with non-propagating tags:</t2>
573
+ @ <h2>Check-ins with non-propagating tags:</h2>
569574
db_prepare(&q,
570575
"%s AND blob.rid IN (SELECT rid FROM tagxref"
571576
" WHERE tagtype=1 AND srcid>0"
572577
" AND tagid IN (SELECT tagid FROM tag "
573578
" WHERE tagname GLOB 'sym-*'))"
574579
--- src/tag.c
+++ src/tag.c
@@ -200,10 +200,15 @@
200 break;
201 }
202 }
203 if( zCol ){
204 db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
 
 
 
 
 
205 }
206 if( tagid==TAG_DATE ){
207 db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d",
208 zValue, rid);
209 }
@@ -563,11 +568,11 @@
563 if( !g.okRead ){ login_needed(); return; }
564
565 style_header("Tagged Check-ins");
566 style_submenu_element("List", "List", "taglist");
567 login_anonymous_available();
568 @ <h2>Check-ins with non-propagating tags:</t2>
569 db_prepare(&q,
570 "%s AND blob.rid IN (SELECT rid FROM tagxref"
571 " WHERE tagtype=1 AND srcid>0"
572 " AND tagid IN (SELECT tagid FROM tag "
573 " WHERE tagname GLOB 'sym-*'))"
574
--- src/tag.c
+++ src/tag.c
@@ -200,10 +200,15 @@
200 break;
201 }
202 }
203 if( zCol ){
204 db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
205 if( tagid==TAG_COMMENT ){
206 char *zCopy = mprintf("%s", zValue);
207 wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
208 free(zCopy);
209 }
210 }
211 if( tagid==TAG_DATE ){
212 db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d",
213 zValue, rid);
214 }
@@ -563,11 +568,11 @@
568 if( !g.okRead ){ login_needed(); return; }
569
570 style_header("Tagged Check-ins");
571 style_submenu_element("List", "List", "taglist");
572 login_anonymous_available();
573 @ <h2>Check-ins with non-propagating tags:</h2>
574 db_prepare(&q,
575 "%s AND blob.rid IN (SELECT rid FROM tagxref"
576 " WHERE tagtype=1 AND srcid>0"
577 " AND tagid IN (SELECT tagid FROM tag "
578 " WHERE tagname GLOB 'sym-*'))"
579
+42 -35
--- src/timeline.c
+++ src/timeline.c
@@ -200,14 +200,10 @@
200200
if( tmFlags & TIMELINE_GRAPH ){
201201
pGraph = graph_init();
202202
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
203203
}
204204
205
- db_multi_exec(
206
- "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);"
207
- "DELETE FROM seen;"
208
- );
209205
@ <table cellspacing=0 border=0 cellpadding=0>
210206
blob_zero(&comment);
211207
while( db_step(pQuery)==SQLITE_ROW ){
212208
int rid = db_column_int(pQuery, 0);
213209
const char *zUuid = db_column_text(pQuery, 1);
@@ -241,11 +237,10 @@
241237
}
242238
if( strcmp(zType,"div")==0 ){
243239
@ <tr><td colspan=3><hr></td></tr>
244240
continue;
245241
}
246
- db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
247242
if( memcmp(zDate, zPrevDate, 10) ){
248243
sprintf(zPrevDate, "%.10s", zDate);
249244
@ <tr><td>
250245
@ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
251246
@ </td></tr>
@@ -253,11 +248,39 @@
253248
memcpy(zTime, &zDate[11], 5);
254249
zTime[5] = 0;
255250
@ <tr>
256251
@ <td valign="top" align="right">%s(zTime)</td>
257252
@ <td width="20" align="left" valign="top">
258
- @ <div id="m%d(rid)"></div>
253
+ if( pGraph && zType[0]=='c' ){
254
+ int nParent = 0;
255
+ int aParent[32];
256
+ const char *zBr;
257
+ int gidx;
258
+ static Stmt qparent;
259
+ static Stmt qbranch;
260
+ db_static_prepare(&qparent,
261
+ "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC"
262
+ );
263
+ db_static_prepare(&qbranch,
264
+ "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
265
+ TAG_BRANCH
266
+ );
267
+ db_bind_int(&qparent, ":rid", rid);
268
+ while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
269
+ aParent[nParent++] = db_column_int(&qparent, 0);
270
+ }
271
+ db_reset(&qparent);
272
+ db_bind_int(&qbranch, ":rid", rid);
273
+ if( db_step(&qbranch)==SQLITE_ROW ){
274
+ zBr = db_column_text(&qbranch, 0);
275
+ }else{
276
+ zBr = "trunk";
277
+ }
278
+ gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr);
279
+ db_reset(&qbranch);
280
+ @ <div id="m%d(gidx)"></div>
281
+ }
259282
if( zBgClr && zBgClr[0] ){
260283
@ <td valign="top" align="left" bgcolor="%h(zBgClr)">
261284
}else{
262285
@ <td valign="top" align="left">
263286
}
@@ -290,37 +313,10 @@
290313
int i;
291314
for(i=0; i<nTag; i++){
292315
@ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b>
293316
}
294317
}
295
- if( pGraph ){
296
- int nParent = 0;
297
- int aParent[32];
298
- const char *zBr;
299
- static Stmt qparent;
300
- static Stmt qbranch;
301
- db_static_prepare(&qparent,
302
- "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC"
303
- );
304
- db_static_prepare(&qbranch,
305
- "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
306
- TAG_BRANCH
307
- );
308
- db_bind_int(&qparent, ":rid", rid);
309
- while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
310
- aParent[nParent++] = db_column_int(&qparent, 0);
311
- }
312
- db_reset(&qparent);
313
- db_bind_int(&qbranch, ":rid", rid);
314
- if( db_step(&qbranch)==SQLITE_ROW ){
315
- zBr = db_column_text(&qbranch, 0);
316
- }else{
317
- zBr = "trunk";
318
- }
319
- graph_add_row(pGraph, rid, isLeaf, nParent, aParent, zBr);
320
- db_reset(&qbranch);
321
- }
322318
}else if( (tmFlags & TIMELINE_ARTID)!=0 ){
323319
hyperlink_to_uuid(zUuid);
324320
}
325321
db_column_blob(pQuery, commentColumn, &comment);
326322
if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
@@ -358,19 +354,27 @@
358354
}else{
359355
@ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
360356
}
361357
}
362358
@ </table>
359
+ timeline_output_graph_javascript(pGraph);
360
+}
361
+
362
+/*
363
+** Generate all of the necessary javascript to generate a timeline
364
+** graph.
365
+*/
366
+void timeline_output_graph_javascript(GraphContext *pGraph){
363367
if( pGraph && pGraph->nErr==0 ){
364368
GraphRow *pRow;
365369
int i;
366370
char cSep;
367371
@ <script type="text/JavaScript">
368372
cgi_printf("var rowinfo = [\n");
369373
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
370374
cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
371
- pRow->rid,
375
+ pRow->idx,
372376
pRow->iRail,
373377
pRow->bDescender,
374378
pRow->mergeOut,
375379
pRow->mergeUpto,
376380
pRow->aiRaiser[pRow->iRail]
@@ -698,11 +702,11 @@
698702
if( zType[0]=='a' ){
699703
tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH;
700704
}else{
701705
tmFlags = TIMELINE_GRAPH;
702706
}
703
- if( P("ng")!=0 ){
707
+ if( P("ng")!=0 || zSearch!=0 ){
704708
tmFlags &= ~TIMELINE_GRAPH;
705709
}
706710
707711
style_header("Timeline");
708712
login_anonymous_available();
@@ -891,10 +895,13 @@
891895
}else if( zBefore ){
892896
blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
893897
}else if( zCirca ){
894898
blob_appendf(&desc, " occurring around %h.<br>", zCirca);
895899
}
900
+ if( zSearch ){
901
+ blob_appendf(&desc, " matching \"%h\"", zSearch);
902
+ }
896903
if( g.okHistory ){
897904
if( zAfter || n==nEntry ){
898905
zDate = db_text(0, "SELECT min(timestamp) FROM timeline");
899906
timeline_submenu(&url, "Older", "b", zDate, "a");
900907
free(zDate);
901908
--- src/timeline.c
+++ src/timeline.c
@@ -200,14 +200,10 @@
200 if( tmFlags & TIMELINE_GRAPH ){
201 pGraph = graph_init();
202 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
203 }
204
205 db_multi_exec(
206 "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);"
207 "DELETE FROM seen;"
208 );
209 @ <table cellspacing=0 border=0 cellpadding=0>
210 blob_zero(&comment);
211 while( db_step(pQuery)==SQLITE_ROW ){
212 int rid = db_column_int(pQuery, 0);
213 const char *zUuid = db_column_text(pQuery, 1);
@@ -241,11 +237,10 @@
241 }
242 if( strcmp(zType,"div")==0 ){
243 @ <tr><td colspan=3><hr></td></tr>
244 continue;
245 }
246 db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
247 if( memcmp(zDate, zPrevDate, 10) ){
248 sprintf(zPrevDate, "%.10s", zDate);
249 @ <tr><td>
250 @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
251 @ </td></tr>
@@ -253,11 +248,39 @@
253 memcpy(zTime, &zDate[11], 5);
254 zTime[5] = 0;
255 @ <tr>
256 @ <td valign="top" align="right">%s(zTime)</td>
257 @ <td width="20" align="left" valign="top">
258 @ <div id="m%d(rid)"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259 if( zBgClr && zBgClr[0] ){
260 @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
261 }else{
262 @ <td valign="top" align="left">
263 }
@@ -290,37 +313,10 @@
290 int i;
291 for(i=0; i<nTag; i++){
292 @ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b>
293 }
294 }
295 if( pGraph ){
296 int nParent = 0;
297 int aParent[32];
298 const char *zBr;
299 static Stmt qparent;
300 static Stmt qbranch;
301 db_static_prepare(&qparent,
302 "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC"
303 );
304 db_static_prepare(&qbranch,
305 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
306 TAG_BRANCH
307 );
308 db_bind_int(&qparent, ":rid", rid);
309 while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
310 aParent[nParent++] = db_column_int(&qparent, 0);
311 }
312 db_reset(&qparent);
313 db_bind_int(&qbranch, ":rid", rid);
314 if( db_step(&qbranch)==SQLITE_ROW ){
315 zBr = db_column_text(&qbranch, 0);
316 }else{
317 zBr = "trunk";
318 }
319 graph_add_row(pGraph, rid, isLeaf, nParent, aParent, zBr);
320 db_reset(&qbranch);
321 }
322 }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
323 hyperlink_to_uuid(zUuid);
324 }
325 db_column_blob(pQuery, commentColumn, &comment);
326 if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
@@ -358,19 +354,27 @@
358 }else{
359 @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
360 }
361 }
362 @ </table>
 
 
 
 
 
 
 
 
363 if( pGraph && pGraph->nErr==0 ){
364 GraphRow *pRow;
365 int i;
366 char cSep;
367 @ <script type="text/JavaScript">
368 cgi_printf("var rowinfo = [\n");
369 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
370 cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
371 pRow->rid,
372 pRow->iRail,
373 pRow->bDescender,
374 pRow->mergeOut,
375 pRow->mergeUpto,
376 pRow->aiRaiser[pRow->iRail]
@@ -698,11 +702,11 @@
698 if( zType[0]=='a' ){
699 tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH;
700 }else{
701 tmFlags = TIMELINE_GRAPH;
702 }
703 if( P("ng")!=0 ){
704 tmFlags &= ~TIMELINE_GRAPH;
705 }
706
707 style_header("Timeline");
708 login_anonymous_available();
@@ -891,10 +895,13 @@
891 }else if( zBefore ){
892 blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
893 }else if( zCirca ){
894 blob_appendf(&desc, " occurring around %h.<br>", zCirca);
895 }
 
 
 
896 if( g.okHistory ){
897 if( zAfter || n==nEntry ){
898 zDate = db_text(0, "SELECT min(timestamp) FROM timeline");
899 timeline_submenu(&url, "Older", "b", zDate, "a");
900 free(zDate);
901
--- src/timeline.c
+++ src/timeline.c
@@ -200,14 +200,10 @@
200 if( tmFlags & TIMELINE_GRAPH ){
201 pGraph = graph_init();
202 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
203 }
204
 
 
 
 
205 @ <table cellspacing=0 border=0 cellpadding=0>
206 blob_zero(&comment);
207 while( db_step(pQuery)==SQLITE_ROW ){
208 int rid = db_column_int(pQuery, 0);
209 const char *zUuid = db_column_text(pQuery, 1);
@@ -241,11 +237,10 @@
237 }
238 if( strcmp(zType,"div")==0 ){
239 @ <tr><td colspan=3><hr></td></tr>
240 continue;
241 }
 
242 if( memcmp(zDate, zPrevDate, 10) ){
243 sprintf(zPrevDate, "%.10s", zDate);
244 @ <tr><td>
245 @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
246 @ </td></tr>
@@ -253,11 +248,39 @@
248 memcpy(zTime, &zDate[11], 5);
249 zTime[5] = 0;
250 @ <tr>
251 @ <td valign="top" align="right">%s(zTime)</td>
252 @ <td width="20" align="left" valign="top">
253 if( pGraph && zType[0]=='c' ){
254 int nParent = 0;
255 int aParent[32];
256 const char *zBr;
257 int gidx;
258 static Stmt qparent;
259 static Stmt qbranch;
260 db_static_prepare(&qparent,
261 "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC"
262 );
263 db_static_prepare(&qbranch,
264 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
265 TAG_BRANCH
266 );
267 db_bind_int(&qparent, ":rid", rid);
268 while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
269 aParent[nParent++] = db_column_int(&qparent, 0);
270 }
271 db_reset(&qparent);
272 db_bind_int(&qbranch, ":rid", rid);
273 if( db_step(&qbranch)==SQLITE_ROW ){
274 zBr = db_column_text(&qbranch, 0);
275 }else{
276 zBr = "trunk";
277 }
278 gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr);
279 db_reset(&qbranch);
280 @ <div id="m%d(gidx)"></div>
281 }
282 if( zBgClr && zBgClr[0] ){
283 @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
284 }else{
285 @ <td valign="top" align="left">
286 }
@@ -290,37 +313,10 @@
313 int i;
314 for(i=0; i<nTag; i++){
315 @ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b>
316 }
317 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318 }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
319 hyperlink_to_uuid(zUuid);
320 }
321 db_column_blob(pQuery, commentColumn, &comment);
322 if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
@@ -358,19 +354,27 @@
354 }else{
355 @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
356 }
357 }
358 @ </table>
359 timeline_output_graph_javascript(pGraph);
360 }
361
362 /*
363 ** Generate all of the necessary javascript to generate a timeline
364 ** graph.
365 */
366 void timeline_output_graph_javascript(GraphContext *pGraph){
367 if( pGraph && pGraph->nErr==0 ){
368 GraphRow *pRow;
369 int i;
370 char cSep;
371 @ <script type="text/JavaScript">
372 cgi_printf("var rowinfo = [\n");
373 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
374 cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
375 pRow->idx,
376 pRow->iRail,
377 pRow->bDescender,
378 pRow->mergeOut,
379 pRow->mergeUpto,
380 pRow->aiRaiser[pRow->iRail]
@@ -698,11 +702,11 @@
702 if( zType[0]=='a' ){
703 tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH;
704 }else{
705 tmFlags = TIMELINE_GRAPH;
706 }
707 if( P("ng")!=0 || zSearch!=0 ){
708 tmFlags &= ~TIMELINE_GRAPH;
709 }
710
711 style_header("Timeline");
712 login_anonymous_available();
@@ -891,10 +895,13 @@
895 }else if( zBefore ){
896 blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
897 }else if( zCirca ){
898 blob_appendf(&desc, " occurring around %h.<br>", zCirca);
899 }
900 if( zSearch ){
901 blob_appendf(&desc, " matching \"%h\"", zSearch);
902 }
903 if( g.okHistory ){
904 if( zAfter || n==nEntry ){
905 zDate = db_text(0, "SELECT min(timestamp) FROM timeline");
906 timeline_submenu(&url, "Older", "b", zDate, "a");
907 free(zDate);
908
+136 -60
--- src/tkt.c
+++ src/tkt.c
@@ -165,32 +165,10 @@
165165
for(i=0; (z = cgi_parameter_name(i))!=0; i++){
166166
Th_Store(z, P(z));
167167
}
168168
}
169169
170
-/*
171
-** Rebuild all tickets named in the _pending_ticket table.
172
-**
173
-** This routine is called just prior to commit after new
174
-** out-of-sequence ticket changes have been added.
175
-*/
176
-static int ticket_rebuild_at_commit(void){
177
- Stmt q;
178
- db_multi_exec(
179
- "DELETE FROM ticket WHERE tkt_uuid IN _pending_ticket"
180
- );
181
- db_prepare(&q, "SELECT uuid FROM _pending_ticket");
182
- while( db_step(&q)==SQLITE_ROW ){
183
- const char *zUuid = db_column_text(&q, 0);
184
- ticket_rebuild_entry(zUuid);
185
- }
186
- db_multi_exec(
187
- "DELETE FROM _pending_ticket"
188
- );
189
- return 0;
190
-}
191
-
192170
/*
193171
** Update an entry of the TICKET table according to the information
194172
** in the control file given in p. Attempt to create the appropriate
195173
** TICKET table entry if createFlag is true. If createFlag is false,
196174
** that means we already know the entry exists and so we can save the
@@ -197,11 +175,11 @@
197175
** work of trying to create it.
198176
**
199177
** Return TRUE if a new TICKET entry was created and FALSE if an
200178
** existing entry was revised.
201179
*/
202
-int ticket_insert(const Manifest *p, int createFlag, int checkTime){
180
+int ticket_insert(const Manifest *p, int createFlag, int rid){
203181
Blob sql;
204182
Stmt q;
205183
int i;
206184
const char *zSep;
207185
int rc = 0;
@@ -224,27 +202,20 @@
224202
zName, zName, p->aField[i].zValue);
225203
}else{
226204
if( fieldId(zName)<0 ) continue;
227205
blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue);
228206
}
207
+ if( rid>0 ){
208
+ wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
209
+ }
229210
}
230211
blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime",
231212
p->zTicketUuid);
232213
db_prepare(&q, "%s", blob_str(&sql));
233214
db_bind_double(&q, ":mtime", p->rDate);
234215
db_step(&q);
235216
db_finalize(&q);
236
- if( checkTime && db_changes()==0 ){
237
- static int isInit = 0;
238
- if( !isInit ){
239
- db_multi_exec("CREATE TEMP TABLE _pending_ticket(uuid TEXT UNIQUE)");
240
- db_commit_hook(ticket_rebuild_at_commit, 1);
241
- isInit = 1;
242
- }
243
- db_multi_exec("INSERT OR IGNORE INTO _pending_ticket "
244
- "VALUES(%Q)", p->zTicketUuid);
245
- }
246217
blob_reset(&sql);
247218
return rc;
248219
}
249220
250221
/*
@@ -264,11 +235,11 @@
264235
db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
265236
while( db_step(&q)==SQLITE_ROW ){
266237
int rid = db_column_int(&q, 0);
267238
content_get(rid, &content);
268239
manifest_parse(&manifest, &content);
269
- ticket_insert(&manifest, createFlag, 0);
240
+ ticket_insert(&manifest, createFlag, rid);
270241
manifest_ticket_event(rid, &manifest, createFlag, tagid);
271242
manifest_clear(&manifest);
272243
createFlag = 0;
273244
}
274245
db_finalize(&q);
@@ -325,35 +296,79 @@
325296
**
326297
** View a ticket.
327298
*/
328299
void tktview_page(void){
329300
const char *zScript;
301
+ char *zFullName;
302
+ const char *zUuid = PD("name","");
303
+
330304
login_check_credentials();
331305
if( !g.okRdTkt ){ login_needed(); return; }
332306
if( g.okWrTkt || g.okApndTkt ){
333307
style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
334308
g.zTop, PD("name",""));
335309
}
336310
if( g.okHistory ){
337
- const char *zUuid = PD("name","");
338311
style_submenu_element("History", "History Of This Ticket",
339312
"%s/tkthistory/%T", g.zTop, zUuid);
340313
style_submenu_element("Timeline", "Timeline Of This Ticket",
341314
"%s/tkttimeline/%T", g.zTop, zUuid);
315
+ style_submenu_element("Check-ins", "Check-ins Of This Ticket",
316
+ "%s/tkttimeline/%T?y=ci", g.zTop, zUuid);
342317
}
343318
if( g.okNewTkt ){
344319
style_submenu_element("New Ticket", "Create a new ticket",
345320
"%s/tktnew", g.zTop);
346321
}
322
+ if( g.okApndTkt && g.okAttach ){
323
+ style_submenu_element("Attach", "Add An Attachment",
324
+ "%s/attachadd?tkt=%T&from=%s/tktview%%3fname=%t",
325
+ g.zTop, zUuid, g.zTop, zUuid);
326
+ }
347327
style_header("View Ticket");
348328
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
349329
ticket_init();
350330
initializeVariablesFromDb();
351331
zScript = ticket_viewpage_code();
352332
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
353333
Th_Render(zScript);
354334
if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
335
+
336
+ zFullName = db_text(0,
337
+ "SELECT tkt_uuid FROM ticket"
338
+ " WHERE tkt_uuid GLOB '%q*'", zUuid);
339
+ if( zFullName ){
340
+ int cnt = 0;
341
+ Stmt q;
342
+ db_prepare(&q,
343
+ "SELECT datetime(mtime,'localtime'), filename, user"
344
+ " FROM attachment"
345
+ " WHERE isLatest AND src!='' AND target=%Q"
346
+ " ORDER BY mtime DESC",
347
+ zFullName);
348
+ while( db_step(&q)==SQLITE_ROW ){
349
+ const char *zDate = db_column_text(&q, 0);
350
+ const char *zFile = db_column_text(&q, 1);
351
+ const char *zUser = db_column_text(&q, 2);
352
+ if( cnt==0 ){
353
+ @ <hr><h2>Attachments:</h2>
354
+ @ <ul>
355
+ }
356
+ cnt++;
357
+ @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
358
+ @ %h(zFile)</a> add by %h(zUser) on
359
+ hyperlink_to_date(zDate, ".");
360
+ if( g.okWrTkt && g.okAttach ){
361
+ @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
362
+ }
363
+ }
364
+ if( cnt ){
365
+ @ </ul>
366
+ }
367
+ db_finalize(&q);
368
+ }
369
+
355370
style_footer();
356371
}
357372
358373
/*
359374
** TH command: append_field FIELD STRING
@@ -610,42 +625,78 @@
610625
return 0;
611626
}
612627
613628
/*
614629
** WEBPAGE: tkttimeline
615
-** URL: /tkttimeline?name=TICKETUUID
630
+** URL: /tkttimeline?name=TICKETUUID&y=TYPE
616631
**
617632
** Show the change history for a single ticket in timeline format.
618633
*/
619634
void tkttimeline_page(void){
620635
Stmt q;
621636
char *zTitle;
622637
char *zSQL;
623638
const char *zUuid;
639
+ char *zFullUuid;
624640
int tagid;
641
+ char zGlobPattern[50];
642
+ const char *zType;
625643
626644
login_check_credentials();
627645
if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
628646
zUuid = PD("name","");
647
+ zType = PD("y","a");
648
+ if( zType[0]!='c' ){
649
+ style_submenu_element("Check-ins", "Check-ins",
650
+ "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
651
+ }else{
652
+ style_submenu_element("Timeline", "Timeline",
653
+ "%s/tkttimeline?name=%T", g.zTop, zUuid);
654
+ }
629655
style_submenu_element("History", "History",
630656
"%s/tkthistory/%s", g.zTop, zUuid);
631657
style_submenu_element("Status", "Status",
632658
"%s/info/%s", g.zTop, zUuid);
633
- zTitle = mprintf("Timeline Of Ticket %h", zUuid);
659
+ if( zType[0]=='c' ){
660
+ zTitle = mprintf("Check-Ins Associated With Ticket %h", zUuid);
661
+ }else{
662
+ zTitle = mprintf("Timeline Of Ticket %h", zUuid);
663
+ }
634664
style_header(zTitle);
635665
free(zTitle);
636666
667
+ sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
668
+ canonical16(zGlobPattern, strlen(zGlobPattern));
637669
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
638670
if( tagid==0 ){
639671
@ No such ticket: %h(zUuid)
640672
style_footer();
641673
return;
642674
}
643
- zSQL = mprintf("%s AND event.objid IN "
644
- " (SELECT rid FROM tagxref WHERE tagid=%d) "
645
- "ORDER BY mtime DESC",
646
- timeline_query_for_www(), tagid);
675
+ zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
676
+ tagid);
677
+ if( zType[0]=='c' ){
678
+ zSQL = mprintf(
679
+ "%s AND event.objid IN "
680
+ " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
681
+ "AND '%s' GLOB (target||'*')) "
682
+ "ORDER BY mtime DESC",
683
+ timeline_query_for_www(), zFullUuid, zFullUuid
684
+ );
685
+ }else{
686
+ zSQL = mprintf(
687
+ "%s AND event.objid IN "
688
+ " (SELECT rid FROM tagxref WHERE tagid=%d"
689
+ " UNION SELECT srcid FROM backlink"
690
+ " WHERE target GLOB '%.4s*'"
691
+ " AND '%s' GLOB (target||'*')"
692
+ " UNION SELECT attachid FROM attachment"
693
+ " WHERE target=%Q) "
694
+ "ORDER BY mtime DESC",
695
+ timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
696
+ );
697
+ }
647698
db_prepare(&q, zSQL);
648699
free(zSQL);
649700
www_print_timeline(&q, TIMELINE_ARTID, 0);
650701
db_finalize(&q);
651702
style_footer();
@@ -667,10 +718,12 @@
667718
if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
668719
zUuid = PD("name","");
669720
zTitle = mprintf("History Of Ticket %h", zUuid);
670721
style_submenu_element("Status", "Status",
671722
"%s/info/%s", g.zTop, zUuid);
723
+ style_submenu_element("Check-ins", "Check-ins",
724
+ "%s/tkttimeline?name=%s?y=ci", g.zTop, zUuid);
672725
style_submenu_element("Timeline", "Timeline",
673726
"%s/tkttimeline?name=%s", g.zTop, zUuid);
674727
style_header(zTitle);
675728
free(zTitle);
676729
@@ -679,37 +732,60 @@
679732
@ No such ticket: %h(zUuid)
680733
style_footer();
681734
return;
682735
}
683736
db_prepare(&q,
684
- "SELECT objid, uuid FROM event, blob"
737
+ "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
738
+ " FROM event, blob"
685739
" WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
686740
" AND blob.rid=event.objid"
687
- " ORDER BY mtime DESC",
688
- tagid
741
+ " UNION "
742
+ "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
743
+ " FROM attachment, blob"
744
+ " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
745
+ " AND blob.rid=attachid"
746
+ " ORDER BY 1 DESC",
747
+ tagid, tagid
689748
);
690749
while( db_step(&q)==SQLITE_ROW ){
691750
Blob content;
692751
Manifest m;
693
- int rid = db_column_int(&q, 0);
694
- const char *zChngUuid = db_column_text(&q, 1);
695
- content_get(rid, &content);
696
- if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
697
- char *zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
698
- char zUuid[12];
699
- memcpy(zUuid, zChngUuid, 10);
700
- zUuid[10] = 0;
701
- @
702
- @ Ticket change
703
- @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zUuid)</a>]</a>
752
+ char zShort[12];
753
+ const char *zDate = db_column_text(&q, 0);
754
+ int rid = db_column_int(&q, 1);
755
+ const char *zChngUuid = db_column_text(&q, 2);
756
+ const char *zFile = db_column_text(&q, 4);
757
+ memcpy(zShort, zChngUuid, 10);
758
+ zShort[10] = 0;
759
+ if( zFile!=0 ){
760
+ const char *zSrc = db_column_text(&q, 3);
761
+ const char *zUser = db_column_text(&q, 5);
762
+ if( zSrc==0 || zSrc[0]==0 ){
763
+ @
764
+ @ <p>Delete attachment "%h(zFile)"
765
+ }else{
766
+ @
767
+ @ <p>Add attachment "%h(zFile)"
768
+ }
769
+ @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
704770
@ (rid %d(rid)) by
705
- hyperlink_to_user(m.zUser,zDate," on");
706
- hyperlink_to_date(zDate, ":");
707
- free(zDate);
708
- ticket_output_change_artifact(&m);
771
+ hyperlink_to_user(zUser,zDate," on");
772
+ hyperlink_to_date(zDate, ".</p>");
773
+ }else{
774
+ content_get(rid, &content);
775
+ if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
776
+ @
777
+ @ <p>Ticket change
778
+ @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
779
+ @ (rid %d(rid)) by
780
+ hyperlink_to_user(m.zUser,zDate," on");
781
+ hyperlink_to_date(zDate, ":");
782
+ ticket_output_change_artifact(&m);
783
+ @ </p>
784
+ }
785
+ manifest_clear(&m);
709786
}
710
- manifest_clear(&m);
711787
}
712788
db_finalize(&q);
713789
style_footer();
714790
}
715791
716792
--- src/tkt.c
+++ src/tkt.c
@@ -165,32 +165,10 @@
165 for(i=0; (z = cgi_parameter_name(i))!=0; i++){
166 Th_Store(z, P(z));
167 }
168 }
169
170 /*
171 ** Rebuild all tickets named in the _pending_ticket table.
172 **
173 ** This routine is called just prior to commit after new
174 ** out-of-sequence ticket changes have been added.
175 */
176 static int ticket_rebuild_at_commit(void){
177 Stmt q;
178 db_multi_exec(
179 "DELETE FROM ticket WHERE tkt_uuid IN _pending_ticket"
180 );
181 db_prepare(&q, "SELECT uuid FROM _pending_ticket");
182 while( db_step(&q)==SQLITE_ROW ){
183 const char *zUuid = db_column_text(&q, 0);
184 ticket_rebuild_entry(zUuid);
185 }
186 db_multi_exec(
187 "DELETE FROM _pending_ticket"
188 );
189 return 0;
190 }
191
192 /*
193 ** Update an entry of the TICKET table according to the information
194 ** in the control file given in p. Attempt to create the appropriate
195 ** TICKET table entry if createFlag is true. If createFlag is false,
196 ** that means we already know the entry exists and so we can save the
@@ -197,11 +175,11 @@
197 ** work of trying to create it.
198 **
199 ** Return TRUE if a new TICKET entry was created and FALSE if an
200 ** existing entry was revised.
201 */
202 int ticket_insert(const Manifest *p, int createFlag, int checkTime){
203 Blob sql;
204 Stmt q;
205 int i;
206 const char *zSep;
207 int rc = 0;
@@ -224,27 +202,20 @@
224 zName, zName, p->aField[i].zValue);
225 }else{
226 if( fieldId(zName)<0 ) continue;
227 blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue);
228 }
 
 
 
229 }
230 blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime",
231 p->zTicketUuid);
232 db_prepare(&q, "%s", blob_str(&sql));
233 db_bind_double(&q, ":mtime", p->rDate);
234 db_step(&q);
235 db_finalize(&q);
236 if( checkTime && db_changes()==0 ){
237 static int isInit = 0;
238 if( !isInit ){
239 db_multi_exec("CREATE TEMP TABLE _pending_ticket(uuid TEXT UNIQUE)");
240 db_commit_hook(ticket_rebuild_at_commit, 1);
241 isInit = 1;
242 }
243 db_multi_exec("INSERT OR IGNORE INTO _pending_ticket "
244 "VALUES(%Q)", p->zTicketUuid);
245 }
246 blob_reset(&sql);
247 return rc;
248 }
249
250 /*
@@ -264,11 +235,11 @@
264 db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
265 while( db_step(&q)==SQLITE_ROW ){
266 int rid = db_column_int(&q, 0);
267 content_get(rid, &content);
268 manifest_parse(&manifest, &content);
269 ticket_insert(&manifest, createFlag, 0);
270 manifest_ticket_event(rid, &manifest, createFlag, tagid);
271 manifest_clear(&manifest);
272 createFlag = 0;
273 }
274 db_finalize(&q);
@@ -325,35 +296,79 @@
325 **
326 ** View a ticket.
327 */
328 void tktview_page(void){
329 const char *zScript;
 
 
 
330 login_check_credentials();
331 if( !g.okRdTkt ){ login_needed(); return; }
332 if( g.okWrTkt || g.okApndTkt ){
333 style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
334 g.zTop, PD("name",""));
335 }
336 if( g.okHistory ){
337 const char *zUuid = PD("name","");
338 style_submenu_element("History", "History Of This Ticket",
339 "%s/tkthistory/%T", g.zTop, zUuid);
340 style_submenu_element("Timeline", "Timeline Of This Ticket",
341 "%s/tkttimeline/%T", g.zTop, zUuid);
 
 
342 }
343 if( g.okNewTkt ){
344 style_submenu_element("New Ticket", "Create a new ticket",
345 "%s/tktnew", g.zTop);
346 }
 
 
 
 
 
347 style_header("View Ticket");
348 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
349 ticket_init();
350 initializeVariablesFromDb();
351 zScript = ticket_viewpage_code();
352 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
353 Th_Render(zScript);
354 if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355 style_footer();
356 }
357
358 /*
359 ** TH command: append_field FIELD STRING
@@ -610,42 +625,78 @@
610 return 0;
611 }
612
613 /*
614 ** WEBPAGE: tkttimeline
615 ** URL: /tkttimeline?name=TICKETUUID
616 **
617 ** Show the change history for a single ticket in timeline format.
618 */
619 void tkttimeline_page(void){
620 Stmt q;
621 char *zTitle;
622 char *zSQL;
623 const char *zUuid;
 
624 int tagid;
 
 
625
626 login_check_credentials();
627 if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
628 zUuid = PD("name","");
 
 
 
 
 
 
 
 
629 style_submenu_element("History", "History",
630 "%s/tkthistory/%s", g.zTop, zUuid);
631 style_submenu_element("Status", "Status",
632 "%s/info/%s", g.zTop, zUuid);
633 zTitle = mprintf("Timeline Of Ticket %h", zUuid);
 
 
 
 
634 style_header(zTitle);
635 free(zTitle);
636
 
 
637 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
638 if( tagid==0 ){
639 @ No such ticket: %h(zUuid)
640 style_footer();
641 return;
642 }
643 zSQL = mprintf("%s AND event.objid IN "
644 " (SELECT rid FROM tagxref WHERE tagid=%d) "
645 "ORDER BY mtime DESC",
646 timeline_query_for_www(), tagid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
647 db_prepare(&q, zSQL);
648 free(zSQL);
649 www_print_timeline(&q, TIMELINE_ARTID, 0);
650 db_finalize(&q);
651 style_footer();
@@ -667,10 +718,12 @@
667 if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
668 zUuid = PD("name","");
669 zTitle = mprintf("History Of Ticket %h", zUuid);
670 style_submenu_element("Status", "Status",
671 "%s/info/%s", g.zTop, zUuid);
 
 
672 style_submenu_element("Timeline", "Timeline",
673 "%s/tkttimeline?name=%s", g.zTop, zUuid);
674 style_header(zTitle);
675 free(zTitle);
676
@@ -679,37 +732,60 @@
679 @ No such ticket: %h(zUuid)
680 style_footer();
681 return;
682 }
683 db_prepare(&q,
684 "SELECT objid, uuid FROM event, blob"
 
685 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
686 " AND blob.rid=event.objid"
687 " ORDER BY mtime DESC",
688 tagid
 
 
 
 
 
689 );
690 while( db_step(&q)==SQLITE_ROW ){
691 Blob content;
692 Manifest m;
693 int rid = db_column_int(&q, 0);
694 const char *zChngUuid = db_column_text(&q, 1);
695 content_get(rid, &content);
696 if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
697 char *zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
698 char zUuid[12];
699 memcpy(zUuid, zChngUuid, 10);
700 zUuid[10] = 0;
701 @
702 @ Ticket change
703 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zUuid)</a>]</a>
 
 
 
 
 
 
 
704 @ (rid %d(rid)) by
705 hyperlink_to_user(m.zUser,zDate," on");
706 hyperlink_to_date(zDate, ":");
707 free(zDate);
708 ticket_output_change_artifact(&m);
 
 
 
 
 
 
 
 
 
 
 
709 }
710 manifest_clear(&m);
711 }
712 db_finalize(&q);
713 style_footer();
714 }
715
716
--- src/tkt.c
+++ src/tkt.c
@@ -165,32 +165,10 @@
165 for(i=0; (z = cgi_parameter_name(i))!=0; i++){
166 Th_Store(z, P(z));
167 }
168 }
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170 /*
171 ** Update an entry of the TICKET table according to the information
172 ** in the control file given in p. Attempt to create the appropriate
173 ** TICKET table entry if createFlag is true. If createFlag is false,
174 ** that means we already know the entry exists and so we can save the
@@ -197,11 +175,11 @@
175 ** work of trying to create it.
176 **
177 ** Return TRUE if a new TICKET entry was created and FALSE if an
178 ** existing entry was revised.
179 */
180 int ticket_insert(const Manifest *p, int createFlag, int rid){
181 Blob sql;
182 Stmt q;
183 int i;
184 const char *zSep;
185 int rc = 0;
@@ -224,27 +202,20 @@
202 zName, zName, p->aField[i].zValue);
203 }else{
204 if( fieldId(zName)<0 ) continue;
205 blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue);
206 }
207 if( rid>0 ){
208 wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
209 }
210 }
211 blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime",
212 p->zTicketUuid);
213 db_prepare(&q, "%s", blob_str(&sql));
214 db_bind_double(&q, ":mtime", p->rDate);
215 db_step(&q);
216 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
217 blob_reset(&sql);
218 return rc;
219 }
220
221 /*
@@ -264,11 +235,11 @@
235 db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
236 while( db_step(&q)==SQLITE_ROW ){
237 int rid = db_column_int(&q, 0);
238 content_get(rid, &content);
239 manifest_parse(&manifest, &content);
240 ticket_insert(&manifest, createFlag, rid);
241 manifest_ticket_event(rid, &manifest, createFlag, tagid);
242 manifest_clear(&manifest);
243 createFlag = 0;
244 }
245 db_finalize(&q);
@@ -325,35 +296,79 @@
296 **
297 ** View a ticket.
298 */
299 void tktview_page(void){
300 const char *zScript;
301 char *zFullName;
302 const char *zUuid = PD("name","");
303
304 login_check_credentials();
305 if( !g.okRdTkt ){ login_needed(); return; }
306 if( g.okWrTkt || g.okApndTkt ){
307 style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
308 g.zTop, PD("name",""));
309 }
310 if( g.okHistory ){
 
311 style_submenu_element("History", "History Of This Ticket",
312 "%s/tkthistory/%T", g.zTop, zUuid);
313 style_submenu_element("Timeline", "Timeline Of This Ticket",
314 "%s/tkttimeline/%T", g.zTop, zUuid);
315 style_submenu_element("Check-ins", "Check-ins Of This Ticket",
316 "%s/tkttimeline/%T?y=ci", g.zTop, zUuid);
317 }
318 if( g.okNewTkt ){
319 style_submenu_element("New Ticket", "Create a new ticket",
320 "%s/tktnew", g.zTop);
321 }
322 if( g.okApndTkt && g.okAttach ){
323 style_submenu_element("Attach", "Add An Attachment",
324 "%s/attachadd?tkt=%T&from=%s/tktview%%3fname=%t",
325 g.zTop, zUuid, g.zTop, zUuid);
326 }
327 style_header("View Ticket");
328 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
329 ticket_init();
330 initializeVariablesFromDb();
331 zScript = ticket_viewpage_code();
332 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
333 Th_Render(zScript);
334 if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
335
336 zFullName = db_text(0,
337 "SELECT tkt_uuid FROM ticket"
338 " WHERE tkt_uuid GLOB '%q*'", zUuid);
339 if( zFullName ){
340 int cnt = 0;
341 Stmt q;
342 db_prepare(&q,
343 "SELECT datetime(mtime,'localtime'), filename, user"
344 " FROM attachment"
345 " WHERE isLatest AND src!='' AND target=%Q"
346 " ORDER BY mtime DESC",
347 zFullName);
348 while( db_step(&q)==SQLITE_ROW ){
349 const char *zDate = db_column_text(&q, 0);
350 const char *zFile = db_column_text(&q, 1);
351 const char *zUser = db_column_text(&q, 2);
352 if( cnt==0 ){
353 @ <hr><h2>Attachments:</h2>
354 @ <ul>
355 }
356 cnt++;
357 @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
358 @ %h(zFile)</a> add by %h(zUser) on
359 hyperlink_to_date(zDate, ".");
360 if( g.okWrTkt && g.okAttach ){
361 @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
362 }
363 }
364 if( cnt ){
365 @ </ul>
366 }
367 db_finalize(&q);
368 }
369
370 style_footer();
371 }
372
373 /*
374 ** TH command: append_field FIELD STRING
@@ -610,42 +625,78 @@
625 return 0;
626 }
627
628 /*
629 ** WEBPAGE: tkttimeline
630 ** URL: /tkttimeline?name=TICKETUUID&y=TYPE
631 **
632 ** Show the change history for a single ticket in timeline format.
633 */
634 void tkttimeline_page(void){
635 Stmt q;
636 char *zTitle;
637 char *zSQL;
638 const char *zUuid;
639 char *zFullUuid;
640 int tagid;
641 char zGlobPattern[50];
642 const char *zType;
643
644 login_check_credentials();
645 if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
646 zUuid = PD("name","");
647 zType = PD("y","a");
648 if( zType[0]!='c' ){
649 style_submenu_element("Check-ins", "Check-ins",
650 "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
651 }else{
652 style_submenu_element("Timeline", "Timeline",
653 "%s/tkttimeline?name=%T", g.zTop, zUuid);
654 }
655 style_submenu_element("History", "History",
656 "%s/tkthistory/%s", g.zTop, zUuid);
657 style_submenu_element("Status", "Status",
658 "%s/info/%s", g.zTop, zUuid);
659 if( zType[0]=='c' ){
660 zTitle = mprintf("Check-Ins Associated With Ticket %h", zUuid);
661 }else{
662 zTitle = mprintf("Timeline Of Ticket %h", zUuid);
663 }
664 style_header(zTitle);
665 free(zTitle);
666
667 sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
668 canonical16(zGlobPattern, strlen(zGlobPattern));
669 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
670 if( tagid==0 ){
671 @ No such ticket: %h(zUuid)
672 style_footer();
673 return;
674 }
675 zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
676 tagid);
677 if( zType[0]=='c' ){
678 zSQL = mprintf(
679 "%s AND event.objid IN "
680 " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
681 "AND '%s' GLOB (target||'*')) "
682 "ORDER BY mtime DESC",
683 timeline_query_for_www(), zFullUuid, zFullUuid
684 );
685 }else{
686 zSQL = mprintf(
687 "%s AND event.objid IN "
688 " (SELECT rid FROM tagxref WHERE tagid=%d"
689 " UNION SELECT srcid FROM backlink"
690 " WHERE target GLOB '%.4s*'"
691 " AND '%s' GLOB (target||'*')"
692 " UNION SELECT attachid FROM attachment"
693 " WHERE target=%Q) "
694 "ORDER BY mtime DESC",
695 timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
696 );
697 }
698 db_prepare(&q, zSQL);
699 free(zSQL);
700 www_print_timeline(&q, TIMELINE_ARTID, 0);
701 db_finalize(&q);
702 style_footer();
@@ -667,10 +718,12 @@
718 if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
719 zUuid = PD("name","");
720 zTitle = mprintf("History Of Ticket %h", zUuid);
721 style_submenu_element("Status", "Status",
722 "%s/info/%s", g.zTop, zUuid);
723 style_submenu_element("Check-ins", "Check-ins",
724 "%s/tkttimeline?name=%s?y=ci", g.zTop, zUuid);
725 style_submenu_element("Timeline", "Timeline",
726 "%s/tkttimeline?name=%s", g.zTop, zUuid);
727 style_header(zTitle);
728 free(zTitle);
729
@@ -679,37 +732,60 @@
732 @ No such ticket: %h(zUuid)
733 style_footer();
734 return;
735 }
736 db_prepare(&q,
737 "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
738 " FROM event, blob"
739 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
740 " AND blob.rid=event.objid"
741 " UNION "
742 "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
743 " FROM attachment, blob"
744 " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
745 " AND blob.rid=attachid"
746 " ORDER BY 1 DESC",
747 tagid, tagid
748 );
749 while( db_step(&q)==SQLITE_ROW ){
750 Blob content;
751 Manifest m;
752 char zShort[12];
753 const char *zDate = db_column_text(&q, 0);
754 int rid = db_column_int(&q, 1);
755 const char *zChngUuid = db_column_text(&q, 2);
756 const char *zFile = db_column_text(&q, 4);
757 memcpy(zShort, zChngUuid, 10);
758 zShort[10] = 0;
759 if( zFile!=0 ){
760 const char *zSrc = db_column_text(&q, 3);
761 const char *zUser = db_column_text(&q, 5);
762 if( zSrc==0 || zSrc[0]==0 ){
763 @
764 @ <p>Delete attachment "%h(zFile)"
765 }else{
766 @
767 @ <p>Add attachment "%h(zFile)"
768 }
769 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
770 @ (rid %d(rid)) by
771 hyperlink_to_user(zUser,zDate," on");
772 hyperlink_to_date(zDate, ".</p>");
773 }else{
774 content_get(rid, &content);
775 if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
776 @
777 @ <p>Ticket change
778 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
779 @ (rid %d(rid)) by
780 hyperlink_to_user(m.zUser,zDate," on");
781 hyperlink_to_date(zDate, ":");
782 ticket_output_change_artifact(&m);
783 @ </p>
784 }
785 manifest_clear(&m);
786 }
 
787 }
788 db_finalize(&q);
789 style_footer();
790 }
791
792
+70 -8
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,12 @@
127127
int isSandbox;
128128
Blob wiki;
129129
Manifest m;
130130
const char *zPageName;
131131
char *zBody = mprintf("%s","<i>Empty Page</i>");
132
+ Stmt q;
133
+ int cnt = 0;
132134
133135
login_check_credentials();
134136
if( !g.okRdWiki ){ login_needed(); return; }
135137
zPageName = P("name");
136138
if( zPageName==0 ){
@@ -152,11 +154,12 @@
152154
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
153155
}
154156
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
155157
@ available on this server.</li>
156158
@ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
157
- @ Search wiki titles: <input type="text" name="title"/> &nbsp; <input type="submit" />
159
+ @ Search wiki titles: <input type="text" name="title"/>
160
+ @ &nbsp; <input type="submit" />
158161
@ </li>
159162
@ </ul>
160163
style_footer();
161164
return;
162165
}
@@ -176,20 +179,26 @@
176179
blob_zero(&m.content);
177180
if( rid ){
178181
Blob content;
179182
content_get(rid, &content);
180183
manifest_parse(&m, &content);
181
- if( m.type==CFTYPE_WIKI ){
182
- zBody = m.zWiki;
184
+ if( m.type==CFTYPE_WIKI && m.zWiki ){
185
+ while( isspace(m.zWiki[0]) ) m.zWiki++;
186
+ if( m.zWiki[0] ) zBody = m.zWiki;
183187
}
184188
}
185189
}
186190
if( !g.isHome ){
187191
if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
188192
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
189193
g.zTop, zPageName);
190194
}
195
+ if( rid && g.okApndWiki && g.okAttach ){
196
+ style_submenu_element("Attach", "Add An Attachment",
197
+ "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
198
+ g.zTop, zPageName, g.zTop, zPageName);
199
+ }
191200
if( rid && g.okApndWiki ){
192201
style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
193202
g.zTop, zPageName);
194203
}
195204
if( g.okHistory ){
@@ -199,10 +208,42 @@
199208
}
200209
style_header(zPageName);
201210
blob_init(&wiki, zBody, -1);
202211
wiki_convert(&wiki, 0, 0);
203212
blob_reset(&wiki);
213
+
214
+ db_prepare(&q,
215
+ "SELECT datetime(mtime,'localtime'), filename, user"
216
+ " FROM attachment"
217
+ " WHERE isLatest AND src!='' AND target=%Q"
218
+ " ORDER BY mtime DESC",
219
+ zPageName);
220
+ while( db_step(&q)==SQLITE_ROW ){
221
+ const char *zDate = db_column_text(&q, 0);
222
+ const char *zFile = db_column_text(&q, 1);
223
+ const char *zUser = db_column_text(&q, 2);
224
+ if( cnt==0 ){
225
+ @ <hr><h2>Attachments:</h2>
226
+ @ <ul>
227
+ }
228
+ cnt++;
229
+ if( g.okHistory ){
230
+ @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
231
+ }else{
232
+ @ <li>
233
+ }
234
+ @ %h(zFile)</a> add by %h(zUser) on
235
+ hyperlink_to_date(zDate, ".");
236
+ if( g.okWrWiki && g.okAttach ){
237
+ @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
238
+ }
239
+ }
240
+ if( cnt ){
241
+ @ </ul>
242
+ }
243
+ db_finalize(&q);
244
+
204245
if( !isSandbox ){
205246
manifest_clear(&m);
206247
}
207248
style_footer();
208249
}
@@ -515,11 +556,13 @@
515556
/*
516557
** Function called to output extra text at the end of each line in
517558
** a wiki history listing.
518559
*/
519560
static void wiki_history_extra(int rid){
520
- @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
561
+ if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
562
+ @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
563
+ }
521564
}
522565
523566
/*
524567
** WEBPAGE: whistory
525568
** URL: /whistory?name=PAGENAME
@@ -538,13 +581,15 @@
538581
style_header(zTitle);
539582
free(zTitle);
540583
541584
zSQL = mprintf("%s AND event.objid IN "
542585
" (SELECT rid FROM tagxref WHERE tagid="
543
- "(SELECT tagid FROM tag WHERE tagname='wiki-%q'))"
586
+ "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
587
+ " UNION SELECT attachid FROM attachment"
588
+ " WHERE target=%Q)"
544589
"ORDER BY mtime DESC",
545
- timeline_query_for_www(), zPageName);
590
+ timeline_query_for_www(), zPageName, zPageName);
546591
db_prepare(&q, zSQL);
547592
free(zSQL);
548593
zWikiPageName = zPageName;
549594
www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra);
550595
db_finalize(&q);
@@ -604,26 +649,43 @@
604649
style_footer();
605650
}
606651
607652
/*
608653
** WEBPAGE: wcontent
654
+**
655
+** all=1 Show deleted pages
609656
**
610657
** List all available wiki pages with date created and last modified.
611658
*/
612659
void wcontent_page(void){
613660
Stmt q;
661
+ int showAll = P("all")!=0;
662
+
614663
login_check_credentials();
615664
if( !g.okRdWiki ){ login_needed(); return; }
616665
style_header("Available Wiki Pages");
666
+ if( showAll ){
667
+ style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
668
+ }else{
669
+ style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
670
+ }
617671
@ <ul>
618672
db_prepare(&q,
619
- "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'"
673
+ "SELECT"
674
+ " substr(tagname, 6),"
675
+ " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC)"
676
+ " FROM tag WHERE tagname GLOB 'wiki-*'"
620677
" ORDER BY lower(tagname)"
621678
);
622679
while( db_step(&q)==SQLITE_ROW ){
623680
const char *zName = db_column_text(&q, 0);
624
- @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li>
681
+ int size = db_column_int(&q, 1);
682
+ if( size>0 ){
683
+ @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
684
+ }else if( showAll ){
685
+ @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)"><s>%h(zName)</s></a></li>
686
+ }
625687
}
626688
db_finalize(&q);
627689
@ </ul>
628690
style_footer();
629691
}
630692
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,12 @@
127 int isSandbox;
128 Blob wiki;
129 Manifest m;
130 const char *zPageName;
131 char *zBody = mprintf("%s","<i>Empty Page</i>");
 
 
132
133 login_check_credentials();
134 if( !g.okRdWiki ){ login_needed(); return; }
135 zPageName = P("name");
136 if( zPageName==0 ){
@@ -152,11 +154,12 @@
152 @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
153 }
154 @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
155 @ available on this server.</li>
156 @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
157 @ Search wiki titles: <input type="text" name="title"/> &nbsp; <input type="submit" />
 
158 @ </li>
159 @ </ul>
160 style_footer();
161 return;
162 }
@@ -176,20 +179,26 @@
176 blob_zero(&m.content);
177 if( rid ){
178 Blob content;
179 content_get(rid, &content);
180 manifest_parse(&m, &content);
181 if( m.type==CFTYPE_WIKI ){
182 zBody = m.zWiki;
 
183 }
184 }
185 }
186 if( !g.isHome ){
187 if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
188 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
189 g.zTop, zPageName);
190 }
 
 
 
 
 
191 if( rid && g.okApndWiki ){
192 style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
193 g.zTop, zPageName);
194 }
195 if( g.okHistory ){
@@ -199,10 +208,42 @@
199 }
200 style_header(zPageName);
201 blob_init(&wiki, zBody, -1);
202 wiki_convert(&wiki, 0, 0);
203 blob_reset(&wiki);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204 if( !isSandbox ){
205 manifest_clear(&m);
206 }
207 style_footer();
208 }
@@ -515,11 +556,13 @@
515 /*
516 ** Function called to output extra text at the end of each line in
517 ** a wiki history listing.
518 */
519 static void wiki_history_extra(int rid){
520 @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
 
 
521 }
522
523 /*
524 ** WEBPAGE: whistory
525 ** URL: /whistory?name=PAGENAME
@@ -538,13 +581,15 @@
538 style_header(zTitle);
539 free(zTitle);
540
541 zSQL = mprintf("%s AND event.objid IN "
542 " (SELECT rid FROM tagxref WHERE tagid="
543 "(SELECT tagid FROM tag WHERE tagname='wiki-%q'))"
 
 
544 "ORDER BY mtime DESC",
545 timeline_query_for_www(), zPageName);
546 db_prepare(&q, zSQL);
547 free(zSQL);
548 zWikiPageName = zPageName;
549 www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra);
550 db_finalize(&q);
@@ -604,26 +649,43 @@
604 style_footer();
605 }
606
607 /*
608 ** WEBPAGE: wcontent
 
 
609 **
610 ** List all available wiki pages with date created and last modified.
611 */
612 void wcontent_page(void){
613 Stmt q;
 
 
614 login_check_credentials();
615 if( !g.okRdWiki ){ login_needed(); return; }
616 style_header("Available Wiki Pages");
 
 
 
 
 
617 @ <ul>
618 db_prepare(&q,
619 "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'"
 
 
 
620 " ORDER BY lower(tagname)"
621 );
622 while( db_step(&q)==SQLITE_ROW ){
623 const char *zName = db_column_text(&q, 0);
624 @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li>
 
 
 
 
 
625 }
626 db_finalize(&q);
627 @ </ul>
628 style_footer();
629 }
630
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,12 @@
127 int isSandbox;
128 Blob wiki;
129 Manifest m;
130 const char *zPageName;
131 char *zBody = mprintf("%s","<i>Empty Page</i>");
132 Stmt q;
133 int cnt = 0;
134
135 login_check_credentials();
136 if( !g.okRdWiki ){ login_needed(); return; }
137 zPageName = P("name");
138 if( zPageName==0 ){
@@ -152,11 +154,12 @@
154 @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
155 }
156 @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
157 @ available on this server.</li>
158 @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
159 @ Search wiki titles: <input type="text" name="title"/>
160 @ &nbsp; <input type="submit" />
161 @ </li>
162 @ </ul>
163 style_footer();
164 return;
165 }
@@ -176,20 +179,26 @@
179 blob_zero(&m.content);
180 if( rid ){
181 Blob content;
182 content_get(rid, &content);
183 manifest_parse(&m, &content);
184 if( m.type==CFTYPE_WIKI && m.zWiki ){
185 while( isspace(m.zWiki[0]) ) m.zWiki++;
186 if( m.zWiki[0] ) zBody = m.zWiki;
187 }
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",
193 g.zTop, zPageName);
194 }
195 if( rid && g.okApndWiki && g.okAttach ){
196 style_submenu_element("Attach", "Add An Attachment",
197 "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
198 g.zTop, zPageName, g.zTop, zPageName);
199 }
200 if( rid && g.okApndWiki ){
201 style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
202 g.zTop, zPageName);
203 }
204 if( g.okHistory ){
@@ -199,10 +208,42 @@
208 }
209 style_header(zPageName);
210 blob_init(&wiki, zBody, -1);
211 wiki_convert(&wiki, 0, 0);
212 blob_reset(&wiki);
213
214 db_prepare(&q,
215 "SELECT datetime(mtime,'localtime'), filename, user"
216 " FROM attachment"
217 " WHERE isLatest AND src!='' AND target=%Q"
218 " ORDER BY mtime DESC",
219 zPageName);
220 while( db_step(&q)==SQLITE_ROW ){
221 const char *zDate = db_column_text(&q, 0);
222 const char *zFile = db_column_text(&q, 1);
223 const char *zUser = db_column_text(&q, 2);
224 if( cnt==0 ){
225 @ <hr><h2>Attachments:</h2>
226 @ <ul>
227 }
228 cnt++;
229 if( g.okHistory ){
230 @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
231 }else{
232 @ <li>
233 }
234 @ %h(zFile)</a> add by %h(zUser) on
235 hyperlink_to_date(zDate, ".");
236 if( g.okWrWiki && g.okAttach ){
237 @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
238 }
239 }
240 if( cnt ){
241 @ </ul>
242 }
243 db_finalize(&q);
244
245 if( !isSandbox ){
246 manifest_clear(&m);
247 }
248 style_footer();
249 }
@@ -515,11 +556,13 @@
556 /*
557 ** Function called to output extra text at the end of each line in
558 ** a wiki history listing.
559 */
560 static void wiki_history_extra(int rid){
561 if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
562 @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
563 }
564 }
565
566 /*
567 ** WEBPAGE: whistory
568 ** URL: /whistory?name=PAGENAME
@@ -538,13 +581,15 @@
581 style_header(zTitle);
582 free(zTitle);
583
584 zSQL = mprintf("%s AND event.objid IN "
585 " (SELECT rid FROM tagxref WHERE tagid="
586 "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
587 " UNION SELECT attachid FROM attachment"
588 " WHERE target=%Q)"
589 "ORDER BY mtime DESC",
590 timeline_query_for_www(), zPageName, zPageName);
591 db_prepare(&q, zSQL);
592 free(zSQL);
593 zWikiPageName = zPageName;
594 www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra);
595 db_finalize(&q);
@@ -604,26 +649,43 @@
649 style_footer();
650 }
651
652 /*
653 ** WEBPAGE: wcontent
654 **
655 ** all=1 Show deleted pages
656 **
657 ** List all available wiki pages with date created and last modified.
658 */
659 void wcontent_page(void){
660 Stmt q;
661 int showAll = P("all")!=0;
662
663 login_check_credentials();
664 if( !g.okRdWiki ){ login_needed(); return; }
665 style_header("Available Wiki Pages");
666 if( showAll ){
667 style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
668 }else{
669 style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
670 }
671 @ <ul>
672 db_prepare(&q,
673 "SELECT"
674 " substr(tagname, 6),"
675 " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC)"
676 " FROM tag WHERE tagname GLOB 'wiki-*'"
677 " ORDER BY lower(tagname)"
678 );
679 while( db_step(&q)==SQLITE_ROW ){
680 const char *zName = db_column_text(&q, 0);
681 int size = db_column_int(&q, 1);
682 if( size>0 ){
683 @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
684 }else if( showAll ){
685 @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)"><s>%h(zName)</s></a></li>
686 }
687 }
688 db_finalize(&q);
689 @ </ul>
690 style_footer();
691 }
692
+196 -3
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -369,10 +369,23 @@
369369
short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
370370
const char *zId; /* ID attribute or NULL */
371371
} *aStack;
372372
};
373373
374
+/*
375
+** Return TRUE if HTML should be used as the sole markup language for wiki.
376
+**
377
+** On first invocation, this routine consults the "wiki-use-html" setting.
378
+** It caches the result for subsequent invocations, under the assumption
379
+** that the setting will not change.
380
+*/
381
+static int wikiUsesHtml(void){
382
+ static int r = -1;
383
+ if( r<0 ) r = db_get_boolean("wiki-use-html", 0);
384
+ return r;
385
+}
386
+
374387
375388
/*
376389
** z points to a "<" character. Check to see if this is the start of
377390
** a valid markup. If it is, return the total number of characters in
378391
** the markup including the initial "<" and the terminating ">". If
@@ -620,11 +633,10 @@
620633
** Parse only Wiki links, return everything else as TOKEN_RAW.
621634
**
622635
** z points to the start of a token. Return the number of
623636
** characters in that token. Write the token type into *pTokenType.
624637
*/
625
-
626638
static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
627639
int n;
628640
if( z[0]=='[' && (n = linkLength(z))>0 ){
629641
*pTokenType = TOKEN_LINK;
630642
return n;
@@ -778,11 +790,11 @@
778790
static void popStack(Renderer *p){
779791
if( p->nStack ){
780792
int iCode;
781793
p->nStack--;
782794
iCode = p->aStack[p->nStack].iCode;
783
- if( iCode!=MARKUP_DIV ){
795
+ if( iCode!=MARKUP_DIV && p->pOut ){
784796
blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName);
785797
}
786798
}
787799
}
788800
@@ -1391,11 +1403,11 @@
13911403
if( flags & WIKI_INLINE ){
13921404
renderer.wantAutoParagraph = 0;
13931405
}else{
13941406
renderer.wantAutoParagraph = 1;
13951407
}
1396
- if( db_get_int("wiki-use-html", 0) ){
1408
+ if( wikiUsesHtml() ){
13971409
renderer.state |= WIKI_USE_HTML;
13981410
}
13991411
if( pOut ){
14001412
renderer.pOut = pOut;
14011413
}else{
@@ -1447,5 +1459,186 @@
14471459
if( z[i]!='<' ) return 0;
14481460
blob_init(pTitle, &z[iStart], i-iStart);
14491461
blob_init(pTail, &z[i+8], -1);
14501462
return 1;
14511463
}
1464
+
1465
+/*
1466
+** Parse text looking for wiki hyperlinks in one of the formats:
1467
+**
1468
+** [target]
1469
+** [target|...]
1470
+**
1471
+** Where "target" can be either an artifact ID prefix or a wiki page
1472
+** name. For each such hyperlink found, add an entry to the
1473
+** backlink table.
1474
+*/
1475
+void wiki_extract_links(
1476
+ char *z, /* The wiki text from which to extract links */
1477
+ int srcid, /* srcid field for new BACKLINK table entries */
1478
+ int srctype, /* srctype field for new BACKLINK table entries */
1479
+ double mtime, /* mtime field for new BACKLINK table entries */
1480
+ int replaceFlag, /* True first delete prior BACKLINK entries */
1481
+ int flags /* wiki parsing flags */
1482
+){
1483
+ Renderer renderer;
1484
+ int tokenType;
1485
+ ParsedMarkup markup;
1486
+ int n;
1487
+ int inlineOnly;
1488
+ int wikiUseHtml = 0;
1489
+
1490
+ memset(&renderer, 0, sizeof(renderer));
1491
+ renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
1492
+ if( flags & WIKI_NOBLOCK ){
1493
+ renderer.state |= INLINE_MARKUP_ONLY;
1494
+ }
1495
+ if( wikiUsesHtml() ){
1496
+ renderer.state |= WIKI_USE_HTML;
1497
+ wikiUseHtml = 1;
1498
+ }
1499
+ inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1500
+ if( replaceFlag ){
1501
+ db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1502
+ srctype, srcid);
1503
+ }
1504
+
1505
+ while( z[0] ){
1506
+ if( wikiUseHtml ){
1507
+ n = nextRawToken(z, &renderer, &tokenType);
1508
+ }else{
1509
+ n = nextWikiToken(z, &renderer, &tokenType);
1510
+ }
1511
+ switch( tokenType ){
1512
+ case TOKEN_LINK: {
1513
+ char *zTarget;
1514
+ int i, c;
1515
+ char zLink[42];
1516
+
1517
+ zTarget = &z[1];
1518
+ for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
1519
+ while(i>1 && zTarget[i-1]==' '){ i--; }
1520
+ c = zTarget[i];
1521
+ zTarget[i] = 0;
1522
+ if( is_valid_uuid(zTarget) ){
1523
+ memcpy(zLink, zTarget, i+1);
1524
+ canonical16(zLink, i);
1525
+ db_multi_exec(
1526
+ "REPLACE INTO backlink(target,srctype,srcid,mtime)"
1527
+ "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime
1528
+ );
1529
+ }
1530
+ zTarget[i] = c;
1531
+ break;
1532
+ }
1533
+ case TOKEN_MARKUP: {
1534
+ const char *zId;
1535
+ int iDiv;
1536
+ parseMarkup(&markup, z);
1537
+
1538
+ /* Markup of the form </div id=ID> where there is a matching
1539
+ ** ID somewhere on the stack. Exit the verbatim if were are in
1540
+ ** it. Pop the stack up to the matching <div>. Discard the
1541
+ ** </div>
1542
+ */
1543
+ if( markup.iCode==MARKUP_DIV && markup.endTag &&
1544
+ (zId = markupId(&markup))!=0 &&
1545
+ (iDiv = findTagWithId(&renderer, MARKUP_DIV, zId))>=0
1546
+ ){
1547
+ if( renderer.inVerbatim ){
1548
+ renderer.inVerbatim = 0;
1549
+ renderer.state = renderer.preVerbState;
1550
+ }
1551
+ while( renderer.nStack>iDiv+1 ) popStack(&renderer);
1552
+ if( renderer.aStack[iDiv].allowWiki ){
1553
+ renderer.state |= ALLOW_WIKI;
1554
+ }else{
1555
+ renderer.state &= ~ALLOW_WIKI;
1556
+ }
1557
+ renderer.nStack--;
1558
+ }else
1559
+
1560
+ /* If within <verbatim id=ID> ignore everything other than
1561
+ ** </verbatim id=ID> and the </dev id=ID2> above.
1562
+ */
1563
+ if( renderer.inVerbatim ){
1564
+ if( endVerbatim(&renderer, &markup) ){
1565
+ renderer.inVerbatim = 0;
1566
+ renderer.state = renderer.preVerbState;
1567
+ }else{
1568
+ n = 1;
1569
+ }
1570
+ }else
1571
+
1572
+ /* Render invalid markup literally. The markup appears in the
1573
+ ** final output as plain text.
1574
+ */
1575
+ if( markup.iCode==MARKUP_INVALID ){
1576
+ n = 1;
1577
+ }else
1578
+
1579
+ /* If the markup is not font-change markup ignore it if the
1580
+ ** font-change-only flag is set.
1581
+ */
1582
+ if( (markup.iType&MUTYPE_FONT)==0 &&
1583
+ (renderer.state & FONT_MARKUP_ONLY)!=0 ){
1584
+ /* Do nothing */
1585
+ }else
1586
+
1587
+ if( markup.iCode==MARKUP_NOWIKI ){
1588
+ if( markup.endTag ){
1589
+ renderer.state |= ALLOW_WIKI;
1590
+ }else{
1591
+ renderer.state &= ~ALLOW_WIKI;
1592
+ }
1593
+ }else
1594
+
1595
+ /* Ignore block markup for in-line rendering.
1596
+ */
1597
+ if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
1598
+ /* Do nothing */
1599
+ }else
1600
+
1601
+ /* Generate end-tags */
1602
+ if( markup.endTag ){
1603
+ popStackToTag(&renderer, markup.iCode);
1604
+ }else
1605
+
1606
+ /* Push <div> markup onto the stack together with the id=ID attribute.
1607
+ */
1608
+ if( markup.iCode==MARKUP_DIV ){
1609
+ pushStackWithId(&renderer, markup.iCode, markupId(&markup),
1610
+ (renderer.state & ALLOW_WIKI)!=0);
1611
+ }else
1612
+
1613
+ /* Enter <verbatim> processing. With verbatim enabled, all other
1614
+ ** markup other than the corresponding end-tag with the same ID is
1615
+ ** ignored.
1616
+ */
1617
+ if( markup.iCode==MARKUP_VERBATIM ){
1618
+ int vAttrIdx, vAttrDidAppend=0;
1619
+ renderer.zVerbatimId = 0;
1620
+ renderer.inVerbatim = 1;
1621
+ renderer.preVerbState = renderer.state;
1622
+ renderer.state &= ~ALLOW_WIKI;
1623
+ for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
1624
+ if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
1625
+ renderer.zVerbatimId = markup.aAttr[0].zValue;
1626
+ }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
1627
+ vAttrDidAppend=1;
1628
+ }
1629
+ }
1630
+ renderer.wantAutoParagraph = 0;
1631
+ }
1632
+
1633
+ /* Restore the input text to its original configuration
1634
+ */
1635
+ unparseMarkup(&markup);
1636
+ break;
1637
+ }
1638
+ default: {
1639
+ break;
1640
+ }
1641
+ }
1642
+ z += n;
1643
+ }
1644
+}
14521645
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -369,10 +369,23 @@
369 short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
370 const char *zId; /* ID attribute or NULL */
371 } *aStack;
372 };
373
 
 
 
 
 
 
 
 
 
 
 
 
 
374
375 /*
376 ** z points to a "<" character. Check to see if this is the start of
377 ** a valid markup. If it is, return the total number of characters in
378 ** the markup including the initial "<" and the terminating ">". If
@@ -620,11 +633,10 @@
620 ** Parse only Wiki links, return everything else as TOKEN_RAW.
621 **
622 ** z points to the start of a token. Return the number of
623 ** characters in that token. Write the token type into *pTokenType.
624 */
625
626 static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
627 int n;
628 if( z[0]=='[' && (n = linkLength(z))>0 ){
629 *pTokenType = TOKEN_LINK;
630 return n;
@@ -778,11 +790,11 @@
778 static void popStack(Renderer *p){
779 if( p->nStack ){
780 int iCode;
781 p->nStack--;
782 iCode = p->aStack[p->nStack].iCode;
783 if( iCode!=MARKUP_DIV ){
784 blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName);
785 }
786 }
787 }
788
@@ -1391,11 +1403,11 @@
1391 if( flags & WIKI_INLINE ){
1392 renderer.wantAutoParagraph = 0;
1393 }else{
1394 renderer.wantAutoParagraph = 1;
1395 }
1396 if( db_get_int("wiki-use-html", 0) ){
1397 renderer.state |= WIKI_USE_HTML;
1398 }
1399 if( pOut ){
1400 renderer.pOut = pOut;
1401 }else{
@@ -1447,5 +1459,186 @@
1447 if( z[i]!='<' ) return 0;
1448 blob_init(pTitle, &z[iStart], i-iStart);
1449 blob_init(pTail, &z[i+8], -1);
1450 return 1;
1451 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1452
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -369,10 +369,23 @@
369 short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
370 const char *zId; /* ID attribute or NULL */
371 } *aStack;
372 };
373
374 /*
375 ** Return TRUE if HTML should be used as the sole markup language for wiki.
376 **
377 ** On first invocation, this routine consults the "wiki-use-html" setting.
378 ** It caches the result for subsequent invocations, under the assumption
379 ** that the setting will not change.
380 */
381 static int wikiUsesHtml(void){
382 static int r = -1;
383 if( r<0 ) r = db_get_boolean("wiki-use-html", 0);
384 return r;
385 }
386
387
388 /*
389 ** z points to a "<" character. Check to see if this is the start of
390 ** a valid markup. If it is, return the total number of characters in
391 ** the markup including the initial "<" and the terminating ">". If
@@ -620,11 +633,10 @@
633 ** Parse only Wiki links, return everything else as TOKEN_RAW.
634 **
635 ** z points to the start of a token. Return the number of
636 ** characters in that token. Write the token type into *pTokenType.
637 */
 
638 static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
639 int n;
640 if( z[0]=='[' && (n = linkLength(z))>0 ){
641 *pTokenType = TOKEN_LINK;
642 return n;
@@ -778,11 +790,11 @@
790 static void popStack(Renderer *p){
791 if( p->nStack ){
792 int iCode;
793 p->nStack--;
794 iCode = p->aStack[p->nStack].iCode;
795 if( iCode!=MARKUP_DIV && p->pOut ){
796 blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName);
797 }
798 }
799 }
800
@@ -1391,11 +1403,11 @@
1403 if( flags & WIKI_INLINE ){
1404 renderer.wantAutoParagraph = 0;
1405 }else{
1406 renderer.wantAutoParagraph = 1;
1407 }
1408 if( wikiUsesHtml() ){
1409 renderer.state |= WIKI_USE_HTML;
1410 }
1411 if( pOut ){
1412 renderer.pOut = pOut;
1413 }else{
@@ -1447,5 +1459,186 @@
1459 if( z[i]!='<' ) return 0;
1460 blob_init(pTitle, &z[iStart], i-iStart);
1461 blob_init(pTail, &z[i+8], -1);
1462 return 1;
1463 }
1464
1465 /*
1466 ** Parse text looking for wiki hyperlinks in one of the formats:
1467 **
1468 ** [target]
1469 ** [target|...]
1470 **
1471 ** Where "target" can be either an artifact ID prefix or a wiki page
1472 ** name. For each such hyperlink found, add an entry to the
1473 ** backlink table.
1474 */
1475 void wiki_extract_links(
1476 char *z, /* The wiki text from which to extract links */
1477 int srcid, /* srcid field for new BACKLINK table entries */
1478 int srctype, /* srctype field for new BACKLINK table entries */
1479 double mtime, /* mtime field for new BACKLINK table entries */
1480 int replaceFlag, /* True first delete prior BACKLINK entries */
1481 int flags /* wiki parsing flags */
1482 ){
1483 Renderer renderer;
1484 int tokenType;
1485 ParsedMarkup markup;
1486 int n;
1487 int inlineOnly;
1488 int wikiUseHtml = 0;
1489
1490 memset(&renderer, 0, sizeof(renderer));
1491 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
1492 if( flags & WIKI_NOBLOCK ){
1493 renderer.state |= INLINE_MARKUP_ONLY;
1494 }
1495 if( wikiUsesHtml() ){
1496 renderer.state |= WIKI_USE_HTML;
1497 wikiUseHtml = 1;
1498 }
1499 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1500 if( replaceFlag ){
1501 db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1502 srctype, srcid);
1503 }
1504
1505 while( z[0] ){
1506 if( wikiUseHtml ){
1507 n = nextRawToken(z, &renderer, &tokenType);
1508 }else{
1509 n = nextWikiToken(z, &renderer, &tokenType);
1510 }
1511 switch( tokenType ){
1512 case TOKEN_LINK: {
1513 char *zTarget;
1514 int i, c;
1515 char zLink[42];
1516
1517 zTarget = &z[1];
1518 for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
1519 while(i>1 && zTarget[i-1]==' '){ i--; }
1520 c = zTarget[i];
1521 zTarget[i] = 0;
1522 if( is_valid_uuid(zTarget) ){
1523 memcpy(zLink, zTarget, i+1);
1524 canonical16(zLink, i);
1525 db_multi_exec(
1526 "REPLACE INTO backlink(target,srctype,srcid,mtime)"
1527 "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime
1528 );
1529 }
1530 zTarget[i] = c;
1531 break;
1532 }
1533 case TOKEN_MARKUP: {
1534 const char *zId;
1535 int iDiv;
1536 parseMarkup(&markup, z);
1537
1538 /* Markup of the form </div id=ID> where there is a matching
1539 ** ID somewhere on the stack. Exit the verbatim if were are in
1540 ** it. Pop the stack up to the matching <div>. Discard the
1541 ** </div>
1542 */
1543 if( markup.iCode==MARKUP_DIV && markup.endTag &&
1544 (zId = markupId(&markup))!=0 &&
1545 (iDiv = findTagWithId(&renderer, MARKUP_DIV, zId))>=0
1546 ){
1547 if( renderer.inVerbatim ){
1548 renderer.inVerbatim = 0;
1549 renderer.state = renderer.preVerbState;
1550 }
1551 while( renderer.nStack>iDiv+1 ) popStack(&renderer);
1552 if( renderer.aStack[iDiv].allowWiki ){
1553 renderer.state |= ALLOW_WIKI;
1554 }else{
1555 renderer.state &= ~ALLOW_WIKI;
1556 }
1557 renderer.nStack--;
1558 }else
1559
1560 /* If within <verbatim id=ID> ignore everything other than
1561 ** </verbatim id=ID> and the </dev id=ID2> above.
1562 */
1563 if( renderer.inVerbatim ){
1564 if( endVerbatim(&renderer, &markup) ){
1565 renderer.inVerbatim = 0;
1566 renderer.state = renderer.preVerbState;
1567 }else{
1568 n = 1;
1569 }
1570 }else
1571
1572 /* Render invalid markup literally. The markup appears in the
1573 ** final output as plain text.
1574 */
1575 if( markup.iCode==MARKUP_INVALID ){
1576 n = 1;
1577 }else
1578
1579 /* If the markup is not font-change markup ignore it if the
1580 ** font-change-only flag is set.
1581 */
1582 if( (markup.iType&MUTYPE_FONT)==0 &&
1583 (renderer.state & FONT_MARKUP_ONLY)!=0 ){
1584 /* Do nothing */
1585 }else
1586
1587 if( markup.iCode==MARKUP_NOWIKI ){
1588 if( markup.endTag ){
1589 renderer.state |= ALLOW_WIKI;
1590 }else{
1591 renderer.state &= ~ALLOW_WIKI;
1592 }
1593 }else
1594
1595 /* Ignore block markup for in-line rendering.
1596 */
1597 if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
1598 /* Do nothing */
1599 }else
1600
1601 /* Generate end-tags */
1602 if( markup.endTag ){
1603 popStackToTag(&renderer, markup.iCode);
1604 }else
1605
1606 /* Push <div> markup onto the stack together with the id=ID attribute.
1607 */
1608 if( markup.iCode==MARKUP_DIV ){
1609 pushStackWithId(&renderer, markup.iCode, markupId(&markup),
1610 (renderer.state & ALLOW_WIKI)!=0);
1611 }else
1612
1613 /* Enter <verbatim> processing. With verbatim enabled, all other
1614 ** markup other than the corresponding end-tag with the same ID is
1615 ** ignored.
1616 */
1617 if( markup.iCode==MARKUP_VERBATIM ){
1618 int vAttrIdx, vAttrDidAppend=0;
1619 renderer.zVerbatimId = 0;
1620 renderer.inVerbatim = 1;
1621 renderer.preVerbState = renderer.state;
1622 renderer.state &= ~ALLOW_WIKI;
1623 for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
1624 if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
1625 renderer.zVerbatimId = markup.aAttr[0].zValue;
1626 }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
1627 vAttrDidAppend=1;
1628 }
1629 }
1630 renderer.wantAutoParagraph = 0;
1631 }
1632
1633 /* Restore the input text to its original configuration
1634 */
1635 unparseMarkup(&markup);
1636 break;
1637 }
1638 default: {
1639 break;
1640 }
1641 }
1642 z += n;
1643 }
1644 }
1645
+3 -3
--- src/winhttp.c
+++ src/winhttp.c
@@ -137,13 +137,13 @@
137137
** Start a listening socket and process incoming HTTP requests on
138138
** that socket.
139139
*/
140140
void win32_http_server(
141141
int mnPort, int mxPort, /* Range of allowed TCP port numbers */
142
- char *zBrowser, /* Command to launch browser. (Or NULL) */
143
- char *zStopper, /* Stop server when this file is exists (Or NULL) */
144
- char *zNotFound /* The --notfound option, or NULL */
142
+ const char *zBrowser, /* Command to launch browser. (Or NULL) */
143
+ const char *zStopper, /* Stop server when this file is exists (Or NULL) */
144
+ const char *zNotFound /* The --notfound option, or NULL */
145145
){
146146
WSADATA wd;
147147
SOCKET s = INVALID_SOCKET;
148148
SOCKADDR_IN addr;
149149
int idCnt = 0;
150150
--- src/winhttp.c
+++ src/winhttp.c
@@ -137,13 +137,13 @@
137 ** Start a listening socket and process incoming HTTP requests on
138 ** that socket.
139 */
140 void win32_http_server(
141 int mnPort, int mxPort, /* Range of allowed TCP port numbers */
142 char *zBrowser, /* Command to launch browser. (Or NULL) */
143 char *zStopper, /* Stop server when this file is exists (Or NULL) */
144 char *zNotFound /* The --notfound option, or NULL */
145 ){
146 WSADATA wd;
147 SOCKET s = INVALID_SOCKET;
148 SOCKADDR_IN addr;
149 int idCnt = 0;
150
--- src/winhttp.c
+++ src/winhttp.c
@@ -137,13 +137,13 @@
137 ** Start a listening socket and process incoming HTTP requests on
138 ** that socket.
139 */
140 void win32_http_server(
141 int mnPort, int mxPort, /* Range of allowed TCP port numbers */
142 const char *zBrowser, /* Command to launch browser. (Or NULL) */
143 const char *zStopper, /* Stop server when this file is exists (Or NULL) */
144 const char *zNotFound /* The --notfound option, or NULL */
145 ){
146 WSADATA wd;
147 SOCKET s = INVALID_SOCKET;
148 SOCKADDR_IN addr;
149 int idCnt = 0;
150
+3
--- src/xfer.c
+++ src/xfer.c
@@ -518,10 +518,11 @@
518518
int cnt = 0;
519519
db_prepare(&q,
520520
"SELECT uuid FROM unclustered JOIN blob USING(rid)"
521521
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
522522
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
523
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
523524
);
524525
while( db_step(&q)==SQLITE_ROW ){
525526
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
526527
cnt++;
527528
}
@@ -536,10 +537,11 @@
536537
Stmt q;
537538
db_prepare(&q,
538539
"SELECT uuid FROM blob "
539540
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
540541
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
542
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
541543
);
542544
while( db_step(&q)==SQLITE_ROW ){
543545
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
544546
}
545547
db_finalize(&q);
@@ -1064,10 +1066,11 @@
10641066
nCardSent = 0;
10651067
nCardRcvd = 0;
10661068
xfer.nFileSent = 0;
10671069
xfer.nDeltaSent = 0;
10681070
xfer.nGimmeSent = 0;
1071
+ xfer.nIGotSent = 0;
10691072
fflush(stdout);
10701073
http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
10711074
blob_reset(&send);
10721075
10731076
/* Begin constructing the next message (which might never be
10741077
--- src/xfer.c
+++ src/xfer.c
@@ -518,10 +518,11 @@
518 int cnt = 0;
519 db_prepare(&q,
520 "SELECT uuid FROM unclustered JOIN blob USING(rid)"
521 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
522 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
 
523 );
524 while( db_step(&q)==SQLITE_ROW ){
525 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
526 cnt++;
527 }
@@ -536,10 +537,11 @@
536 Stmt q;
537 db_prepare(&q,
538 "SELECT uuid FROM blob "
539 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
540 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
 
541 );
542 while( db_step(&q)==SQLITE_ROW ){
543 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
544 }
545 db_finalize(&q);
@@ -1064,10 +1066,11 @@
1064 nCardSent = 0;
1065 nCardRcvd = 0;
1066 xfer.nFileSent = 0;
1067 xfer.nDeltaSent = 0;
1068 xfer.nGimmeSent = 0;
 
1069 fflush(stdout);
1070 http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
1071 blob_reset(&send);
1072
1073 /* Begin constructing the next message (which might never be
1074
--- src/xfer.c
+++ src/xfer.c
@@ -518,10 +518,11 @@
518 int cnt = 0;
519 db_prepare(&q,
520 "SELECT uuid FROM unclustered JOIN blob USING(rid)"
521 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
522 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
523 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
524 );
525 while( db_step(&q)==SQLITE_ROW ){
526 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
527 cnt++;
528 }
@@ -536,10 +537,11 @@
537 Stmt q;
538 db_prepare(&q,
539 "SELECT uuid FROM blob "
540 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
541 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
542 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
543 );
544 while( db_step(&q)==SQLITE_ROW ){
545 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
546 }
547 db_finalize(&q);
@@ -1064,10 +1066,11 @@
1066 nCardSent = 0;
1067 nCardRcvd = 0;
1068 xfer.nFileSent = 0;
1069 xfer.nDeltaSent = 0;
1070 xfer.nGimmeSent = 0;
1071 xfer.nIGotSent = 0;
1072 fflush(stdout);
1073 http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
1074 blob_reset(&send);
1075
1076 /* Begin constructing the next message (which might never be
1077
+24 -5
--- src/zip.c
+++ src/zip.c
@@ -378,23 +378,42 @@
378378
blob_reset(&filename);
379379
zip_close(pZip);
380380
}
381381
382382
/*
383
-** COMMAND: test-baseline-zip
383
+** COMMAND: zip
384
+**
385
+** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME]
384386
**
385
-** Generate a ZIP archive for a specified baseline.
387
+** Generate a ZIP archive for a specified version. If the --name option is
388
+** used, it argument becomes the name of the top-level directory in the
389
+** resulting ZIP archive. If --name is omitted, the top-level directory
390
+** named is derived from the project name, the check-in date and time, and
391
+** the artifact ID of the check-in.
386392
*/
387393
void baseline_zip_cmd(void){
388394
int rid;
389395
Blob zip;
396
+ const char *zName;
397
+ zName = find_option("name", 0, 1);
398
+ db_find_and_open_repository(1);
390399
if( g.argc!=4 ){
391
- usage("UUID ZIPFILE");
400
+ usage("VERSION OUTPUTFILE");
392401
}
393
- db_must_be_within_tree();
394402
rid = name_to_rid(g.argv[2]);
395
- zip_of_baseline(rid, &zip, g.argv[2]);
403
+ if( zName==0 ){
404
+ zName = db_text("default-name",
405
+ "SELECT replace(%Q,' ','_') "
406
+ " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
407
+ " || substr(blob.uuid, 1, 10)"
408
+ " FROM event, blob"
409
+ " WHERE event.objid=%d"
410
+ " AND blob.rid=%d",
411
+ db_get("project-name", "unnamed"), rid, rid
412
+ );
413
+ }
414
+ zip_of_baseline(rid, &zip, zName);
396415
blob_write_to_file(&zip, g.argv[3]);
397416
}
398417
399418
/*
400419
** WEBPAGE: zip
401420
--- src/zip.c
+++ src/zip.c
@@ -378,23 +378,42 @@
378 blob_reset(&filename);
379 zip_close(pZip);
380 }
381
382 /*
383 ** COMMAND: test-baseline-zip
 
 
384 **
385 ** Generate a ZIP archive for a specified baseline.
 
 
 
 
386 */
387 void baseline_zip_cmd(void){
388 int rid;
389 Blob zip;
 
 
 
390 if( g.argc!=4 ){
391 usage("UUID ZIPFILE");
392 }
393 db_must_be_within_tree();
394 rid = name_to_rid(g.argv[2]);
395 zip_of_baseline(rid, &zip, g.argv[2]);
 
 
 
 
 
 
 
 
 
 
 
396 blob_write_to_file(&zip, g.argv[3]);
397 }
398
399 /*
400 ** WEBPAGE: zip
401
--- src/zip.c
+++ src/zip.c
@@ -378,23 +378,42 @@
378 blob_reset(&filename);
379 zip_close(pZip);
380 }
381
382 /*
383 ** COMMAND: zip
384 **
385 ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME]
386 **
387 ** Generate a ZIP archive for a specified version. If the --name option is
388 ** used, it argument becomes the name of the top-level directory in the
389 ** resulting ZIP archive. If --name is omitted, the top-level directory
390 ** named is derived from the project name, the check-in date and time, and
391 ** the artifact ID of the check-in.
392 */
393 void baseline_zip_cmd(void){
394 int rid;
395 Blob zip;
396 const char *zName;
397 zName = find_option("name", 0, 1);
398 db_find_and_open_repository(1);
399 if( g.argc!=4 ){
400 usage("VERSION OUTPUTFILE");
401 }
 
402 rid = name_to_rid(g.argv[2]);
403 if( zName==0 ){
404 zName = db_text("default-name",
405 "SELECT replace(%Q,' ','_') "
406 " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
407 " || substr(blob.uuid, 1, 10)"
408 " FROM event, blob"
409 " WHERE event.objid=%d"
410 " AND blob.rid=%d",
411 db_get("project-name", "unnamed"), rid, rid
412 );
413 }
414 zip_of_baseline(rid, &zip, zName);
415 blob_write_to_file(&zip, g.argv[3]);
416 }
417
418 /*
419 ** WEBPAGE: zip
420
--- www/branching.wiki
+++ www/branching.wiki
11
ADDED www/build-icons/linux.gif
22
ADDED www/build-icons/linux64.gif
33
ADDED www/build-icons/mac.gif
44
ADDED www/build-icons/src.gif
55
ADDED www/build-icons/win32.gif
--- www/branching.wiki
+++ www/branching.wiki
0
1 DDED www/build-icons/linux.gif
2 DDED www/build-icons/linux64.gif
3 DDED www/build-icons/mac.gif
4 DDED www/build-icons/src.gif
5 DDED www/build-icons/win32.gif
--- www/branching.wiki
+++ www/branching.wiki
0
1 DDED www/build-icons/linux.gif
2 DDED www/build-icons/linux64.gif
3 DDED www/build-icons/mac.gif
4 DDED www/build-icons/src.gif
5 DDED www/build-icons/win32.gif

Binary file

Binary file

+1 -4
--- www/build.wiki
+++ www/build.wiki
@@ -34,13 +34,10 @@
3434
3535
<li><p>Select a version of of fossil you want to download. Click on its
3636
link. Note that you must successfully log in as "anonymous" in step 1
3737
above in order to see the link to the detailed version information.</p></li>
3838
39
-<li><p>On the version information page, click on the "[details]" hyperlink
40
-that appears right after the check-in comment at the top of the page.</p>
41
-
4239
<li><p>Finally, click on the
4340
"Zip Archive" link. This link will build a ZIP archive of the
4441
complete source code and download it to your browser.
4542
</ol>
4643
@@ -54,11 +51,11 @@
5451
ZIP archive you downloaded into that directory. You should be
5552
in the top-level folder of that directory</p></li>
5653
5754
<li><p><b>(Optional:)</b>
5855
Edit the Makefile to set it up like you want. You probably do not
59
-need to do anything. Do not be intimidated: There are only 5
56
+need to do anything. Do not be intimidated: There are less than 10
6057
variables in the makefile that can be changed. The whole Makefile
6158
is only a few dozen lines long and most of those lines are comments.</p>
6259
6360
<li><p>Type "<b>make</b>"
6461
</ol>
6562
--- www/build.wiki
+++ www/build.wiki
@@ -34,13 +34,10 @@
34
35 <li><p>Select a version of of fossil you want to download. Click on its
36 link. Note that you must successfully log in as "anonymous" in step 1
37 above in order to see the link to the detailed version information.</p></li>
38
39 <li><p>On the version information page, click on the "[details]" hyperlink
40 that appears right after the check-in comment at the top of the page.</p>
41
42 <li><p>Finally, click on the
43 "Zip Archive" link. This link will build a ZIP archive of the
44 complete source code and download it to your browser.
45 </ol>
46
@@ -54,11 +51,11 @@
54 ZIP archive you downloaded into that directory. You should be
55 in the top-level folder of that directory</p></li>
56
57 <li><p><b>(Optional:)</b>
58 Edit the Makefile to set it up like you want. You probably do not
59 need to do anything. Do not be intimidated: There are only 5
60 variables in the makefile that can be changed. The whole Makefile
61 is only a few dozen lines long and most of those lines are comments.</p>
62
63 <li><p>Type "<b>make</b>"
64 </ol>
65
--- www/build.wiki
+++ www/build.wiki
@@ -34,13 +34,10 @@
34
35 <li><p>Select a version of of fossil you want to download. Click on its
36 link. Note that you must successfully log in as "anonymous" in step 1
37 above in order to see the link to the detailed version information.</p></li>
38
 
 
 
39 <li><p>Finally, click on the
40 "Zip Archive" link. This link will build a ZIP archive of the
41 complete source code and download it to your browser.
42 </ol>
43
@@ -54,11 +51,11 @@
51 ZIP archive you downloaded into that directory. You should be
52 in the top-level folder of that directory</p></li>
53
54 <li><p><b>(Optional:)</b>
55 Edit the Makefile to set it up like you want. You probably do not
56 need to do anything. Do not be intimidated: There are less than 10
57 variables in the makefile that can be changed. The whole Makefile
58 is only a few dozen lines long and most of those lines are comments.</p>
59
60 <li><p>Type "<b>make</b>"
61 </ol>
62
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -58,12 +58,12 @@
5858
only works when you start your server using the "<b>fossil server</b>"
5959
or "<b>fossil ui</b>"
6060
command line and is intended to show what the documentation you are currently
6161
editing looks like before you check it in.
6262
63
-Finally, the <i>&lt;filename&gt;</i> element of the URL is the full
64
-pathname of the documentation file starting from the root of the source
63
+Finally, the <i>&lt;filename&gt;</i> element of the URL is the
64
+pathname of the documentation file relative to the root of the source
6565
tree.
6666
6767
The mimetype (and thus the rendering) of documentation files is
6868
determined by the file suffix. Fossil currently understands 192
6969
different file suffixes, including all the popular ones such as
@@ -110,14 +110,29 @@
110110
For example, to see the version of this document associated with
111111
check-in [9be1b00392], simply replace the "<b>/tip/</b>" with
112112
"<b>/9be1b00392/</b>". You can also substitute the symbolic name
113113
for a particular version or branch. For example, you might
114114
replace "<b>/tip/</b>" with "<b>/trunk/</b>" to get the latest
115
-version of this document in the "trunk" branch. (As of this writing,
116
-the self-hosting fossil repository only has a single branch "trunk" and
117
-so "trunk" and "tip" amount to the same thing, but they would be different
118
-in a project with multiple branches.)
115
+version of this document in the "trunk" branch. The symbolic name
116
+can also be a date and time string in any of the following formats:</p>
117
+
118
+<ul>
119
+<li> <i>YYYY-MM-DD</i>
120
+<li> <i>YYYY-MM-DD</i><b>T</b><i>HH:MM</i>
121
+<li> <i>YYYY-MM-DD</i><b>T</b><i>HH:MM:SS</i>
122
+</ul>
123
+
124
+When the symbolic name is a date and time, fossil shows the version
125
+of the document that was most recently checked in as of the date
126
+and time specified. So, for example, to see what the fossil website
127
+looked like at the beginning of 2010, enter:
128
+
129
+<blockquote>
130
+<a href="http://www.fossil-scm.org/index.html/doc/2010-01-01/www/index.wiki">
131
+http://www.fossil-scm.org/index.html/doc/<b>2010-01-01</b>/www/index.wiki
132
+</a>
133
+</blockquote>
119134
120135
The file that encodes this document is stored in the fossil source tree under
121136
the name "<b>www/embeddeddoc.wiki</b>" and so that name forms the
122137
last part of the URL for this document.
123138
124139
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -58,12 +58,12 @@
58 only works when you start your server using the "<b>fossil server</b>"
59 or "<b>fossil ui</b>"
60 command line and is intended to show what the documentation you are currently
61 editing looks like before you check it in.
62
63 Finally, the <i>&lt;filename&gt;</i> element of the URL is the full
64 pathname of the documentation file starting from the root of the source
65 tree.
66
67 The mimetype (and thus the rendering) of documentation files is
68 determined by the file suffix. Fossil currently understands 192
69 different file suffixes, including all the popular ones such as
@@ -110,14 +110,29 @@
110 For example, to see the version of this document associated with
111 check-in [9be1b00392], simply replace the "<b>/tip/</b>" with
112 "<b>/9be1b00392/</b>". You can also substitute the symbolic name
113 for a particular version or branch. For example, you might
114 replace "<b>/tip/</b>" with "<b>/trunk/</b>" to get the latest
115 version of this document in the "trunk" branch. (As of this writing,
116 the self-hosting fossil repository only has a single branch "trunk" and
117 so "trunk" and "tip" amount to the same thing, but they would be different
118 in a project with multiple branches.)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
120 The file that encodes this document is stored in the fossil source tree under
121 the name "<b>www/embeddeddoc.wiki</b>" and so that name forms the
122 last part of the URL for this document.
123
124
--- www/embeddeddoc.wiki
+++ www/embeddeddoc.wiki
@@ -58,12 +58,12 @@
58 only works when you start your server using the "<b>fossil server</b>"
59 or "<b>fossil ui</b>"
60 command line and is intended to show what the documentation you are currently
61 editing looks like before you check it in.
62
63 Finally, the <i>&lt;filename&gt;</i> element of the URL is the
64 pathname of the documentation file relative to the root of the source
65 tree.
66
67 The mimetype (and thus the rendering) of documentation files is
68 determined by the file suffix. Fossil currently understands 192
69 different file suffixes, including all the popular ones such as
@@ -110,14 +110,29 @@
110 For example, to see the version of this document associated with
111 check-in [9be1b00392], simply replace the "<b>/tip/</b>" with
112 "<b>/9be1b00392/</b>". You can also substitute the symbolic name
113 for a particular version or branch. For example, you might
114 replace "<b>/tip/</b>" with "<b>/trunk/</b>" to get the latest
115 version of this document in the "trunk" branch. The symbolic name
116 can also be a date and time string in any of the following formats:</p>
117
118 <ul>
119 <li> <i>YYYY-MM-DD</i>
120 <li> <i>YYYY-MM-DD</i><b>T</b><i>HH:MM</i>
121 <li> <i>YYYY-MM-DD</i><b>T</b><i>HH:MM:SS</i>
122 </ul>
123
124 When the symbolic name is a date and time, fossil shows the version
125 of the document that was most recently checked in as of the date
126 and time specified. So, for example, to see what the fossil website
127 looked like at the beginning of 2010, enter:
128
129 <blockquote>
130 <a href="http://www.fossil-scm.org/index.html/doc/2010-01-01/www/index.wiki">
131 http://www.fossil-scm.org/index.html/doc/<b>2010-01-01</b>/www/index.wiki
132 </a>
133 </blockquote>
134
135 The file that encodes this document is stored in the fossil source tree under
136 the name "<b>www/embeddeddoc.wiki</b>" and so that name forms the
137 last part of the URL for this document.
138
139
+237 -85
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -1,26 +1,23 @@
11
<title>Fossil File Formats</title>
22
<h1 align="center">
33
Fossil File Formats
44
</h1>
55
6
-<p>The global state of a fossil repository is kept simple so that it can
6
+The global state of a fossil repository is kept simple so that it can
77
endure in useful form for decades or centuries.
88
A fossil repository is intended to be readable,
9
-searchable, and extensible by people not yet born.</p>
9
+searchable, and extensible by people not yet born.
1010
11
-<p>
1211
The global state of a fossil repository is an unordered
1312
set of <i>artifacts</i>.
1413
An artifact might be a source code file, the text of a wiki page,
1514
part of a trouble ticket, or one of several special control artifacts
1615
used to show the relationships between other artifacts within the
1716
project. Each artifact is normally represented on disk as a separate
1817
file. Artifacts can be text or binary.
19
-</p>
2018
21
-<p>
2219
In addition to the global state,
2320
each fossil repository also contains local state.
2421
The local state consists of web-page formatting
2522
preferences, authorized users, ticket display and reporting formats,
2623
and so forth. The global state is shared in common among all
@@ -29,82 +26,73 @@
2926
The local state is not versioned and is not synchronized
3027
with the global state.
3128
The local state is not composed of artifacts and is not intended to be enduring.
3229
This document is concerned with global state only. Local state is only
3330
mentioned here in order to distinguish it from global state.
34
-</p>
3531
36
-<p>
3732
Each artifact in the repository is named by its SHA1 hash.
3833
No prefixes or meta information is added to a artifact before
3934
its hash is computed. The name of a artifact in the repository
4035
is exactly the same SHA1 hash that is computed by sha1sum
4136
on the file as it exists in your source tree.</p>
4237
43
-<p>
4438
Some artifacts have a particular format which gives them special
45
-meaning to fossil. Fossil recognizes:</p>
39
+meaning to fossil. Fossil recognizes:
4640
4741
<ul>
48
-<li> Manifests </li>
49
-<li> Clusters </li>
50
-<li> Control Artifacts </li>
51
-<li> Wiki Pages </li>
52
-<li> Ticket Changes </li>
42
+<li> [#manifest | Manifests] </li>
43
+<li> [#cluster | Clusters] </li>
44
+<li> [#ctrl | Control Artifacts] </li>
45
+<li> [#wikichng | Wiki Pages] </li>
46
+<li> [#tktchng | Ticket Changes] </li>
47
+<li> [#artifact | Artifacts] </li>
5348
</ul>
5449
55
-<p>These five artifact types are described in the sequel.</p>
50
+These five artifact types are described in the sequel.
5651
57
-<p>In the current implementation (as of 2009-01-25) the artifacts that
52
+In the current implementation (as of 2009-01-25) the artifacts that
5853
make up a fossil repository are stored in in as delta- and zlib-compressed
5954
blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This
6055
is an implementation detail and might change in a future release. For
6156
the purpose of this article "file format" means the format of the artifacts,
6257
not how the artifacts are stored on disk. It is the artifact format that
6358
is intended to be enduring. The specifics of how artifacts are stored on
6459
disk, though stable, is not intended to live as long as the
65
-artifact format.</p>
60
+artifact format.
6661
62
+<a name="manifest"></a>
6763
<h2>1.0 The Manifest</h2>
6864
69
-<p>A manifest defines a check-in or version of the project
65
+A manifest defines a check-in or version of the project
7066
source tree. The manifest contains a list of artifacts for
7167
each file in the project and the corresponding filenames, as
7268
well as information such as parent check-ins, the name of the
7369
programmer who created the check-in, the date and time when
7470
the check-in was created, and any check-in comments associated
75
-with the check-in.</p>
71
+with the check-in.
7672
77
-<p>
7873
Any artifact in the repository that follows the syntactic rules
7974
of a manifest is a manifest. Note that a manifest can
8075
be both a real manifest and also a content file, though this
8176
is rare.
82
-</p>
8377
84
-<p>
8578
A manifest is a text file. Newline characters
8679
(ASCII 0x0a) separate the file into "cards".
8780
Each card begins with a single
8881
character "card type". Zero or more arguments may follow
8982
the card type. All arguments are separated from each other
9083
and from the card-type character by a single space
9184
character. There is no surplus white space between arguments
9285
and no leading or trailing whitespace except for the newline
9386
character that acts as the card separator.
94
-</p>
9587
96
-<p>
9788
All cards of the manifest occur in strict sorted lexicographical order.
9889
No card may be duplicated.
9990
The entire manifest may be PGP clear-signed, but otherwise it
10091
may contain no additional text or data beyond what is described here.
101
-</p>
10292
103
-<p>
10493
Allowed cards in the manifest are as follows:
105
-</p>
10694
10795
<blockquote>
10896
<b>C</b> <i>checkin-comment</i><br>
10997
<b>D</b> <i>time-and-date-stamp</i><br>
11098
<b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
@@ -113,11 +101,10 @@
113101
<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br>
114102
<b>U</b> <i>user-login</i><br>
115103
<b>Z</b> <i>manifest-checksum</i>
116104
</blockquote>
117105
118
-<p>
119106
A manifest must have exactly one C-card. The sole argument to
120107
the C-card is a check-in comment that describes the check-in that
121108
the manifest defines. The check-in comment is text. The following
122109
escape sequences are applied to the text:
123110
A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A
@@ -124,24 +111,20 @@
124111
newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash
125112
(ASCII 0x5C) is represented as two backslashes "\\". Apart from
126113
space and newline, no other whitespace characters are allowed in
127114
the check-in comment. Nor are any unprintable characters allowed
128115
in the comment.
129
-</p>
130116
131
-<p>
132117
A manifest must have exactly one D-card. The sole argument to
133118
the D-card is a date-time stamp in the ISO8601 format. The
134119
date and time should be in coordinated universal time (UTC).
135120
The format is:
136
-</p>
137121
138122
<blockquote>
139123
<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>
140124
</blockquote>
141125
142
-<p>
143126
A manifest has zero or more F-cards. Each F-card defines a file
144127
(other than the manifest itself) which is part of the check-in that
145128
the manifest defines. There are two, three, or four arguments.
146129
The first argument
147130
is the pathname of the file in the check-in relative to the root
@@ -157,13 +140,11 @@
157140
always readable and writable. This can be expressed by "w" permission
158141
if desired but is optional.
159142
The optional 4th argument is the name of the same file as it existed in
160143
the parent check-in. If the name of the file is unchanged from its
161144
parent, then the 4th argument is omitted.
162
-</p>
163145
164
-<p>
165146
A manifest has zero or one P-cards. Most manifests have one P-card.
166147
The P-card has a varying number of arguments that
167148
defines other manifests from which the current manifest
168149
is derived. Each argument is an 40-character lowercase
169150
hexadecimal SHA1 of the predecessor manifest. All arguments
@@ -171,13 +152,11 @@
171152
The first predecessor is the direct ancestor of the manifest.
172153
Other arguments define manifests with which the first was
173154
merged to yield the current manifest. Most manifests have
174155
a P-card with a single argument. The first manifest in the
175156
project has no ancestors and thus has no P-card.
176
-</p>
177157
178
-<p>
179158
A manifest may optionally have a single R-card. The R-card has
180159
a single argument which is the MD5 checksum of all files in
181160
the check-in except the manifest itself. The checksum is expressed
182161
as 32-characters of lowercase hexadecimal. The checksum is
183162
computed as follows: For each file in the check-in (except for
@@ -185,13 +164,11 @@
185164
take the pathname of the file relative to the root of the
186165
repository, append a single space (ASCII 0x20), the
187166
size of the file in ASCII decimal, a single newline
188167
character (ASCII 0x0A), and the complete text of the file.
189168
Compute the MD5 checksum of the the result.
190
-</p>
191169
192
-<p>
193170
A manifest might contain one or more T-cards used to set tags or
194171
properties on the check-in. The format of the T-card is the same as
195172
described in <i>Control Artifacts</i> section below, except that the
196173
second argument is the single characcter "<b>*</b>" instead of an
197174
artifact ID. The <b>*</b> in place of the artifact ID indicates that
@@ -199,42 +176,35 @@
199176
possible to encode the current artifact ID as part of an artifact,
200177
since the act of inserting the artifact ID would change the artifact ID,
201178
hence a <b>*</b> is used to represent "self". T-cards are typically
202179
added to manifests in order to set the <b>branch</b> property and a
203180
symbolic name when the check-in is intended to start a new branch.
204
-</p>
205181
206
-<p>
207182
Each manifest has a single U-card. The argument to the U-card is
208183
the login of the user who created the manifest. The login name
209184
is encoded using the same character escapes as is used for the
210185
check-in comment argument to the C-card.
211
-</p>
212186
213
-<p>
214187
A manifest has an option Z-card as its last line. The argument
215188
to the Z-card is a 32-character lowercase hexadecimal MD5 hash
216189
of all prior lines of the manifest up to and including the newline
217190
character that immediately precedes the "Z". The Z-card is just
218191
a sanity check to prove that the manifest is well-formed and
219192
consistent.
220
-</p>
221193
222
-<p>A sample manifest from Fossil itself can be seen
194
+A sample manifest from Fossil itself can be seen
223195
[/artifact/28987096ac | here].
224196
197
+<a name="cluster"></a>
225198
<h2>2.0 Clusters</h2>
226199
227
-<p>
228200
A cluster is a artifact that declares the existence of other artifacts.
229201
Clusters are used during repository synchronization to help
230202
reduce network traffic. As such, clusters are an optimization and
231203
may be removed from a repository without loss or damage to the
232204
underlying project code.
233
-</p>
234205
235
-<p>
236206
Clusters follow a syntax that is very similar to manifests.
237207
A Cluster is a line-oriented text file. Newline characters
238208
(ASCII 0x0a) separate the artifact into cards. Each card begins with a single
239209
character "card type". Zero or more arguments may follow
240210
the card type. All arguments are separated from each other
@@ -245,67 +215,58 @@
245215
All cards of a cluster occur in strict sorted lexicographical order.
246216
No card may be duplicated.
247217
The cluster may not contain additional text or data beyond
248218
what is described here.
249219
Unlike manifests, clusters are never PGP signed.
250
-</p>
251220
252
-<p>
253221
Allowed cards in the cluster are as follows:
254
-</p>
255222
256223
<blockquote>
257224
<b>M</b> <i>artifact-id</i><br />
258225
<b>Z</b> <i>checksum</i>
259226
</blockquote>
260227
261
-<p>
262228
A cluster contains one or more "M" cards followed by a single "Z"
263229
line. Each M card has a single argument which is the artifact ID of
264230
another artifact in the repository. The Z card work exactly like
265231
the Z card of a manifest. The argument to the Z card is the
266232
lower-case hexadecimal representation of the MD5 checksum of all
267233
prior cards in the cluster. Note that the Z card is required
268234
on a cluster.
269
-</p>
270235
271
-<p>An example cluster from Fossil can be seen
272
-[/artifact/d03dbdd73a2a8 | here].</p>
236
+An example cluster from Fossil can be seen
237
+[/artifact/d03dbdd73a2a8 | here].
273238
274
-
239
+<a name="ctrl"></a>
275240
<h2>3.0 Control Artifacts</h2>
276241
277
-<p>
278242
Control artifacts are used to assign properties to other artifacts
279243
within the repository. The basic format of a control artifact is
280244
the same as a manifest or cluster. A control artifact is a text
281245
files divided into cards by newline characters. Each card has a
282246
single-character card type followed by arguments. Spaces separate
283247
the card type and the arguments. No surplus whitespace is allowed.
284248
All cards must occur in strict lexigraphical order.
285
-</p>
286249
287
-<p>
288250
Allowed cards in a control artifact are as follows:
289
-</p>
290251
291252
<blockquote>
292253
<b>D</b> <i>time-and-date-stamp</i><br />
293254
<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name artifact-id ?value?</i><br />
255
+<b>U</b> <i>user-name</i><br />
294256
<b>Z</b> <i>checksum</i><br />
295257
</blockquote>
296258
297
-<p>
298259
A control artifact must have one D card and one Z card and
299260
one or more T cards. No other cards or other text is
300261
allowed in a control artifact. Control artifacts might be PGP
301
-clearsigned.</p>
262
+clearsigned.
302263
303
-<p>The D card and the Z card of a control artifact are the same
304
-as in a manifest.</p>
264
+The D card and the Z card of a control artifact are the same
265
+as in a manifest.
305266
306
-<p>The T card represents a "tag" or property that is applied to
267
+The T card represents a "tag" or property that is applied to
307268
some other artifact. The T card has two or three values. The
308269
second argument is the 40 character lowercase artifact ID of the artifact
309270
to which the tag is to be applied. The
310271
first value is the tag name. The first character of the tag
311272
is either "+", "-", or "*". A "+" means the tag should be added
@@ -313,28 +274,37 @@
313274
The "*" character means the tag should be added to the artifact
314275
and all direct descendants (but not branches) of the artifact down
315276
to but not including the first descendant that contains a
316277
more recent "-" tag with the same name.
317278
The optional third argument is the value of the tag. A tag
318
-without a value is a boolean.</p>
279
+without a value is a boolean.
319280
320
-<p>When two or more tags with the same name are applied to the
281
+When two or more tags with the same name are applied to the
321282
same artifact, the tag with the latest (most recent) date is
322
-used.</p>
283
+used.
323284
324
-<p>Some tags have special meaning. The "comment" tag when applied
285
+Some tags have special meaning. The "comment" tag when applied
325286
to a check-in will override the check-in comment of that check-in
326
-for display purposes.</p>
287
+for display purposes. The "user" tag overrides the name of the
288
+check-in user. The "date" tag overrides the check-in date.
289
+The "branch" tag sets the name of the branch that at check-in
290
+belongs to. Symbolic tags begin with the "sym-" prefix.
291
+
292
+The U card is the name of the user that created the control
293
+artifact. The Z card is the usual artifact checksum.
294
+
295
+An example control artifacts can be seen [/info/9d302ccda8 | here].
296
+
327297
328298
<a name="wikichng"></a>
329299
<h2>4.0 Wiki Pages</h2>
330300
331
-<p>A wiki page is an artifact with a format similar to manifests,
301
+A wiki page is an artifact with a format similar to manifests,
332302
clusters, and control artifacts. The artifact is divided into
333303
cards by newline characters. The format of each card is as in
334304
manifests, clusters, and control artifacts. Wiki artifacts accept
335
-the following card types:</p>
305
+the following card types:
336306
337307
<blockquote>
338308
<b>D</b> <i>time-and-date-stamp</i><br />
339309
<b>L</b> <i>wiki-title</i><br />
340310
<b>P</b> <i>parent-artifact-id</i>+<br />
@@ -341,63 +311,245 @@
341311
<b>U</b> <i>user-name</i><br />
342312
<b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br />
343313
<b>Z</b> <i>checksum</i>
344314
</blockquote>
345315
346
-<p>The D card is the date and time when the wiki page was edited.
316
+The D card is the date and time when the wiki page was edited.
347317
The P card specifies the parent wiki pages, if any. The L card
348318
gives the name of the wiki page. The U card specifies the login
349319
of the user who made this edit to the wiki page. The Z card is
350
-the usual checksum over the either artifact.</p>
320
+the usual checksum over the either artifact.
351321
352
-<p>The W card is used to specify the text of the wiki page. The
322
+The W card is used to specify the text of the wiki page. The
353323
argument to the W card is an integer which is the number of bytes
354324
of text in the wiki page. That text follows the newline character
355325
that terminates the W card. The wiki text is always followed by one
356
-extra newline.</p>
326
+extra newline.
327
+
328
+An example wiki artifact can be seen
329
+[/artifact/7b2f5fd0e0 | here].
357330
358331
<a name="tktchng"></a>
359332
<h2>5.0 Ticket Changes</h2>
360333
361
-<p>A ticket-change artifact represents a change to a trouble ticket.
362
-The following cards are allowed on a ticket change artifact:</p>
334
+A ticket-change artifact represents a change to a trouble ticket.
335
+The following cards are allowed on a ticket change artifact:
363336
364337
<blockquote>
365338
<b>D</b> <i>time-and-date-stamp</i><br />
366339
<b>J</b> ?<b>+</b>?<i>name</i> ?<i>value</i>?<br />
367340
<b>K</b> <i>ticket-id</i><br />
368341
<b>U</b> <i>user-name</i><br />
369342
<b>Z</b> <i>checksum</i>
370343
</blockquote>
371344
372
-<p>
373345
The D card is the usual date and time stamp and represents the point
374346
in time when the change was entered. The U card is the login of the
375347
programmer who entered this change. The Z card is the checksum over
376
-the entire artifact.</p>
348
+the entire artifact.
377349
378
-<p>
379350
Every ticket has a unique ID. The ticket to which this change is applied
380351
is specified by the K card. A ticket exists if it contains one or
381352
more changes. The first "change" to a ticket is what brings the
382
-ticket into existence.</p>
353
+ticket into existence.
383354
384
-<p>
385355
J cards specify changes to the "value" of "fields" in the ticket.
386356
If the <i>value</i> parameter of the J card is omitted, then the
387357
field is set to an empty string.
388358
Each fossil server has a ticket configuration which specifies the fields its
389359
understands. The ticket configuration is part of the local state for
390360
the repository and thus can vary from one repository to another.
391361
Hence a J card might specify a <i>field</i> that do not exist in the
392362
local ticket configuration. If a J card specifies a <i>field</i> that
393363
is not in the local configuration, then that J card
394
-is simply ignored.</p>
364
+is simply ignored.
395365
396
-<p>
397366
The first argument of the J card is the field name. The second
398367
value is the field value. If the field name begins with "+" then
399368
the value is appended to the prior value. Otherwise, the value
400369
on the J card replaces any previous value of the field.
401370
The field name and value are both encoded using the character
402371
escapes defined for the C card of a manifest.
403
-</p>
372
+
373
+An example ticket-change artifact can be seen
374
+[/artifact/91f1ec6af053 | here].
375
+
376
+<a name="attachment"></a>
377
+<h2>6.0 Attachments</h2>
378
+
379
+An attachment artifact associates some other artifact that is the
380
+attachment (the source artifact) with a ticket or wiki page to which
381
+the attachment is connected (the target artifact).
382
+The following cards are allowed on an attachment artifact:
383
+
384
+<blockquote>
385
+<b>A</b> <i>filename target</i> ?<i>source</i>?
386
+<b>C</b> <i>comment</i><br>
387
+<b>D</b> <i>time-and-date-stamp</i><br />
388
+<b>U</b> <i>user-name</i><br />
389
+<b>Z</b> <i>checksum</i>
390
+</blockquote>
391
+
392
+The A card specifies a filename for the attachment in its first argument.
393
+The second argument to the A card is the name
394
+of the wiki page or ticket to which the attachment is connected. The
395
+third argument is either missing or else it is the 40-character artifact
396
+ID of the attachment itself. A missing third argument means that the
397
+attachment should be deleted.
398
+
399
+The C card is an optional comment describing what the attachment is about.
400
+The C card is optional, but there can only be one.
401
+
402
+A single D card is required to give the date and time when the attachment
403
+was applied.
404
+
405
+A single U card gives the name of the user to added the attachment.
406
+If an attachment is added anonymously, then the U card may be omitted.
407
+
408
+The Z card is the usual checksum over the rest of the attachment artifact.
409
+
410
+
411
+<a name="summary"></a>
412
+<h2>7.0 Card Summary</h2>
413
+
414
+The following table summaries the various kinds of cards that
415
+appear on Fossil artifacts:
416
+
417
+<table border=1 width="100%">
418
+<tr>
419
+<th rowspan=2 valign=bottom>Card Format</th>
420
+<th colspan=6>Used By</th>
421
+</tr>
422
+<tr>
423
+<th>Manifest</th>
424
+<th>Cluster</th>
425
+<th>Control</th>
426
+<th>Wiki</th>
427
+<th>Ticket</th>
428
+<th>Attachment</th>
429
+</tr>
430
+<tr>
431
+<td><b>A</b> <i>filename target source</i></td>
432
+<td>&nbsp;</td>
433
+<td>&nbsp;</td>
434
+<td>&nbsp;</td>
435
+<td>&nbsp;</td>
436
+<td>&nbsp;</td>
437
+<td align=center><b>X</b></td>
438
+</tr>
439
+<tr>
440
+<td><b>C</b> <i>coment-text</i></td>
441
+<td align=center><b>X</b></td>
442
+<td>&nbsp;</td>
443
+<td>&nbsp;</td>
444
+<td>&nbsp;</td>
445
+<td>&nbsp;</td>
446
+<td align=center><b>X</b></td>
447
+</tr>
448
+<tr>
449
+<td><b>D</b> <i>date-time-stamp</i></td>
450
+<td align=center><b>X</b></td>
451
+<td align=center>&nbsp;</td>
452
+<td align=center><b>X</b></td>
453
+<td align=center><b>X</b></td>
454
+<td align=center><b>X</b></td>
455
+<td align=center><b>X</b></td>
456
+</tr>
457
+<tr>
458
+<td><b>F</b> <i>filename uuid permissions oldname</i></td>
459
+<td align=center><b>X</b></td>
460
+<td align=center>&nbsp;</td>
461
+<td align=center>&nbsp;</td>
462
+<td align=center>&nbsp;</td>
463
+<td align=center>&nbsp;</td>
464
+<td align=center>&nbsp;</td>
465
+</tr>
466
+<tr>
467
+<td><b>J</b> <i>name value</i></td>
468
+<td align=center>&nbsp;</td>
469
+<td align=center>&nbsp;</td>
470
+<td align=center>&nbsp;</td>
471
+<td align=center>&nbsp;</td>
472
+<td align=center><b>X</b></td>
473
+<td align=center>&nbsp;</td>
474
+</tr>
475
+<tr>
476
+<td><b>K</b> <i>ticket-uuid</i></td>
477
+<td align=center>&nbsp;</td>
478
+<td align=center>&nbsp;</td>
479
+<td align=center>&nbsp;</td>
480
+<td align=center>&nbsp;</td>
481
+<td align=center><b>X</b></td>
482
+<td align=center>&nbsp;</td>
483
+</tr>
484
+<tr>
485
+<td><b>L</b> <i>wiki-title</i></td>
486
+<td align=center>&nbsp;</td>
487
+<td align=center>&nbsp;</td>
488
+<td align=center>&nbsp;</td>
489
+<td align=center><b>X</b></td>
490
+<td align=center>&nbsp;</td>
491
+<td align=center>&nbsp;</td>
492
+</tr>
493
+<tr>
494
+<td><b>M</b> <i>uuid</i></td>
495
+<td align=center>&nbsp;</td>
496
+<td align=center><b>X</b></td>
497
+<td align=center>&nbsp;</td>
498
+<td align=center>&nbsp;</td>
499
+<td align=center>&nbsp;</td>
500
+<td align=center>&nbsp;</td>
501
+</tr>
502
+<tr>
503
+<td><b>P</b> <i>uuid ...</i></td>
504
+<td align=center><b>X</b></td>
505
+<td align=center>&nbsp;</td>
506
+<td align=center>&nbsp;</td>
507
+<td align=center><b>X</b></td>
508
+<td align=center>&nbsp;</td>
509
+<td align=center>&nbsp;</td>
510
+</tr>
511
+<tr>
512
+<td><b>R</b> <i>md5sum</i></td>
513
+<td align=center><b>X</b></td>
514
+<td align=center>&nbsp;</td>
515
+<td align=center>&nbsp;</td>
516
+<td align=center>&nbsp;</td>
517
+<td align=center>&nbsp;</td>
518
+<td align=center>&nbsp;</td>
519
+<tr>
520
+<td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname uuid value</i></td>
521
+<td align=center><b>X</b></td>
522
+<td align=center>&nbsp;</td>
523
+<td align=center><b>X</b></td>
524
+<td align=center>&nbsp;</td>
525
+<td align=center>&nbsp;</td>
526
+<td align=center>&nbsp;</td>
527
+</tr>
528
+<tr>
529
+<td><b>U</b> <i>username</i></td>
530
+<td align=center><b>X</b></td>
531
+<td align=center>&nbsp;</td>
532
+<td align=center><b>X</b></td>
533
+<td align=center><b>X</b></td>
534
+<td align=center><b>X</b></td>
535
+<td align=center><b>X</b></td>
536
+</tr>
537
+<tr>
538
+<td><b>W</b> <i>size</i></td>
539
+<td align=center>&nbsp;</td>
540
+<td align=center>&nbsp;</td>
541
+<td align=center>&nbsp;</td>
542
+<td align=center><b>X</b></td>
543
+<td align=center>&nbsp;</td>
544
+<td align=center>&nbsp;</td>
545
+</tr>
546
+<tr>
547
+<td><b>Z</b> <i>md5sum</i></td>
548
+<td align=center><b>X</b></td>
549
+<td align=center><b>X</b></td>
550
+<td align=center><b>X</b></td>
551
+<td align=center><b>X</b></td>
552
+<td align=center><b>X</b></td>
553
+<td align=center><b>X</b></td>
554
+</tr>
555
+</table>
404556
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -1,26 +1,23 @@
1 <title>Fossil File Formats</title>
2 <h1 align="center">
3 Fossil File Formats
4 </h1>
5
6 <p>The global state of a fossil repository is kept simple so that it can
7 endure in useful form for decades or centuries.
8 A fossil repository is intended to be readable,
9 searchable, and extensible by people not yet born.</p>
10
11 <p>
12 The global state of a fossil repository is an unordered
13 set of <i>artifacts</i>.
14 An artifact might be a source code file, the text of a wiki page,
15 part of a trouble ticket, or one of several special control artifacts
16 used to show the relationships between other artifacts within the
17 project. Each artifact is normally represented on disk as a separate
18 file. Artifacts can be text or binary.
19 </p>
20
21 <p>
22 In addition to the global state,
23 each fossil repository also contains local state.
24 The local state consists of web-page formatting
25 preferences, authorized users, ticket display and reporting formats,
26 and so forth. The global state is shared in common among all
@@ -29,82 +26,73 @@
29 The local state is not versioned and is not synchronized
30 with the global state.
31 The local state is not composed of artifacts and is not intended to be enduring.
32 This document is concerned with global state only. Local state is only
33 mentioned here in order to distinguish it from global state.
34 </p>
35
36 <p>
37 Each artifact in the repository is named by its SHA1 hash.
38 No prefixes or meta information is added to a artifact before
39 its hash is computed. The name of a artifact in the repository
40 is exactly the same SHA1 hash that is computed by sha1sum
41 on the file as it exists in your source tree.</p>
42
43 <p>
44 Some artifacts have a particular format which gives them special
45 meaning to fossil. Fossil recognizes:</p>
46
47 <ul>
48 <li> Manifests </li>
49 <li> Clusters </li>
50 <li> Control Artifacts </li>
51 <li> Wiki Pages </li>
52 <li> Ticket Changes </li>
 
53 </ul>
54
55 <p>These five artifact types are described in the sequel.</p>
56
57 <p>In the current implementation (as of 2009-01-25) the artifacts that
58 make up a fossil repository are stored in in as delta- and zlib-compressed
59 blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This
60 is an implementation detail and might change in a future release. For
61 the purpose of this article "file format" means the format of the artifacts,
62 not how the artifacts are stored on disk. It is the artifact format that
63 is intended to be enduring. The specifics of how artifacts are stored on
64 disk, though stable, is not intended to live as long as the
65 artifact format.</p>
66
 
67 <h2>1.0 The Manifest</h2>
68
69 <p>A manifest defines a check-in or version of the project
70 source tree. The manifest contains a list of artifacts for
71 each file in the project and the corresponding filenames, as
72 well as information such as parent check-ins, the name of the
73 programmer who created the check-in, the date and time when
74 the check-in was created, and any check-in comments associated
75 with the check-in.</p>
76
77 <p>
78 Any artifact in the repository that follows the syntactic rules
79 of a manifest is a manifest. Note that a manifest can
80 be both a real manifest and also a content file, though this
81 is rare.
82 </p>
83
84 <p>
85 A manifest is a text file. Newline characters
86 (ASCII 0x0a) separate the file into "cards".
87 Each card begins with a single
88 character "card type". Zero or more arguments may follow
89 the card type. All arguments are separated from each other
90 and from the card-type character by a single space
91 character. There is no surplus white space between arguments
92 and no leading or trailing whitespace except for the newline
93 character that acts as the card separator.
94 </p>
95
96 <p>
97 All cards of the manifest occur in strict sorted lexicographical order.
98 No card may be duplicated.
99 The entire manifest may be PGP clear-signed, but otherwise it
100 may contain no additional text or data beyond what is described here.
101 </p>
102
103 <p>
104 Allowed cards in the manifest are as follows:
105 </p>
106
107 <blockquote>
108 <b>C</b> <i>checkin-comment</i><br>
109 <b>D</b> <i>time-and-date-stamp</i><br>
110 <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
@@ -113,11 +101,10 @@
113 <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br>
114 <b>U</b> <i>user-login</i><br>
115 <b>Z</b> <i>manifest-checksum</i>
116 </blockquote>
117
118 <p>
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:
123 A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A
@@ -124,24 +111,20 @@
124 newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash
125 (ASCII 0x5C) is represented as two backslashes "\\". Apart from
126 space and newline, no other whitespace characters are allowed in
127 the check-in comment. Nor are any unprintable characters allowed
128 in the comment.
129 </p>
130
131 <p>
132 A manifest must have exactly one D-card. The sole argument to
133 the D-card is a date-time stamp in the ISO8601 format. The
134 date and time should be in coordinated universal time (UTC).
135 The format is:
136 </p>
137
138 <blockquote>
139 <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>
140 </blockquote>
141
142 <p>
143 A manifest has zero or more F-cards. Each F-card defines a file
144 (other than the manifest itself) which is part of the check-in that
145 the manifest defines. There are two, three, or four arguments.
146 The first argument
147 is the pathname of the file in the check-in relative to the root
@@ -157,13 +140,11 @@
157 always readable and writable. This can be expressed by "w" permission
158 if desired but is optional.
159 The optional 4th argument is the name of the same file as it existed in
160 the parent check-in. If the name of the file is unchanged from its
161 parent, then the 4th argument is omitted.
162 </p>
163
164 <p>
165 A manifest has zero or one P-cards. Most manifests have one P-card.
166 The P-card has a varying number of arguments that
167 defines other manifests from which the current manifest
168 is derived. Each argument is an 40-character lowercase
169 hexadecimal SHA1 of the predecessor manifest. All arguments
@@ -171,13 +152,11 @@
171 The first predecessor is the direct ancestor of the manifest.
172 Other arguments define manifests with which the first was
173 merged to yield the current manifest. Most manifests have
174 a P-card with a single argument. The first manifest in the
175 project has no ancestors and thus has no P-card.
176 </p>
177
178 <p>
179 A manifest may optionally have a single R-card. The R-card has
180 a single argument which is the MD5 checksum of all files in
181 the check-in except the manifest itself. The checksum is expressed
182 as 32-characters of lowercase hexadecimal. The checksum is
183 computed as follows: For each file in the check-in (except for
@@ -185,13 +164,11 @@
185 take the pathname of the file relative to the root of the
186 repository, append a single space (ASCII 0x20), the
187 size of the file in ASCII decimal, a single newline
188 character (ASCII 0x0A), and the complete text of the file.
189 Compute the MD5 checksum of the the result.
190 </p>
191
192 <p>
193 A manifest might contain one or more T-cards used to set tags or
194 properties on the check-in. The format of the T-card is the same as
195 described in <i>Control Artifacts</i> section below, except that the
196 second argument is the single characcter "<b>*</b>" instead of an
197 artifact ID. The <b>*</b> in place of the artifact ID indicates that
@@ -199,42 +176,35 @@
199 possible to encode the current artifact ID as part of an artifact,
200 since the act of inserting the artifact ID would change the artifact ID,
201 hence a <b>*</b> is used to represent "self". T-cards are typically
202 added to manifests in order to set the <b>branch</b> property and a
203 symbolic name when the check-in is intended to start a new branch.
204 </p>
205
206 <p>
207 Each manifest has a single U-card. The argument to the U-card is
208 the login of the user who created the manifest. The login name
209 is encoded using the same character escapes as is used for the
210 check-in comment argument to the C-card.
211 </p>
212
213 <p>
214 A manifest has an option Z-card as its last line. The argument
215 to the Z-card is a 32-character lowercase hexadecimal MD5 hash
216 of all prior lines of the manifest up to and including the newline
217 character that immediately precedes the "Z". The Z-card is just
218 a sanity check to prove that the manifest is well-formed and
219 consistent.
220 </p>
221
222 <p>A sample manifest from Fossil itself can be seen
223 [/artifact/28987096ac | here].
224
 
225 <h2>2.0 Clusters</h2>
226
227 <p>
228 A cluster is a artifact that declares the existence of other artifacts.
229 Clusters are used during repository synchronization to help
230 reduce network traffic. As such, clusters are an optimization and
231 may be removed from a repository without loss or damage to the
232 underlying project code.
233 </p>
234
235 <p>
236 Clusters follow a syntax that is very similar to manifests.
237 A Cluster is a line-oriented text file. Newline characters
238 (ASCII 0x0a) separate the artifact into cards. Each card begins with a single
239 character "card type". Zero or more arguments may follow
240 the card type. All arguments are separated from each other
@@ -245,67 +215,58 @@
245 All cards of a cluster occur in strict sorted lexicographical order.
246 No card may be duplicated.
247 The cluster may not contain additional text or data beyond
248 what is described here.
249 Unlike manifests, clusters are never PGP signed.
250 </p>
251
252 <p>
253 Allowed cards in the cluster are as follows:
254 </p>
255
256 <blockquote>
257 <b>M</b> <i>artifact-id</i><br />
258 <b>Z</b> <i>checksum</i>
259 </blockquote>
260
261 <p>
262 A cluster contains one or more "M" cards followed by a single "Z"
263 line. Each M card has a single argument which is the artifact ID of
264 another artifact in the repository. The Z card work exactly like
265 the Z card of a manifest. The argument to the Z card is the
266 lower-case hexadecimal representation of the MD5 checksum of all
267 prior cards in the cluster. Note that the Z card is required
268 on a cluster.
269 </p>
270
271 <p>An example cluster from Fossil can be seen
272 [/artifact/d03dbdd73a2a8 | here].</p>
273
274
275 <h2>3.0 Control Artifacts</h2>
276
277 <p>
278 Control artifacts are used to assign properties to other artifacts
279 within the repository. The basic format of a control artifact is
280 the same as a manifest or cluster. A control artifact is a text
281 files divided into cards by newline characters. Each card has a
282 single-character card type followed by arguments. Spaces separate
283 the card type and the arguments. No surplus whitespace is allowed.
284 All cards must occur in strict lexigraphical order.
285 </p>
286
287 <p>
288 Allowed cards in a control artifact are as follows:
289 </p>
290
291 <blockquote>
292 <b>D</b> <i>time-and-date-stamp</i><br />
293 <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name artifact-id ?value?</i><br />
 
294 <b>Z</b> <i>checksum</i><br />
295 </blockquote>
296
297 <p>
298 A control artifact must have one D card and one Z card and
299 one or more T cards. No other cards or other text is
300 allowed in a control artifact. Control artifacts might be PGP
301 clearsigned.</p>
302
303 <p>The D card and the Z card of a control artifact are the same
304 as in a manifest.</p>
305
306 <p>The T card represents a "tag" or property that is applied to
307 some other artifact. The T card has two or three values. The
308 second argument is the 40 character lowercase artifact ID of the artifact
309 to which the tag is to be applied. The
310 first value is the tag name. The first character of the tag
311 is either "+", "-", or "*". A "+" means the tag should be added
@@ -313,28 +274,37 @@
313 The "*" character means the tag should be added to the artifact
314 and all direct descendants (but not branches) of the artifact down
315 to but not including the first descendant that contains a
316 more recent "-" tag with the same name.
317 The optional third argument is the value of the tag. A tag
318 without a value is a boolean.</p>
319
320 <p>When two or more tags with the same name are applied to the
321 same artifact, the tag with the latest (most recent) date is
322 used.</p>
323
324 <p>Some tags have special meaning. The "comment" tag when applied
325 to a check-in will override the check-in comment of that check-in
326 for display purposes.</p>
 
 
 
 
 
 
 
 
 
327
328 <a name="wikichng"></a>
329 <h2>4.0 Wiki Pages</h2>
330
331 <p>A wiki page is an artifact with a format similar to manifests,
332 clusters, and control artifacts. The artifact is divided into
333 cards by newline characters. The format of each card is as in
334 manifests, clusters, and control artifacts. Wiki artifacts accept
335 the following card types:</p>
336
337 <blockquote>
338 <b>D</b> <i>time-and-date-stamp</i><br />
339 <b>L</b> <i>wiki-title</i><br />
340 <b>P</b> <i>parent-artifact-id</i>+<br />
@@ -341,63 +311,245 @@
341 <b>U</b> <i>user-name</i><br />
342 <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br />
343 <b>Z</b> <i>checksum</i>
344 </blockquote>
345
346 <p>The D card is the date and time when the wiki page was edited.
347 The P card specifies the parent wiki pages, if any. The L card
348 gives the name of the wiki page. The U card specifies the login
349 of the user who made this edit to the wiki page. The Z card is
350 the usual checksum over the either artifact.</p>
351
352 <p>The W card is used to specify the text of the wiki page. The
353 argument to the W card is an integer which is the number of bytes
354 of text in the wiki page. That text follows the newline character
355 that terminates the W card. The wiki text is always followed by one
356 extra newline.</p>
 
 
 
357
358 <a name="tktchng"></a>
359 <h2>5.0 Ticket Changes</h2>
360
361 <p>A ticket-change artifact represents a change to a trouble ticket.
362 The following cards are allowed on a ticket change artifact:</p>
363
364 <blockquote>
365 <b>D</b> <i>time-and-date-stamp</i><br />
366 <b>J</b> ?<b>+</b>?<i>name</i> ?<i>value</i>?<br />
367 <b>K</b> <i>ticket-id</i><br />
368 <b>U</b> <i>user-name</i><br />
369 <b>Z</b> <i>checksum</i>
370 </blockquote>
371
372 <p>
373 The D card is the usual date and time stamp and represents the point
374 in time when the change was entered. The U card is the login of the
375 programmer who entered this change. The Z card is the checksum over
376 the entire artifact.</p>
377
378 <p>
379 Every ticket has a unique ID. The ticket to which this change is applied
380 is specified by the K card. A ticket exists if it contains one or
381 more changes. The first "change" to a ticket is what brings the
382 ticket into existence.</p>
383
384 <p>
385 J cards specify changes to the "value" of "fields" in the ticket.
386 If the <i>value</i> parameter of the J card is omitted, then the
387 field is set to an empty string.
388 Each fossil server has a ticket configuration which specifies the fields its
389 understands. The ticket configuration is part of the local state for
390 the repository and thus can vary from one repository to another.
391 Hence a J card might specify a <i>field</i> that do not exist in the
392 local ticket configuration. If a J card specifies a <i>field</i> that
393 is not in the local configuration, then that J card
394 is simply ignored.</p>
395
396 <p>
397 The first argument of the J card is the field name. The second
398 value is the field value. If the field name begins with "+" then
399 the value is appended to the prior value. Otherwise, the value
400 on the J card replaces any previous value of the field.
401 The field name and value are both encoded using the character
402 escapes defined for the C card of a manifest.
403 </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -1,26 +1,23 @@
1 <title>Fossil File Formats</title>
2 <h1 align="center">
3 Fossil File Formats
4 </h1>
5
6 The global state of a fossil repository is kept simple so that it can
7 endure in useful form for decades or centuries.
8 A fossil repository is intended to be readable,
9 searchable, and extensible by people not yet born.
10
 
11 The global state of a fossil repository is an unordered
12 set of <i>artifacts</i>.
13 An artifact might be a source code file, the text of a wiki page,
14 part of a trouble ticket, or one of several special control artifacts
15 used to show the relationships between other artifacts within the
16 project. Each artifact is normally represented on disk as a separate
17 file. Artifacts can be text or binary.
 
18
 
19 In addition to the global state,
20 each fossil repository also contains local state.
21 The local state consists of web-page formatting
22 preferences, authorized users, ticket display and reporting formats,
23 and so forth. The global state is shared in common among all
@@ -29,82 +26,73 @@
26 The local state is not versioned and is not synchronized
27 with the global state.
28 The local state is not composed of artifacts and is not intended to be enduring.
29 This document is concerned with global state only. Local state is only
30 mentioned here in order to distinguish it from global state.
 
31
 
32 Each artifact in the repository is named by its SHA1 hash.
33 No prefixes or meta information is added to a artifact before
34 its hash is computed. The name of a artifact in the repository
35 is exactly the same SHA1 hash that is computed by sha1sum
36 on the file as it exists in your source tree.</p>
37
 
38 Some artifacts have a particular format which gives them special
39 meaning to fossil. Fossil recognizes:
40
41 <ul>
42 <li> [#manifest | Manifests] </li>
43 <li> [#cluster | Clusters] </li>
44 <li> [#ctrl | Control Artifacts] </li>
45 <li> [#wikichng | Wiki Pages] </li>
46 <li> [#tktchng | Ticket Changes] </li>
47 <li> [#artifact | Artifacts] </li>
48 </ul>
49
50 These five artifact types are described in the sequel.
51
52 In the current implementation (as of 2009-01-25) the artifacts that
53 make up a fossil repository are stored in in as delta- and zlib-compressed
54 blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This
55 is an implementation detail and might change in a future release. For
56 the purpose of this article "file format" means the format of the artifacts,
57 not how the artifacts are stored on disk. It is the artifact format that
58 is intended to be enduring. The specifics of how artifacts are stored on
59 disk, though stable, is not intended to live as long as the
60 artifact format.
61
62 <a name="manifest"></a>
63 <h2>1.0 The Manifest</h2>
64
65 A manifest defines a check-in or version of the project
66 source tree. The manifest contains a list of artifacts for
67 each file in the project and the corresponding filenames, as
68 well as information such as parent check-ins, the name of the
69 programmer who created the check-in, the date and time when
70 the check-in was created, and any check-in comments associated
71 with the check-in.
72
 
73 Any artifact in the repository that follows the syntactic rules
74 of a manifest is a manifest. Note that a manifest can
75 be both a real manifest and also a content file, though this
76 is rare.
 
77
 
78 A manifest is a text file. Newline characters
79 (ASCII 0x0a) separate the file into "cards".
80 Each card begins with a single
81 character "card type". Zero or more arguments may follow
82 the card type. All arguments are separated from each other
83 and from the card-type character by a single space
84 character. There is no surplus white space between arguments
85 and no leading or trailing whitespace except for the newline
86 character that acts as the card separator.
 
87
 
88 All cards of the manifest occur in strict sorted lexicographical order.
89 No card may be duplicated.
90 The entire manifest may be PGP clear-signed, but otherwise it
91 may contain no additional text or data beyond what is described here.
 
92
 
93 Allowed cards in the manifest are as follows:
 
94
95 <blockquote>
96 <b>C</b> <i>checkin-comment</i><br>
97 <b>D</b> <i>time-and-date-stamp</i><br>
98 <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
@@ -113,11 +101,10 @@
101 <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br>
102 <b>U</b> <i>user-login</i><br>
103 <b>Z</b> <i>manifest-checksum</i>
104 </blockquote>
105
 
106 A manifest must have exactly one C-card. The sole argument to
107 the C-card is a check-in comment that describes the check-in that
108 the manifest defines. The check-in comment is text. The following
109 escape sequences are applied to the text:
110 A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A
@@ -124,24 +111,20 @@
111 newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash
112 (ASCII 0x5C) is represented as two backslashes "\\". Apart from
113 space and newline, no other whitespace characters are allowed in
114 the check-in comment. Nor are any unprintable characters allowed
115 in the comment.
 
116
 
117 A manifest must have exactly one D-card. The sole argument to
118 the D-card is a date-time stamp in the ISO8601 format. The
119 date and time should be in coordinated universal time (UTC).
120 The format is:
 
121
122 <blockquote>
123 <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>
124 </blockquote>
125
 
126 A manifest has zero or more F-cards. Each F-card defines a file
127 (other than the manifest itself) which is part of the check-in that
128 the manifest defines. There are two, three, or four arguments.
129 The first argument
130 is the pathname of the file in the check-in relative to the root
@@ -157,13 +140,11 @@
140 always readable and writable. This can be expressed by "w" permission
141 if desired but is optional.
142 The optional 4th argument is the name of the same file as it existed in
143 the parent check-in. If the name of the file is unchanged from its
144 parent, then the 4th argument is omitted.
 
145
 
146 A manifest has zero or one P-cards. Most manifests have one P-card.
147 The P-card has a varying number of arguments that
148 defines other manifests from which the current manifest
149 is derived. Each argument is an 40-character lowercase
150 hexadecimal SHA1 of the predecessor manifest. All arguments
@@ -171,13 +152,11 @@
152 The first predecessor is the direct ancestor of the manifest.
153 Other arguments define manifests with which the first was
154 merged to yield the current manifest. Most manifests have
155 a P-card with a single argument. The first manifest in the
156 project has no ancestors and thus has no P-card.
 
157
 
158 A manifest may optionally have a single R-card. The R-card has
159 a single argument which is the MD5 checksum of all files in
160 the check-in except the manifest itself. The checksum is expressed
161 as 32-characters of lowercase hexadecimal. The checksum is
162 computed as follows: For each file in the check-in (except for
@@ -185,13 +164,11 @@
164 take the pathname of the file relative to the root of the
165 repository, append a single space (ASCII 0x20), the
166 size of the file in ASCII decimal, a single newline
167 character (ASCII 0x0A), and the complete text of the file.
168 Compute the MD5 checksum of the the result.
 
169
 
170 A manifest might contain one or more T-cards used to set tags or
171 properties on the check-in. The format of the T-card is the same as
172 described in <i>Control Artifacts</i> section below, except that the
173 second argument is the single characcter "<b>*</b>" instead of an
174 artifact ID. The <b>*</b> in place of the artifact ID indicates that
@@ -199,42 +176,35 @@
176 possible to encode the current artifact ID as part of an artifact,
177 since the act of inserting the artifact ID would change the artifact ID,
178 hence a <b>*</b> is used to represent "self". T-cards are typically
179 added to manifests in order to set the <b>branch</b> property and a
180 symbolic name when the check-in is intended to start a new branch.
 
181
 
182 Each manifest has a single U-card. The argument to the U-card is
183 the login of the user who created the manifest. The login name
184 is encoded using the same character escapes as is used for the
185 check-in comment argument to the C-card.
 
186
 
187 A manifest has an option Z-card as its last line. The argument
188 to the Z-card is a 32-character lowercase hexadecimal MD5 hash
189 of all prior lines of the manifest up to and including the newline
190 character that immediately precedes the "Z". The Z-card is just
191 a sanity check to prove that the manifest is well-formed and
192 consistent.
 
193
194 A sample manifest from Fossil itself can be seen
195 [/artifact/28987096ac | here].
196
197 <a name="cluster"></a>
198 <h2>2.0 Clusters</h2>
199
 
200 A cluster is a artifact that declares the existence of other artifacts.
201 Clusters are used during repository synchronization to help
202 reduce network traffic. As such, clusters are an optimization and
203 may be removed from a repository without loss or damage to the
204 underlying project code.
 
205
 
206 Clusters follow a syntax that is very similar to manifests.
207 A Cluster is a line-oriented text file. Newline characters
208 (ASCII 0x0a) separate the artifact into cards. Each card begins with a single
209 character "card type". Zero or more arguments may follow
210 the card type. All arguments are separated from each other
@@ -245,67 +215,58 @@
215 All cards of a cluster occur in strict sorted lexicographical order.
216 No card may be duplicated.
217 The cluster may not contain additional text or data beyond
218 what is described here.
219 Unlike manifests, clusters are never PGP signed.
 
220
 
221 Allowed cards in the cluster are as follows:
 
222
223 <blockquote>
224 <b>M</b> <i>artifact-id</i><br />
225 <b>Z</b> <i>checksum</i>
226 </blockquote>
227
 
228 A cluster contains one or more "M" cards followed by a single "Z"
229 line. Each M card has a single argument which is the artifact ID of
230 another artifact in the repository. The Z card work exactly like
231 the Z card of a manifest. The argument to the Z card is the
232 lower-case hexadecimal representation of the MD5 checksum of all
233 prior cards in the cluster. Note that the Z card is required
234 on a cluster.
 
235
236 An example cluster from Fossil can be seen
237 [/artifact/d03dbdd73a2a8 | here].
238
239 <a name="ctrl"></a>
240 <h2>3.0 Control Artifacts</h2>
241
 
242 Control artifacts are used to assign properties to other artifacts
243 within the repository. The basic format of a control artifact is
244 the same as a manifest or cluster. A control artifact is a text
245 files divided into cards by newline characters. Each card has a
246 single-character card type followed by arguments. Spaces separate
247 the card type and the arguments. No surplus whitespace is allowed.
248 All cards must occur in strict lexigraphical order.
 
249
 
250 Allowed cards in a control artifact are as follows:
 
251
252 <blockquote>
253 <b>D</b> <i>time-and-date-stamp</i><br />
254 <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name artifact-id ?value?</i><br />
255 <b>U</b> <i>user-name</i><br />
256 <b>Z</b> <i>checksum</i><br />
257 </blockquote>
258
 
259 A control artifact must have one D card and one Z card and
260 one or more T cards. No other cards or other text is
261 allowed in a control artifact. Control artifacts might be PGP
262 clearsigned.
263
264 The D card and the Z card of a control artifact are the same
265 as in a manifest.
266
267 The T card represents a "tag" or property that is applied to
268 some other artifact. The T card has two or three values. The
269 second argument is the 40 character lowercase artifact ID of the artifact
270 to which the tag is to be applied. The
271 first value is the tag name. The first character of the tag
272 is either "+", "-", or "*". A "+" means the tag should be added
@@ -313,28 +274,37 @@
274 The "*" character means the tag should be added to the artifact
275 and all direct descendants (but not branches) of the artifact down
276 to but not including the first descendant that contains a
277 more recent "-" tag with the same name.
278 The optional third argument is the value of the tag. A tag
279 without a value is a boolean.
280
281 When two or more tags with the same name are applied to the
282 same artifact, the tag with the latest (most recent) date is
283 used.
284
285 Some tags have special meaning. The "comment" tag when applied
286 to a check-in will override the check-in comment of that check-in
287 for display purposes. The "user" tag overrides the name of the
288 check-in user. The "date" tag overrides the check-in date.
289 The "branch" tag sets the name of the branch that at check-in
290 belongs to. Symbolic tags begin with the "sym-" prefix.
291
292 The U card is the name of the user that created the control
293 artifact. The Z card is the usual artifact checksum.
294
295 An example control artifacts can be seen [/info/9d302ccda8 | here].
296
297
298 <a name="wikichng"></a>
299 <h2>4.0 Wiki Pages</h2>
300
301 A wiki page is an artifact with a format similar to manifests,
302 clusters, and control artifacts. The artifact is divided into
303 cards by newline characters. The format of each card is as in
304 manifests, clusters, and control artifacts. Wiki artifacts accept
305 the following card types:
306
307 <blockquote>
308 <b>D</b> <i>time-and-date-stamp</i><br />
309 <b>L</b> <i>wiki-title</i><br />
310 <b>P</b> <i>parent-artifact-id</i>+<br />
@@ -341,63 +311,245 @@
311 <b>U</b> <i>user-name</i><br />
312 <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br />
313 <b>Z</b> <i>checksum</i>
314 </blockquote>
315
316 The D card is the date and time when the wiki page was edited.
317 The P card specifies the parent wiki pages, if any. The L card
318 gives the name of the wiki page. The U card specifies the login
319 of the user who made this edit to the wiki page. The Z card is
320 the usual checksum over the either artifact.
321
322 The W card is used to specify the text of the wiki page. The
323 argument to the W card is an integer which is the number of bytes
324 of text in the wiki page. That text follows the newline character
325 that terminates the W card. The wiki text is always followed by one
326 extra newline.
327
328 An example wiki artifact can be seen
329 [/artifact/7b2f5fd0e0 | here].
330
331 <a name="tktchng"></a>
332 <h2>5.0 Ticket Changes</h2>
333
334 A ticket-change artifact represents a change to a trouble ticket.
335 The following cards are allowed on a ticket change artifact:
336
337 <blockquote>
338 <b>D</b> <i>time-and-date-stamp</i><br />
339 <b>J</b> ?<b>+</b>?<i>name</i> ?<i>value</i>?<br />
340 <b>K</b> <i>ticket-id</i><br />
341 <b>U</b> <i>user-name</i><br />
342 <b>Z</b> <i>checksum</i>
343 </blockquote>
344
 
345 The D card is the usual date and time stamp and represents the point
346 in time when the change was entered. The U card is the login of the
347 programmer who entered this change. The Z card is the checksum over
348 the entire artifact.
349
 
350 Every ticket has a unique ID. The ticket to which this change is applied
351 is specified by the K card. A ticket exists if it contains one or
352 more changes. The first "change" to a ticket is what brings the
353 ticket into existence.
354
 
355 J cards specify changes to the "value" of "fields" in the ticket.
356 If the <i>value</i> parameter of the J card is omitted, then the
357 field is set to an empty string.
358 Each fossil server has a ticket configuration which specifies the fields its
359 understands. The ticket configuration is part of the local state for
360 the repository and thus can vary from one repository to another.
361 Hence a J card might specify a <i>field</i> that do not exist in the
362 local ticket configuration. If a J card specifies a <i>field</i> that
363 is not in the local configuration, then that J card
364 is simply ignored.
365
 
366 The first argument of the J card is the field name. The second
367 value is the field value. If the field name begins with "+" then
368 the value is appended to the prior value. Otherwise, the value
369 on the J card replaces any previous value of the field.
370 The field name and value are both encoded using the character
371 escapes defined for the C card of a manifest.
372
373 An example ticket-change artifact can be seen
374 [/artifact/91f1ec6af053 | here].
375
376 <a name="attachment"></a>
377 <h2>6.0 Attachments</h2>
378
379 An attachment artifact associates some other artifact that is the
380 attachment (the source artifact) with a ticket or wiki page to which
381 the attachment is connected (the target artifact).
382 The following cards are allowed on an attachment artifact:
383
384 <blockquote>
385 <b>A</b> <i>filename target</i> ?<i>source</i>?
386 <b>C</b> <i>comment</i><br>
387 <b>D</b> <i>time-and-date-stamp</i><br />
388 <b>U</b> <i>user-name</i><br />
389 <b>Z</b> <i>checksum</i>
390 </blockquote>
391
392 The A card specifies a filename for the attachment in its first argument.
393 The second argument to the A card is the name
394 of the wiki page or ticket to which the attachment is connected. The
395 third argument is either missing or else it is the 40-character artifact
396 ID of the attachment itself. A missing third argument means that the
397 attachment should be deleted.
398
399 The C card is an optional comment describing what the attachment is about.
400 The C card is optional, but there can only be one.
401
402 A single D card is required to give the date and time when the attachment
403 was applied.
404
405 A single U card gives the name of the user to added the attachment.
406 If an attachment is added anonymously, then the U card may be omitted.
407
408 The Z card is the usual checksum over the rest of the attachment artifact.
409
410
411 <a name="summary"></a>
412 <h2>7.0 Card Summary</h2>
413
414 The following table summaries the various kinds of cards that
415 appear on Fossil artifacts:
416
417 <table border=1 width="100%">
418 <tr>
419 <th rowspan=2 valign=bottom>Card Format</th>
420 <th colspan=6>Used By</th>
421 </tr>
422 <tr>
423 <th>Manifest</th>
424 <th>Cluster</th>
425 <th>Control</th>
426 <th>Wiki</th>
427 <th>Ticket</th>
428 <th>Attachment</th>
429 </tr>
430 <tr>
431 <td><b>A</b> <i>filename target source</i></td>
432 <td>&nbsp;</td>
433 <td>&nbsp;</td>
434 <td>&nbsp;</td>
435 <td>&nbsp;</td>
436 <td>&nbsp;</td>
437 <td align=center><b>X</b></td>
438 </tr>
439 <tr>
440 <td><b>C</b> <i>coment-text</i></td>
441 <td align=center><b>X</b></td>
442 <td>&nbsp;</td>
443 <td>&nbsp;</td>
444 <td>&nbsp;</td>
445 <td>&nbsp;</td>
446 <td align=center><b>X</b></td>
447 </tr>
448 <tr>
449 <td><b>D</b> <i>date-time-stamp</i></td>
450 <td align=center><b>X</b></td>
451 <td align=center>&nbsp;</td>
452 <td align=center><b>X</b></td>
453 <td align=center><b>X</b></td>
454 <td align=center><b>X</b></td>
455 <td align=center><b>X</b></td>
456 </tr>
457 <tr>
458 <td><b>F</b> <i>filename uuid permissions oldname</i></td>
459 <td align=center><b>X</b></td>
460 <td align=center>&nbsp;</td>
461 <td align=center>&nbsp;</td>
462 <td align=center>&nbsp;</td>
463 <td align=center>&nbsp;</td>
464 <td align=center>&nbsp;</td>
465 </tr>
466 <tr>
467 <td><b>J</b> <i>name value</i></td>
468 <td align=center>&nbsp;</td>
469 <td align=center>&nbsp;</td>
470 <td align=center>&nbsp;</td>
471 <td align=center>&nbsp;</td>
472 <td align=center><b>X</b></td>
473 <td align=center>&nbsp;</td>
474 </tr>
475 <tr>
476 <td><b>K</b> <i>ticket-uuid</i></td>
477 <td align=center>&nbsp;</td>
478 <td align=center>&nbsp;</td>
479 <td align=center>&nbsp;</td>
480 <td align=center>&nbsp;</td>
481 <td align=center><b>X</b></td>
482 <td align=center>&nbsp;</td>
483 </tr>
484 <tr>
485 <td><b>L</b> <i>wiki-title</i></td>
486 <td align=center>&nbsp;</td>
487 <td align=center>&nbsp;</td>
488 <td align=center>&nbsp;</td>
489 <td align=center><b>X</b></td>
490 <td align=center>&nbsp;</td>
491 <td align=center>&nbsp;</td>
492 </tr>
493 <tr>
494 <td><b>M</b> <i>uuid</i></td>
495 <td align=center>&nbsp;</td>
496 <td align=center><b>X</b></td>
497 <td align=center>&nbsp;</td>
498 <td align=center>&nbsp;</td>
499 <td align=center>&nbsp;</td>
500 <td align=center>&nbsp;</td>
501 </tr>
502 <tr>
503 <td><b>P</b> <i>uuid ...</i></td>
504 <td align=center><b>X</b></td>
505 <td align=center>&nbsp;</td>
506 <td align=center>&nbsp;</td>
507 <td align=center><b>X</b></td>
508 <td align=center>&nbsp;</td>
509 <td align=center>&nbsp;</td>
510 </tr>
511 <tr>
512 <td><b>R</b> <i>md5sum</i></td>
513 <td align=center><b>X</b></td>
514 <td align=center>&nbsp;</td>
515 <td align=center>&nbsp;</td>
516 <td align=center>&nbsp;</td>
517 <td align=center>&nbsp;</td>
518 <td align=center>&nbsp;</td>
519 <tr>
520 <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname uuid value</i></td>
521 <td align=center><b>X</b></td>
522 <td align=center>&nbsp;</td>
523 <td align=center><b>X</b></td>
524 <td align=center>&nbsp;</td>
525 <td align=center>&nbsp;</td>
526 <td align=center>&nbsp;</td>
527 </tr>
528 <tr>
529 <td><b>U</b> <i>username</i></td>
530 <td align=center><b>X</b></td>
531 <td align=center>&nbsp;</td>
532 <td align=center><b>X</b></td>
533 <td align=center><b>X</b></td>
534 <td align=center><b>X</b></td>
535 <td align=center><b>X</b></td>
536 </tr>
537 <tr>
538 <td><b>W</b> <i>size</i></td>
539 <td align=center>&nbsp;</td>
540 <td align=center>&nbsp;</td>
541 <td align=center>&nbsp;</td>
542 <td align=center><b>X</b></td>
543 <td align=center>&nbsp;</td>
544 <td align=center>&nbsp;</td>
545 </tr>
546 <tr>
547 <td><b>Z</b> <i>md5sum</i></td>
548 <td align=center><b>X</b></td>
549 <td align=center><b>X</b></td>
550 <td align=center><b>X</b></td>
551 <td align=center><b>X</b></td>
552 <td align=center><b>X</b></td>
553 <td align=center><b>X</b></td>
554 </tr>
555 </table>
556
+63 -44
--- www/mkdownload.tcl
+++ www/mkdownload.tcl
@@ -27,54 +27,73 @@
2727
The historical source code is also available in the
2828
<a href="/fossil/doc/tip/www/selfhost.wiki">self-hosting
2929
Fossil repositories</a>.
3030
</p>
3131
32
-<table cellpadding="5">
33
-}
34
-
35
-proc Product {pattern desc} {
36
- set flist [glob -nocomplain download/$pattern]
37
- foreach file [lsort -dict $flist] {
38
- set file [file tail $file]
39
- if {![regexp -- {-([0-9]+)\.} $file all version]} continue
40
- set mtime [file mtime download/$file]
41
- set date [clock format $mtime -format {%Y-%m-%d %H:%M:%S UTC} -gmt 1]
42
- set size [file size download/$file]
43
- set units bytes
44
- if {$size>1024*1024} {
45
- set size [format %.2f [expr {$size/(1024.0*1024.0)}]]
46
- set units MiB
47
- } elseif {$size>1024} {
48
- set size [format %.2f [expr {$size/(1024.0)}]]
49
- set units KiB
50
- }
51
- puts "<tr><td width=\"10\"></td>"
52
- puts "<td valign=\"top\" align=\"right\">"
53
- puts "<a href=\"download/$file\">$file</a></td>"
54
- puts "<td width=\"5\"></td>"
55
- regsub -all VERSION $desc $version d2
56
- puts "<td valign=\"top\">[string trim $d2].<br>Size: $size $units.<br>"
57
- puts "Created: $date</td></tr>"
58
- }
59
-}
60
-
61
-Product fossil-linux-x86-*.zip {
62
- Prebuilt fossil binary version [VERSION] for Linux on x86
63
-}
64
-Product fossil-linux-amd64-*.zip {
65
- Prebuilt fossil binary version [VERSION] for Linux on amd64
66
-}
67
-Product fossil-macosx-x86-*.zip {
68
- Prebuilt fossil binary version [VERSION] for MacOSX on x86
69
-}
70
-Product fossil-w32-*.zip {
71
- Prebuilt fossil binary version [VERSION] for windows
72
-}
73
-Product fossil-src-*.tar.gz {
74
- Source code tarball for fossil version [VERSION]
75
-}
32
+<p>
33
+<u>Important Note:</u>
34
+After upgrading to a newer version of fossil, it is always a good idea
35
+to run:
36
+<blockquote><pre>
37
+<b><big><tt>fossil all rebuild</tt></big></b>
38
+</pre></blockquote>
39
+Running "rebuild" this way is not always necessary, but it never hurts.
40
+</p>
41
+
42
+<table cellpadding="10">
43
+}
44
+
45
+# Find all all unique timestamps.
46
+#
47
+foreach file [glob -nocomplain download/fossil-*.zip] {
48
+ if {[regexp {(\d+).zip$} $file all datetime]
49
+ && [string length $datetime]==14} {
50
+ set adate($datetime) 1
51
+ }
52
+}
53
+
54
+# Do all dates from newest to oldest
55
+#
56
+foreach datetime [lsort -decr [array names adate]] {
57
+ set dt [string range $datetime 0 3]-[string range $datetime 4 5]-
58
+ append dt "[string range $datetime 6 7] "
59
+ append dt "[string range $datetime 8 9]:[string range $datetime 10 11]:"
60
+ append dt "[string range $datetime 12 13]"
61
+ set link [string map {{ } +} $dt]
62
+ set hr http://www.fossil-scm.org/fossil/timeline?c=$link&y=ci
63
+ puts "<tr><td colspan=5 align=center><hr>"
64
+ puts "<b>Fossil snapshot as of <a href=\"$hr\">$dt</a><td width=30></b>"
65
+ puts "</td></tr>"
66
+
67
+ foreach {prefix suffix img desc} {
68
+ fossil-linux-x86 zip linux.gif {Linux x86}
69
+ fossil-linux-amd64 zip linux64.gif {Linux x86_64}
70
+ fossil-macosx-x86 zip mac.gif {Mac 10.5 x86}
71
+ fossil-w32 zip win32.gif {Windows}
72
+ fossil-src tar.gz src.gif {Source Tarball}
73
+ } {
74
+ set filename download/$prefix-$datetime.$suffix
75
+ if {[file exists $filename]} {
76
+ set size [file size $filename]
77
+ set units bytes
78
+ if {$size>1024*1024} {
79
+ set size [format %.2f [expr {$size/(1024.0*1024.0)}]]
80
+ set units MiB
81
+ } elseif {$size>1024} {
82
+ set size [format %.2f [expr {$size/(1024.0)}]]
83
+ set units KiB
84
+ }
85
+ puts "<td align=center valign=bottom><a href=\"$filename\">"
86
+ puts "<img src=\"build-icons/$img\" border=0><br>$desc</a><br>"
87
+ puts "$size $units</td>"
88
+ } else {
89
+ puts "<td>&nbsp;</td>"
90
+ }
91
+ }
92
+ puts "</tr>"
93
+}
94
+puts "<tr><td colspan=5><hr></td></tr>"
7695
7796
puts {</table>
7897
</body>
7998
</html>
8099
}
81100
--- www/mkdownload.tcl
+++ www/mkdownload.tcl
@@ -27,54 +27,73 @@
27 The historical source code is also available in the
28 <a href="/fossil/doc/tip/www/selfhost.wiki">self-hosting
29 Fossil repositories</a>.
30 </p>
31
32 <table cellpadding="5">
33 }
34
35 proc Product {pattern desc} {
36 set flist [glob -nocomplain download/$pattern]
37 foreach file [lsort -dict $flist] {
38 set file [file tail $file]
39 if {![regexp -- {-([0-9]+)\.} $file all version]} continue
40 set mtime [file mtime download/$file]
41 set date [clock format $mtime -format {%Y-%m-%d %H:%M:%S UTC} -gmt 1]
42 set size [file size download/$file]
43 set units bytes
44 if {$size>1024*1024} {
45 set size [format %.2f [expr {$size/(1024.0*1024.0)}]]
46 set units MiB
47 } elseif {$size>1024} {
48 set size [format %.2f [expr {$size/(1024.0)}]]
49 set units KiB
50 }
51 puts "<tr><td width=\"10\"></td>"
52 puts "<td valign=\"top\" align=\"right\">"
53 puts "<a href=\"download/$file\">$file</a></td>"
54 puts "<td width=\"5\"></td>"
55 regsub -all VERSION $desc $version d2
56 puts "<td valign=\"top\">[string trim $d2].<br>Size: $size $units.<br>"
57 puts "Created: $date</td></tr>"
58 }
59 }
60
61 Product fossil-linux-x86-*.zip {
62 Prebuilt fossil binary version [VERSION] for Linux on x86
63 }
64 Product fossil-linux-amd64-*.zip {
65 Prebuilt fossil binary version [VERSION] for Linux on amd64
66 }
67 Product fossil-macosx-x86-*.zip {
68 Prebuilt fossil binary version [VERSION] for MacOSX on x86
69 }
70 Product fossil-w32-*.zip {
71 Prebuilt fossil binary version [VERSION] for windows
72 }
73 Product fossil-src-*.tar.gz {
74 Source code tarball for fossil version [VERSION]
75 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
77 puts {</table>
78 </body>
79 </html>
80 }
81
--- www/mkdownload.tcl
+++ www/mkdownload.tcl
@@ -27,54 +27,73 @@
27 The historical source code is also available in the
28 <a href="/fossil/doc/tip/www/selfhost.wiki">self-hosting
29 Fossil repositories</a>.
30 </p>
31
32 <p>
33 <u>Important Note:</u>
34 After upgrading to a newer version of fossil, it is always a good idea
35 to run:
36 <blockquote><pre>
37 <b><big><tt>fossil all rebuild</tt></big></b>
38 </pre></blockquote>
39 Running "rebuild" this way is not always necessary, but it never hurts.
40 </p>
41
42 <table cellpadding="10">
43 }
44
45 # Find all all unique timestamps.
46 #
47 foreach file [glob -nocomplain download/fossil-*.zip] {
48 if {[regexp {(\d+).zip$} $file all datetime]
49 && [string length $datetime]==14} {
50 set adate($datetime) 1
51 }
52 }
53
54 # Do all dates from newest to oldest
55 #
56 foreach datetime [lsort -decr [array names adate]] {
57 set dt [string range $datetime 0 3]-[string range $datetime 4 5]-
58 append dt "[string range $datetime 6 7] "
59 append dt "[string range $datetime 8 9]:[string range $datetime 10 11]:"
60 append dt "[string range $datetime 12 13]"
61 set link [string map {{ } +} $dt]
62 set hr http://www.fossil-scm.org/fossil/timeline?c=$link&y=ci
63 puts "<tr><td colspan=5 align=center><hr>"
64 puts "<b>Fossil snapshot as of <a href=\"$hr\">$dt</a><td width=30></b>"
65 puts "</td></tr>"
66
67 foreach {prefix suffix img desc} {
68 fossil-linux-x86 zip linux.gif {Linux x86}
69 fossil-linux-amd64 zip linux64.gif {Linux x86_64}
70 fossil-macosx-x86 zip mac.gif {Mac 10.5 x86}
71 fossil-w32 zip win32.gif {Windows}
72 fossil-src tar.gz src.gif {Source Tarball}
73 } {
74 set filename download/$prefix-$datetime.$suffix
75 if {[file exists $filename]} {
76 set size [file size $filename]
77 set units bytes
78 if {$size>1024*1024} {
79 set size [format %.2f [expr {$size/(1024.0*1024.0)}]]
80 set units MiB
81 } elseif {$size>1024} {
82 set size [format %.2f [expr {$size/(1024.0)}]]
83 set units KiB
84 }
85 puts "<td align=center valign=bottom><a href=\"$filename\">"
86 puts "<img src=\"build-icons/$img\" border=0><br>$desc</a><br>"
87 puts "$size $units</td>"
88 } else {
89 puts "<td>&nbsp;</td>"
90 }
91 }
92 puts "</tr>"
93 }
94 puts "<tr><td colspan=5><hr></td></tr>"
95
96 puts {</table>
97 </body>
98 </html>
99 }
100
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -67,19 +67,20 @@
6767
6868
<blockquote>
6969
<b>fossil ui </b><i> repository-filename</i>
7070
</blockquote>
7171
72
- <p>This starts a webserver listening on port 8080. You can
73
- specify a different port using the <b>-port</b> option on the command-line.
74
- After the server is running, fossil will then attempt to launch your
75
- web browser and make it point to this web server. If your system
72
+ <p>This starts a web server then automatically launches your
73
+ web browser and makes it point to this web server. If your system
7674
has an unusual configuration, fossil might not be able to figure out
77
- how to start your web browser. In that case, start the web browser
78
- yourself and point it at http://localhost:8080/. Click on the
79
- "Admin" link on the menu bar to start configuring your repository.</p>
80
-
75
+ how to start your web browser. In that case, first tell fossil
76
+ where to find your web browser using a command like this:</p>
77
+
78
+ <blockquote>
79
+ <b>fossil setting web-browser </b><i> path-to-web-browser</i>
80
+ </blockquote>
81
+
8182
<p>By default, fossil does not require a login for HTTP connections
8283
coming in from the IP loopback address 127.0.0.1. You can, and perhaps
8384
should, change this after you create a few users.</p>
8485
8586
<p>When you are finished configuring, just press Control-C or use
8687
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -67,19 +67,20 @@
67
68 <blockquote>
69 <b>fossil ui </b><i> repository-filename</i>
70 </blockquote>
71
72 <p>This starts a webserver listening on port 8080. You can
73 specify a different port using the <b>-port</b> option on the command-line.
74 After the server is running, fossil will then attempt to launch your
75 web browser and make it point to this web server. If your system
76 has an unusual configuration, fossil might not be able to figure out
77 how to start your web browser. In that case, start the web browser
78 yourself and point it at http://localhost:8080/. Click on the
79 "Admin" link on the menu bar to start configuring your repository.</p>
80
 
 
 
81 <p>By default, fossil does not require a login for HTTP connections
82 coming in from the IP loopback address 127.0.0.1. You can, and perhaps
83 should, change this after you create a few users.</p>
84
85 <p>When you are finished configuring, just press Control-C or use
86
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -67,19 +67,20 @@
67
68 <blockquote>
69 <b>fossil ui </b><i> repository-filename</i>
70 </blockquote>
71
72 <p>This starts a web server then automatically launches your
73 web browser and makes it point to this web server. If your system
 
 
74 has an unusual configuration, fossil might not be able to figure out
75 how to start your web browser. In that case, first tell fossil
76 where to find your web browser using a command like this:</p>
77
78 <blockquote>
79 <b>fossil setting web-browser </b><i> path-to-web-browser</i>
80 </blockquote>
81
82 <p>By default, fossil does not require a login for HTTP connections
83 coming in from the IP loopback address 127.0.0.1. You can, and perhaps
84 should, change this after you create a few users.</p>
85
86 <p>When you are finished configuring, just press Control-C or use
87
--- www/webui.wiki
+++ www/webui.wiki
@@ -148,18 +148,5 @@
148148
</verbatim>
149149
150150
As always, you'll want to adjust the pathnames to whatever is appropriate
151151
for your system. The xinetd setup uses a different syntax but follows
152152
the same idea.
153
-
154
-Once you have your new repository running on the network server, delete
155
-the original repository from your local machine, then clone the repository
156
-off of the server:
157
-
158
- <b>fossil clone http://user:[email protected]/cgi-bin/my-project</b>
159
-
160
-(As always, adjust the URL as appropriate for your installation.)
161
-After copying a repository, it is important to reclone it onto new machines.
162
-Each repository has a random "repository ID" and repositories will not
163
-sync with another repository having the same ID (to avoid sync loops).
164
-Cloning the repository will give you a new repository ID in your local
165
-copy and allow you to sync with the server.
166153
--- www/webui.wiki
+++ www/webui.wiki
@@ -148,18 +148,5 @@
148 </verbatim>
149
150 As always, you'll want to adjust the pathnames to whatever is appropriate
151 for your system. The xinetd setup uses a different syntax but follows
152 the same idea.
153
154 Once you have your new repository running on the network server, delete
155 the original repository from your local machine, then clone the repository
156 off of the server:
157
158 <b>fossil clone http://user:[email protected]/cgi-bin/my-project</b>
159
160 (As always, adjust the URL as appropriate for your installation.)
161 After copying a repository, it is important to reclone it onto new machines.
162 Each repository has a random "repository ID" and repositories will not
163 sync with another repository having the same ID (to avoid sync loops).
164 Cloning the repository will give you a new repository ID in your local
165 copy and allow you to sync with the server.
166
--- www/webui.wiki
+++ www/webui.wiki
@@ -148,18 +148,5 @@
148 </verbatim>
149
150 As always, you'll want to adjust the pathnames to whatever is appropriate
151 for your system. The xinetd setup uses a different syntax but follows
152 the same idea.
 
 
 
 
 
 
 
 
 
 
 
 
 
153
--- www/wikitheory.wiki
+++ www/wikitheory.wiki
@@ -8,11 +8,11 @@
88
* [./embeddeddoc.wiki | Embedded documentation] files whose
99
name ends in "wiki".
1010
1111
The [/wiki_rules | formatting rules] for fossil wiki
1212
are designed to be simple and intuitive. The idea is that wiki provides
13
-paragaph breaks, numbered and bulletted lists, and hyperlinking for
13
+paragraph breaks, numbered and bulletted lists, and hyperlinking for
1414
simple documents together with a safe subset of HTML for more complex
1515
formatting tasks.
1616
1717
Some commentators feel that the use of HTML is a mistake and that
1818
fossil should use the markup language of the
1919
--- www/wikitheory.wiki
+++ www/wikitheory.wiki
@@ -8,11 +8,11 @@
8 * [./embeddeddoc.wiki | Embedded documentation] files whose
9 name ends in "wiki".
10
11 The [/wiki_rules | formatting rules] for fossil wiki
12 are designed to be simple and intuitive. The idea is that wiki provides
13 paragaph breaks, numbered and bulletted lists, and hyperlinking for
14 simple documents together with a safe subset of HTML for more complex
15 formatting tasks.
16
17 Some commentators feel that the use of HTML is a mistake and that
18 fossil should use the markup language of the
19
--- www/wikitheory.wiki
+++ www/wikitheory.wiki
@@ -8,11 +8,11 @@
8 * [./embeddeddoc.wiki | Embedded documentation] files whose
9 name ends in "wiki".
10
11 The [/wiki_rules | formatting rules] for fossil wiki
12 are designed to be simple and intuitive. The idea is that wiki provides
13 paragraph breaks, numbered and bulletted lists, and hyperlinking for
14 simple documents together with a safe subset of HTML for more complex
15 formatting tasks.
16
17 Some commentators feel that the use of HTML is a mistake and that
18 fossil should use the markup language of the
19

Keyboard Shortcuts

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