Fossil SCM

The control file parser now reads ticket changes and wiki pages.

drh 2007-10-05 13:03 trunk
Commit 2ab2db0bd3be4d7e16bf53f2e6c1386d3961d86e
+2 -7
--- ideas.txt
+++ ideas.txt
@@ -90,17 +90,12 @@
9090
9191
A uuid filename description
9292
D datetime
9393
P uuid ...
9494
U user
95
- W wikipagename uuid
96
- Z md5sum
97
-
98
-On page:
99
-
100
- <title>....</title>
101
- <readonly/>
95
+ W size \n text \n
96
+ Z cksum
10297
10398
Hyperlinks:
10499
105100
[lowercasehex] /info/lowercasehex
106101
[attachment.gif] inline image
107102
--- ideas.txt
+++ ideas.txt
@@ -90,17 +90,12 @@
90
91 A uuid filename description
92 D datetime
93 P uuid ...
94 U user
95 W wikipagename uuid
96 Z md5sum
97
98 On page:
99
100 <title>....</title>
101 <readonly/>
102
103 Hyperlinks:
104
105 [lowercasehex] /info/lowercasehex
106 [attachment.gif] inline image
107
--- ideas.txt
+++ ideas.txt
@@ -90,17 +90,12 @@
90
91 A uuid filename description
92 D datetime
93 P uuid ...
94 U user
95 W size \n text \n
96 Z cksum
 
 
 
 
 
97
98 Hyperlinks:
99
100 [lowercasehex] /info/lowercasehex
101 [attachment.gif] inline image
102
+296 -45
--- src/manifest.c
+++ src/manifest.c
@@ -22,11 +22,11 @@
2222
*******************************************************************************
2323
**
2424
** This file contains code used to cross link control files and
2525
** manifests. The file is named "manifest.c" because it was
2626
** original only used to parse manifests. Then later clusters
27
-** and control files were added.
27
+** and control files and wiki pages and tickets were added.
2828
*/
2929
#include "config.h"
3030
#include "manifest.h"
3131
#include <assert.h>
3232
@@ -35,22 +35,35 @@
3535
** Types of control files
3636
*/
3737
#define CFTYPE_MANIFEST 1
3838
#define CFTYPE_CLUSTER 2
3939
#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
4049
4150
/*
4251
** A parsed manifest or cluster.
4352
*/
4453
struct Manifest {
4554
Blob content; /* The original content blob */
4655
int type; /* Type of file */
56
+ int mode; /* Access mode */
4757
char *zComment; /* Decoded comment */
4858
char zUuid[UUID_SIZE+1]; /* Self UUID */
4959
double rDate; /* Time in the "D" line */
5060
char *zUser; /* Name of the user */
5161
char *zRepoCksum; /* MD5 checksum of the baseline content */
62
+ char *zWiki; /* Text of the wiki page */
63
+ char *zWikiTitle; /* Name of the wiki page */
64
+ char *zTicketUuid; /* UUID for a ticket */
5265
int nFile; /* Number of F lines */
5366
int nFileAlloc; /* Slots allocated in aFile[] */
5467
struct {
5568
char *zName; /* Name of a file */
5669
char *zUuid; /* UUID of the file */
@@ -66,10 +79,23 @@
6679
struct {
6780
char *zName; /* Name of the tag */
6881
char *zUuid; /* UUID that the tag is applied to */
6982
char *zValue; /* Value if the tag is really a property */
7083
} *aTag;
84
+ int nField; /* Number of J lines */
85
+ int nFieldAlloc; /* Slots allocated in aField[] */
86
+ struct {
87
+ char *zName; /* Key or field name */
88
+ char *zValue; /* Value of the field */
89
+ } *aField;
90
+ int nAttach; /* Number of A lines */
91
+ int nAttachAlloc; /* Slots allocated in aAttach[] */
92
+ struct {
93
+ char *zUuid; /* UUID of the attachment */
94
+ char *zName; /* Name of the attachment */
95
+ char *zDesc; /* Description of the attachment */
96
+ } *aAttach;
7197
};
7298
#endif
7399
74100
75101
/*
@@ -78,29 +104,46 @@
78104
void manifest_clear(Manifest *p){
79105
blob_reset(&p->content);
80106
free(p->aFile);
81107
free(p->azParent);
82108
free(p->azCChild);
109
+ free(p->aTag);
110
+ free(p->aField);
111
+ free(p->aAttach);
83112
memset(p, 0, sizeof(*p));
84113
}
85114
86115
/*
87
-** Parse a manifest blob into a Manifest object. The Manifest
88
-** object takes over the input blob and will free it when the
116
+** Parse a blob into a Manifest object. The Manifest object
117
+** takes over the input blob and will free it when the
89118
** Manifest object is freed. Zeros are inserted into the blob
90119
** as string terminators so that blob should not be used again.
91120
**
92
-** Return TRUE if the content really is a manifest. Return FALSE
93
-** if there are syntax errors.
121
+** Return TRUE if the content really is a control file of some
122
+** kind. Return FALSE if there are syntax errors.
123
+**
124
+** This routine is strict about the format of a control file.
125
+** The format must match exactly or else it is rejected. This
126
+** rule minimizes the risk that a content file will be mistaken
127
+** for a control file simply because they look the same.
94128
**
95129
** The pContent is reset. If TRUE is returned, then pContent will
96130
** be reset when the Manifest object is cleared. If FALSE is
97131
** returned then the Manifest object is cleared automatically
98132
** and pContent is reset before the return.
133
+**
134
+** The entire file can be PGP clear-signed. The signature is ignored.
135
+** The file consists of zero or more cards, one card per line.
136
+** (Except: the content of the W card can extend of multiple lines.)
137
+** Each card is divided into tokens by a single space character.
138
+** The first token is a single upper-case letter which is the card type.
139
+** The card type determines the other parameters to the card.
140
+** Cards must occur in lexicographical order.
99141
*/
100142
int manifest_parse(Manifest *p, Blob *pContent){
101143
int seenHeader = 0;
144
+ int seenZ = 0;
102145
int i, lineNo=0;
103146
Blob line, token, a1, a2, a3;
104147
Blob selfuuid;
105148
char cPrevType = 0;
106149
@@ -135,10 +178,48 @@
135178
}
136179
cPrevType = z[0];
137180
seenHeader = 1;
138181
if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
139182
switch( z[0] ){
183
+ /*
184
+ ** A <uuid> <filename> <description>
185
+ **
186
+ ** Identifies an attachment to either a wiki page or a ticket.
187
+ ** <uuid> is the artifact that is the attachment.
188
+ */
189
+ case 'A': {
190
+ char *zName, *zUuid, *zDesc;
191
+ md5sum_step_text(blob_buffer(&line), blob_size(&line));
192
+ if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
193
+ if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
194
+ if( blob_token(&line, &a3)==0 ) goto manifest_syntax_error;
195
+ zUuid = blob_terminate(&a1);
196
+ zName = blob_terminate(&a2);
197
+ zDesc = blob_terminate(&a3);
198
+ if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
199
+ if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
200
+ defossilize(zName);
201
+ if( !file_is_simple_pathname(zName) ){
202
+ goto manifest_syntax_error;
203
+ }
204
+ defossilize(zDesc);
205
+ if( p->nAttach>=p->nAttachAlloc ){
206
+ p->nAttachAlloc = p->nAttachAlloc*2 + 10;
207
+ p->aAttach = realloc(p->aAttach,
208
+ p->nAttachAlloc*sizeof(p->aAttach[0]) );
209
+ if( p->aAttach==0 ) fossil_panic("out of memory");
210
+ }
211
+ i = p->nAttach++;
212
+ p->aAttach[i].zUuid = zUuid;
213
+ p->aAttach[i].zName = zName;
214
+ p->aAttach[i].zDesc = zDesc;
215
+ if( i>0 && strcmp(p->aAttach[i-1].zUuid, zUuid)>=0 ){
216
+ goto manifest_syntax_error;
217
+ }
218
+ break;
219
+ }
220
+
140221
/*
141222
** C <comment>
142223
**
143224
** Comment text is fossil-encoded. There may be no more than
144225
** one C line. C lines are required for manifests and are
@@ -169,10 +250,33 @@
169250
if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
170251
zDate = blob_terminate(&a1);
171252
p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
172253
break;
173254
}
255
+
256
+ /*
257
+ ** E <mode>
258
+ **
259
+ ** Access mode. <mode> can be one of "read", "append",
260
+ ** or "write".
261
+ */
262
+ case 'E': {
263
+ md5sum_step_text(blob_buffer(&line), blob_size(&line));
264
+ if( p->mode!=0 ) goto manifest_syntax_error;
265
+ if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
266
+ if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
267
+ if( blob_eq(&a1, "write") ){
268
+ p->mode = CFMODE_WRITE;
269
+ }else if( blob_eq(&a1, "append") ){
270
+ p->mode = CFMODE_APPEND;
271
+ }else if( blob_eq(&a1, "read") ){
272
+ p->mode = CFMODE_READ;
273
+ }else{
274
+ goto manifest_syntax_error;
275
+ }
276
+ break;
277
+ }
174278
175279
/*
176280
** F <filename> <uuid>
177281
**
178282
** Identifies a file in a manifest. Multiple F lines are
@@ -204,10 +308,74 @@
204308
if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
205309
goto manifest_syntax_error;
206310
}
207311
break;
208312
}
313
+
314
+ /*
315
+ ** J <name> <value>
316
+ **
317
+ ** Specifies a name value pair for ticket.
318
+ */
319
+ case 'J': {
320
+ char *zName, *zValue;
321
+ md5sum_step_text(blob_buffer(&line), blob_size(&line));
322
+ if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
323
+ if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
324
+ if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
325
+ zName = blob_terminate(&a1);
326
+ zValue = blob_terminate(&a2);
327
+ defossilize(zValue);
328
+ if( p->nField>=p->nFieldAlloc ){
329
+ p->nFieldAlloc = p->nFieldAlloc*2 + 10;
330
+ p->aField = realloc(p->aField,
331
+ p->nFieldAlloc*sizeof(p->aField[0]) );
332
+ if( p->aField==0 ) fossil_panic("out of memory");
333
+ }
334
+ i = p->nField++;
335
+ p->aField[i].zName = zName;
336
+ p->aField[i].zValue = zValue;
337
+ if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){
338
+ goto manifest_syntax_error;
339
+ }
340
+ break;
341
+ }
342
+
343
+
344
+ /*
345
+ ** K <uuid>
346
+ **
347
+ ** A K-line gives the UUID for the ticket which this control file
348
+ ** is amending.
349
+ */
350
+ case 'K': {
351
+ char *zUuid;
352
+ md5sum_step_text(blob_buffer(&line), blob_size(&line));
353
+ if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
354
+ zUuid = blob_terminate(&a1);
355
+ if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
356
+ if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
357
+ if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
358
+ p->zTicketUuid = zUuid;
359
+ break;
360
+ }
361
+
362
+ /*
363
+ ** L <wikitite>
364
+ **
365
+ ** The wiki page title is fossil-encoded. There may be no more than
366
+ ** one L line.
367
+ */
368
+ case 'L': {
369
+ md5sum_step_text(blob_buffer(&line), blob_size(&line));
370
+ if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
371
+ if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
372
+ if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
373
+ p->zWikiTitle = blob_terminate(&a1);
374
+ defossilize(p->zWikiTitle);
375
+ break;
376
+ }
209377
210378
/*
211379
** M <uuid>
212380
**
213381
** An M-line identifies another artifact by its UUID. M-lines
@@ -231,10 +399,52 @@
231399
if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){
232400
goto manifest_syntax_error;
233401
}
234402
break;
235403
}
404
+
405
+ /*
406
+ ** P <uuid> ...
407
+ **
408
+ ** Specify one or more other artifacts where are the parents of
409
+ ** this artifact. The first parent is the primary parent. All
410
+ ** others are parents by merge.
411
+ */
412
+ case 'P': {
413
+ md5sum_step_text(blob_buffer(&line), blob_size(&line));
414
+ while( blob_token(&line, &a1) ){
415
+ char *zUuid;
416
+ if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
417
+ zUuid = blob_terminate(&a1);
418
+ if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
419
+ if( p->nParent>=p->nParentAlloc ){
420
+ p->nParentAlloc = p->nParentAlloc*2 + 5;
421
+ p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));
422
+ if( p->azParent==0 ) fossil_panic("out of memory");
423
+ }
424
+ i = p->nParent++;
425
+ p->azParent[i] = zUuid;
426
+ }
427
+ break;
428
+ }
429
+
430
+ /*
431
+ ** R <md5sum>
432
+ **
433
+ ** Specify the MD5 checksum of the entire baseline in a
434
+ ** manifest.
435
+ */
436
+ case 'R': {
437
+ md5sum_step_text(blob_buffer(&line), blob_size(&line));
438
+ if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
439
+ if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
440
+ if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
441
+ if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
442
+ p->zRepoCksum = blob_terminate(&a1);
443
+ if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
444
+ break;
445
+ }
236446
237447
/*
238448
** T (+|*|-)<tagname> <uuid> ?<value>?
239449
**
240450
** Create or cancel a tag or property. The tagname is fossil-encoded.
@@ -311,57 +521,46 @@
311521
defossilize(p->zUser);
312522
break;
313523
}
314524
315525
/*
316
- ** R <md5sum>
317
- **
318
- ** Specify the MD5 checksum of the entire baseline in a
319
- ** manifest.
320
- */
321
- case 'R': {
322
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
323
- if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
324
- if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
325
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
326
- if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
327
- p->zRepoCksum = blob_terminate(&a1);
328
- if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
329
- break;
330
- }
331
-
332
- /*
333
- ** P <uuid> ...
334
- **
335
- ** Specify one or more other artifacts where are the parents of
336
- ** this artifact. The first parent is the primary parent. All
337
- ** others are parents by merge.
338
- */
339
- case 'P': {
340
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
341
- while( blob_token(&line, &a1) ){
342
- char *zUuid;
343
- if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
344
- zUuid = blob_terminate(&a1);
345
- if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
346
- if( p->nParent>=p->nParentAlloc ){
347
- p->nParentAlloc = p->nParentAlloc*2 + 5;
348
- p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));
349
- if( p->azParent==0 ) fossil_panic("out of memory");
350
- }
351
- i = p->nParent++;
352
- p->azParent[i] = zUuid;
353
- }
354
- break;
355
- }
526
+ ** W <size>
527
+ **
528
+ ** The next <size> bytes of the file contain the text of the wiki
529
+ ** page. There is always an extra \n before the start of the next
530
+ ** record.
531
+ */
532
+ case 'W': {
533
+ int size;
534
+ Blob wiki;
535
+ md5sum_step_text(blob_buffer(&line), blob_size(&line));
536
+ if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
537
+ if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
538
+ if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error;
539
+ if( size<0 ) goto manifest_syntax_error;
540
+ if( p->zWiki!=0 ) goto manifest_syntax_error;
541
+ blob_zero(&wiki);
542
+ if( blob_extract(pContent, size+1, &wiki)!=size+1 ){
543
+ goto manifest_syntax_error;
544
+ }
545
+ p->zWiki = blob_buffer(&wiki);
546
+ if( p->zWiki[size]!='\n' ) goto manifest_syntax_error;
547
+ p->zWiki[size] = 0;
548
+ break;
549
+ }
550
+
356551
357552
/*
358553
** Z <md5sum>
359554
**
360555
** MD5 checksum on this control file. The checksum is over all
361556
** lines (other than PGP-signature lines) prior to the current
362557
** line. This must be the last record.
558
+ **
559
+ ** This card is required for all control file types except for
560
+ ** Manifest. It is not required for manifest only for historical
561
+ ** compatibility reasons.
363562
*/
364563
case 'Z': {
365564
int rc;
366565
Blob hash;
367566
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
@@ -370,10 +569,11 @@
370569
if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
371570
md5sum_finish(&hash);
372571
rc = blob_compare(&hash, &a1);
373572
blob_reset(&hash);
374573
if( rc!=0 ) goto manifest_syntax_error;
574
+ seenZ = 1;
375575
break;
376576
}
377577
default: {
378578
goto manifest_syntax_error;
379579
}
@@ -382,23 +582,62 @@
382582
if( !seenHeader ) goto manifest_syntax_error;
383583
384584
if( p->nFile>0 ){
385585
if( p->nCChild>0 ) goto manifest_syntax_error;
386586
if( p->rDate==0.0 ) goto manifest_syntax_error;
587
+ if( p->nField>0 ) goto manifest_syntax_error;
588
+ if( p->zTicketUuid ) goto manifest_syntax_error;
589
+ if( p->nAttach>0 ) goto manifest_syntax_error;
590
+ if( p->zWiki ) goto manifest_syntax_error;
591
+ if( p->zWikiTitle ) goto manifest_syntax_error;
592
+ if( p->zTicketUuid ) goto manifest_syntax_error;
387593
p->type = CFTYPE_MANIFEST;
388594
}else if( p->nCChild>0 ){
389595
if( p->rDate>0.0 ) goto manifest_syntax_error;
390596
if( p->zComment!=0 ) goto manifest_syntax_error;
391597
if( p->zUser!=0 ) goto manifest_syntax_error;
392598
if( p->nTag>0 ) goto manifest_syntax_error;
393599
if( p->nParent>0 ) goto manifest_syntax_error;
394600
if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
601
+ if( p->nField>0 ) goto manifest_syntax_error;
602
+ if( p->zTicketUuid ) goto manifest_syntax_error;
603
+ if( p->nAttach>0 ) goto manifest_syntax_error;
604
+ if( p->zWiki ) goto manifest_syntax_error;
605
+ if( p->zWikiTitle ) goto manifest_syntax_error;
606
+ if( !seenZ ) goto manifest_syntax_error;
395607
p->type = CFTYPE_CLUSTER;
608
+ }else if( p->nField>0 ){
609
+ if( p->rDate==0.0 ) goto manifest_syntax_error;
610
+ if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
611
+ if( p->zWiki ) goto manifest_syntax_error;
612
+ if( p->zWikiTitle ) goto manifest_syntax_error;
613
+ if( p->nCChild>0 ) goto manifest_syntax_error;
614
+ if( p->nTag>0 ) goto manifest_syntax_error;
615
+ if( p->zTicketUuid==0 ) goto manifest_syntax_error;
616
+ if( p->zUser==0 ) goto manifest_syntax_error;
617
+ if( !seenZ ) goto manifest_syntax_error;
618
+ p->type = CFTYPE_TICKET;
619
+ }else if( p->zWiki!=0 ){
620
+ if( p->rDate==0.0 ) goto manifest_syntax_error;
621
+ if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
622
+ if( p->nCChild>0 ) goto manifest_syntax_error;
623
+ if( p->nTag>0 ) goto manifest_syntax_error;
624
+ if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
625
+ if( p->zUser==0 ) goto manifest_syntax_error;
626
+ if( p->zWikiTitle==0 ) goto manifest_syntax_error;
627
+ if( !seenZ ) goto manifest_syntax_error;
628
+ p->type = CFTYPE_WIKI;
396629
}else if( p->nTag>0 ){
397630
if( p->rDate<=0.0 ) goto manifest_syntax_error;
398631
if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
399632
if( p->nParent>0 ) goto manifest_syntax_error;
633
+ if( p->nAttach>0 ) goto manifest_syntax_error;
634
+ if( p->nField>0 ) goto manifest_syntax_error;
635
+ if( p->zWiki ) goto manifest_syntax_error;
636
+ if( p->zWikiTitle ) goto manifest_syntax_error;
637
+ if( p->zTicketUuid ) goto manifest_syntax_error;
638
+ if( !seenZ ) goto manifest_syntax_error;
400639
p->type = CFTYPE_CONTROL;
401640
}else{
402641
goto manifest_syntax_error;
403642
}
404643
@@ -582,10 +821,22 @@
582821
rid, m.rDate, tid);
583822
}
584823
if( parentid ){
585824
tag_propagate_all(parentid);
586825
}
826
+ }
827
+ if( m.type==CFTYPE_WIKI ){
828
+ char *zTag = mprintf("wiki-%s", m.zWikiTitle);
829
+ int tagid = tag_findid(zTag, 1);
830
+ int prior;
831
+ tag_insert(zTag, 1, 0, rid, m.rDate, rid);
832
+ free(zTag);
833
+ prior = db_int(0, "SELECT rid FROM tagxref WHERE tagid=%d"
834
+ " ORDER BY mtime DESC LIMIT 1 OFFSET 1", tagid);
835
+ if( prior ){
836
+ content_deltify(prior, rid, 0);
837
+ }
587838
}
588839
db_end_transaction(0);
589840
manifest_clear(&m);
590841
return 1;
591842
}
592843
--- src/manifest.c
+++ src/manifest.c
@@ -22,11 +22,11 @@
22 *******************************************************************************
23 **
24 ** This file contains code used to cross link control files and
25 ** manifests. The file is named "manifest.c" because it was
26 ** original only used to parse manifests. Then later clusters
27 ** and control files were added.
28 */
29 #include "config.h"
30 #include "manifest.h"
31 #include <assert.h>
32
@@ -35,22 +35,35 @@
35 ** Types of control files
36 */
37 #define CFTYPE_MANIFEST 1
38 #define CFTYPE_CLUSTER 2
39 #define CFTYPE_CONTROL 3
 
 
 
 
 
 
 
 
 
40
41 /*
42 ** A parsed manifest or cluster.
43 */
44 struct Manifest {
45 Blob content; /* The original content blob */
46 int type; /* Type of file */
 
47 char *zComment; /* Decoded comment */
48 char zUuid[UUID_SIZE+1]; /* Self UUID */
49 double rDate; /* Time in the "D" line */
50 char *zUser; /* Name of the user */
51 char *zRepoCksum; /* MD5 checksum of the baseline content */
 
 
 
52 int nFile; /* Number of F lines */
53 int nFileAlloc; /* Slots allocated in aFile[] */
54 struct {
55 char *zName; /* Name of a file */
56 char *zUuid; /* UUID of the file */
@@ -66,10 +79,23 @@
66 struct {
67 char *zName; /* Name of the tag */
68 char *zUuid; /* UUID that the tag is applied to */
69 char *zValue; /* Value if the tag is really a property */
70 } *aTag;
 
 
 
 
 
 
 
 
 
 
 
 
 
71 };
72 #endif
73
74
75 /*
@@ -78,29 +104,46 @@
78 void manifest_clear(Manifest *p){
79 blob_reset(&p->content);
80 free(p->aFile);
81 free(p->azParent);
82 free(p->azCChild);
 
 
 
83 memset(p, 0, sizeof(*p));
84 }
85
86 /*
87 ** Parse a manifest blob into a Manifest object. The Manifest
88 ** object takes over the input blob and will free it when the
89 ** Manifest object is freed. Zeros are inserted into the blob
90 ** as string terminators so that blob should not be used again.
91 **
92 ** Return TRUE if the content really is a manifest. Return FALSE
93 ** if there are syntax errors.
 
 
 
 
 
94 **
95 ** The pContent is reset. If TRUE is returned, then pContent will
96 ** be reset when the Manifest object is cleared. If FALSE is
97 ** returned then the Manifest object is cleared automatically
98 ** and pContent is reset before the return.
 
 
 
 
 
 
 
 
99 */
100 int manifest_parse(Manifest *p, Blob *pContent){
101 int seenHeader = 0;
 
102 int i, lineNo=0;
103 Blob line, token, a1, a2, a3;
104 Blob selfuuid;
105 char cPrevType = 0;
106
@@ -135,10 +178,48 @@
135 }
136 cPrevType = z[0];
137 seenHeader = 1;
138 if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
139 switch( z[0] ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140 /*
141 ** C <comment>
142 **
143 ** Comment text is fossil-encoded. There may be no more than
144 ** one C line. C lines are required for manifests and are
@@ -169,10 +250,33 @@
169 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
170 zDate = blob_terminate(&a1);
171 p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
172 break;
173 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
175 /*
176 ** F <filename> <uuid>
177 **
178 ** Identifies a file in a manifest. Multiple F lines are
@@ -204,10 +308,74 @@
204 if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
205 goto manifest_syntax_error;
206 }
207 break;
208 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
210 /*
211 ** M <uuid>
212 **
213 ** An M-line identifies another artifact by its UUID. M-lines
@@ -231,10 +399,52 @@
231 if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){
232 goto manifest_syntax_error;
233 }
234 break;
235 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
237 /*
238 ** T (+|*|-)<tagname> <uuid> ?<value>?
239 **
240 ** Create or cancel a tag or property. The tagname is fossil-encoded.
@@ -311,57 +521,46 @@
311 defossilize(p->zUser);
312 break;
313 }
314
315 /*
316 ** R <md5sum>
317 **
318 ** Specify the MD5 checksum of the entire baseline in a
319 ** manifest.
320 */
321 case 'R': {
322 md5sum_step_text(blob_buffer(&line), blob_size(&line));
323 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
324 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
325 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
326 if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
327 p->zRepoCksum = blob_terminate(&a1);
328 if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
329 break;
330 }
331
332 /*
333 ** P <uuid> ...
334 **
335 ** Specify one or more other artifacts where are the parents of
336 ** this artifact. The first parent is the primary parent. All
337 ** others are parents by merge.
338 */
339 case 'P': {
340 md5sum_step_text(blob_buffer(&line), blob_size(&line));
341 while( blob_token(&line, &a1) ){
342 char *zUuid;
343 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
344 zUuid = blob_terminate(&a1);
345 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
346 if( p->nParent>=p->nParentAlloc ){
347 p->nParentAlloc = p->nParentAlloc*2 + 5;
348 p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));
349 if( p->azParent==0 ) fossil_panic("out of memory");
350 }
351 i = p->nParent++;
352 p->azParent[i] = zUuid;
353 }
354 break;
355 }
356
357 /*
358 ** Z <md5sum>
359 **
360 ** MD5 checksum on this control file. The checksum is over all
361 ** lines (other than PGP-signature lines) prior to the current
362 ** line. This must be the last record.
 
 
 
 
363 */
364 case 'Z': {
365 int rc;
366 Blob hash;
367 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
@@ -370,10 +569,11 @@
370 if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
371 md5sum_finish(&hash);
372 rc = blob_compare(&hash, &a1);
373 blob_reset(&hash);
374 if( rc!=0 ) goto manifest_syntax_error;
 
375 break;
376 }
377 default: {
378 goto manifest_syntax_error;
379 }
@@ -382,23 +582,62 @@
382 if( !seenHeader ) goto manifest_syntax_error;
383
384 if( p->nFile>0 ){
385 if( p->nCChild>0 ) goto manifest_syntax_error;
386 if( p->rDate==0.0 ) goto manifest_syntax_error;
 
 
 
 
 
 
387 p->type = CFTYPE_MANIFEST;
388 }else if( p->nCChild>0 ){
389 if( p->rDate>0.0 ) goto manifest_syntax_error;
390 if( p->zComment!=0 ) goto manifest_syntax_error;
391 if( p->zUser!=0 ) goto manifest_syntax_error;
392 if( p->nTag>0 ) goto manifest_syntax_error;
393 if( p->nParent>0 ) goto manifest_syntax_error;
394 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
 
 
 
 
 
 
395 p->type = CFTYPE_CLUSTER;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396 }else if( p->nTag>0 ){
397 if( p->rDate<=0.0 ) goto manifest_syntax_error;
398 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
399 if( p->nParent>0 ) goto manifest_syntax_error;
 
 
 
 
 
 
400 p->type = CFTYPE_CONTROL;
401 }else{
402 goto manifest_syntax_error;
403 }
404
@@ -582,10 +821,22 @@
582 rid, m.rDate, tid);
583 }
584 if( parentid ){
585 tag_propagate_all(parentid);
586 }
 
 
 
 
 
 
 
 
 
 
 
 
587 }
588 db_end_transaction(0);
589 manifest_clear(&m);
590 return 1;
591 }
592
--- src/manifest.c
+++ src/manifest.c
@@ -22,11 +22,11 @@
22 *******************************************************************************
23 **
24 ** This file contains code used to cross link control files and
25 ** manifests. The file is named "manifest.c" because it was
26 ** original only used to parse manifests. Then later clusters
27 ** and control files and wiki pages and tickets were added.
28 */
29 #include "config.h"
30 #include "manifest.h"
31 #include <assert.h>
32
@@ -35,22 +35,35 @@
35 ** Types of control files
36 */
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 char zUuid[UUID_SIZE+1]; /* Self UUID */
59 double rDate; /* Time in the "D" line */
60 char *zUser; /* Name of the user */
61 char *zRepoCksum; /* MD5 checksum of the baseline content */
62 char *zWiki; /* Text of the wiki page */
63 char *zWikiTitle; /* Name of the wiki page */
64 char *zTicketUuid; /* UUID for a ticket */
65 int nFile; /* Number of F lines */
66 int nFileAlloc; /* Slots allocated in aFile[] */
67 struct {
68 char *zName; /* Name of a file */
69 char *zUuid; /* UUID of the file */
@@ -66,10 +79,23 @@
79 struct {
80 char *zName; /* Name of the tag */
81 char *zUuid; /* UUID that the tag is applied to */
82 char *zValue; /* Value if the tag is really a property */
83 } *aTag;
84 int nField; /* Number of J lines */
85 int nFieldAlloc; /* Slots allocated in aField[] */
86 struct {
87 char *zName; /* Key or field name */
88 char *zValue; /* Value of the field */
89 } *aField;
90 int nAttach; /* Number of A lines */
91 int nAttachAlloc; /* Slots allocated in aAttach[] */
92 struct {
93 char *zUuid; /* UUID of the attachment */
94 char *zName; /* Name of the attachment */
95 char *zDesc; /* Description of the attachment */
96 } *aAttach;
97 };
98 #endif
99
100
101 /*
@@ -78,29 +104,46 @@
104 void manifest_clear(Manifest *p){
105 blob_reset(&p->content);
106 free(p->aFile);
107 free(p->azParent);
108 free(p->azCChild);
109 free(p->aTag);
110 free(p->aField);
111 free(p->aAttach);
112 memset(p, 0, sizeof(*p));
113 }
114
115 /*
116 ** Parse a blob into a Manifest object. The Manifest object
117 ** takes over the input blob and will free it when the
118 ** Manifest object is freed. Zeros are inserted into the blob
119 ** as string terminators so that blob should not be used again.
120 **
121 ** Return TRUE if the content really is a control file of some
122 ** kind. Return FALSE if there are syntax errors.
123 **
124 ** This routine is strict about the format of a control file.
125 ** The format must match exactly or else it is rejected. This
126 ** rule minimizes the risk that a content file will be mistaken
127 ** for a control file simply because they look the same.
128 **
129 ** The pContent is reset. If TRUE is returned, then pContent will
130 ** be reset when the Manifest object is cleared. If FALSE is
131 ** returned then the Manifest object is cleared automatically
132 ** and pContent is reset before the return.
133 **
134 ** The entire file can be PGP clear-signed. The signature is ignored.
135 ** The file consists of zero or more cards, one card per line.
136 ** (Except: the content of the W card can extend of multiple lines.)
137 ** Each card is divided into tokens by a single space character.
138 ** The first token is a single upper-case letter which is the card type.
139 ** The card type determines the other parameters to the card.
140 ** Cards must occur in lexicographical order.
141 */
142 int manifest_parse(Manifest *p, Blob *pContent){
143 int seenHeader = 0;
144 int seenZ = 0;
145 int i, lineNo=0;
146 Blob line, token, a1, a2, a3;
147 Blob selfuuid;
148 char cPrevType = 0;
149
@@ -135,10 +178,48 @@
178 }
179 cPrevType = z[0];
180 seenHeader = 1;
181 if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
182 switch( z[0] ){
183 /*
184 ** A <uuid> <filename> <description>
185 **
186 ** Identifies an attachment to either a wiki page or a ticket.
187 ** <uuid> is the artifact that is the attachment.
188 */
189 case 'A': {
190 char *zName, *zUuid, *zDesc;
191 md5sum_step_text(blob_buffer(&line), blob_size(&line));
192 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
193 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
194 if( blob_token(&line, &a3)==0 ) goto manifest_syntax_error;
195 zUuid = blob_terminate(&a1);
196 zName = blob_terminate(&a2);
197 zDesc = blob_terminate(&a3);
198 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
199 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
200 defossilize(zName);
201 if( !file_is_simple_pathname(zName) ){
202 goto manifest_syntax_error;
203 }
204 defossilize(zDesc);
205 if( p->nAttach>=p->nAttachAlloc ){
206 p->nAttachAlloc = p->nAttachAlloc*2 + 10;
207 p->aAttach = realloc(p->aAttach,
208 p->nAttachAlloc*sizeof(p->aAttach[0]) );
209 if( p->aAttach==0 ) fossil_panic("out of memory");
210 }
211 i = p->nAttach++;
212 p->aAttach[i].zUuid = zUuid;
213 p->aAttach[i].zName = zName;
214 p->aAttach[i].zDesc = zDesc;
215 if( i>0 && strcmp(p->aAttach[i-1].zUuid, zUuid)>=0 ){
216 goto manifest_syntax_error;
217 }
218 break;
219 }
220
221 /*
222 ** C <comment>
223 **
224 ** Comment text is fossil-encoded. There may be no more than
225 ** one C line. C lines are required for manifests and are
@@ -169,10 +250,33 @@
250 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
251 zDate = blob_terminate(&a1);
252 p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
253 break;
254 }
255
256 /*
257 ** E <mode>
258 **
259 ** Access mode. <mode> can be one of "read", "append",
260 ** or "write".
261 */
262 case 'E': {
263 md5sum_step_text(blob_buffer(&line), blob_size(&line));
264 if( p->mode!=0 ) goto manifest_syntax_error;
265 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
266 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
267 if( blob_eq(&a1, "write") ){
268 p->mode = CFMODE_WRITE;
269 }else if( blob_eq(&a1, "append") ){
270 p->mode = CFMODE_APPEND;
271 }else if( blob_eq(&a1, "read") ){
272 p->mode = CFMODE_READ;
273 }else{
274 goto manifest_syntax_error;
275 }
276 break;
277 }
278
279 /*
280 ** F <filename> <uuid>
281 **
282 ** Identifies a file in a manifest. Multiple F lines are
@@ -204,10 +308,74 @@
308 if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
309 goto manifest_syntax_error;
310 }
311 break;
312 }
313
314 /*
315 ** J <name> <value>
316 **
317 ** Specifies a name value pair for ticket.
318 */
319 case 'J': {
320 char *zName, *zValue;
321 md5sum_step_text(blob_buffer(&line), blob_size(&line));
322 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
323 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
324 if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
325 zName = blob_terminate(&a1);
326 zValue = blob_terminate(&a2);
327 defossilize(zValue);
328 if( p->nField>=p->nFieldAlloc ){
329 p->nFieldAlloc = p->nFieldAlloc*2 + 10;
330 p->aField = realloc(p->aField,
331 p->nFieldAlloc*sizeof(p->aField[0]) );
332 if( p->aField==0 ) fossil_panic("out of memory");
333 }
334 i = p->nField++;
335 p->aField[i].zName = zName;
336 p->aField[i].zValue = zValue;
337 if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){
338 goto manifest_syntax_error;
339 }
340 break;
341 }
342
343
344 /*
345 ** K <uuid>
346 **
347 ** A K-line gives the UUID for the ticket which this control file
348 ** is amending.
349 */
350 case 'K': {
351 char *zUuid;
352 md5sum_step_text(blob_buffer(&line), blob_size(&line));
353 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
354 zUuid = blob_terminate(&a1);
355 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
356 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
357 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
358 p->zTicketUuid = zUuid;
359 break;
360 }
361
362 /*
363 ** L <wikitite>
364 **
365 ** The wiki page title is fossil-encoded. There may be no more than
366 ** one L line.
367 */
368 case 'L': {
369 md5sum_step_text(blob_buffer(&line), blob_size(&line));
370 if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
371 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
372 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
373 p->zWikiTitle = blob_terminate(&a1);
374 defossilize(p->zWikiTitle);
375 break;
376 }
377
378 /*
379 ** M <uuid>
380 **
381 ** An M-line identifies another artifact by its UUID. M-lines
@@ -231,10 +399,52 @@
399 if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){
400 goto manifest_syntax_error;
401 }
402 break;
403 }
404
405 /*
406 ** P <uuid> ...
407 **
408 ** Specify one or more other artifacts where are the parents of
409 ** this artifact. The first parent is the primary parent. All
410 ** others are parents by merge.
411 */
412 case 'P': {
413 md5sum_step_text(blob_buffer(&line), blob_size(&line));
414 while( blob_token(&line, &a1) ){
415 char *zUuid;
416 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
417 zUuid = blob_terminate(&a1);
418 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
419 if( p->nParent>=p->nParentAlloc ){
420 p->nParentAlloc = p->nParentAlloc*2 + 5;
421 p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));
422 if( p->azParent==0 ) fossil_panic("out of memory");
423 }
424 i = p->nParent++;
425 p->azParent[i] = zUuid;
426 }
427 break;
428 }
429
430 /*
431 ** R <md5sum>
432 **
433 ** Specify the MD5 checksum of the entire baseline in a
434 ** manifest.
435 */
436 case 'R': {
437 md5sum_step_text(blob_buffer(&line), blob_size(&line));
438 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
439 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
440 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
441 if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
442 p->zRepoCksum = blob_terminate(&a1);
443 if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
444 break;
445 }
446
447 /*
448 ** T (+|*|-)<tagname> <uuid> ?<value>?
449 **
450 ** Create or cancel a tag or property. The tagname is fossil-encoded.
@@ -311,57 +521,46 @@
521 defossilize(p->zUser);
522 break;
523 }
524
525 /*
526 ** W <size>
527 **
528 ** The next <size> bytes of the file contain the text of the wiki
529 ** page. There is always an extra \n before the start of the next
530 ** record.
531 */
532 case 'W': {
533 int size;
534 Blob wiki;
535 md5sum_step_text(blob_buffer(&line), blob_size(&line));
536 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
537 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
538 if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error;
539 if( size<0 ) goto manifest_syntax_error;
540 if( p->zWiki!=0 ) goto manifest_syntax_error;
541 blob_zero(&wiki);
542 if( blob_extract(pContent, size+1, &wiki)!=size+1 ){
543 goto manifest_syntax_error;
544 }
545 p->zWiki = blob_buffer(&wiki);
546 if( p->zWiki[size]!='\n' ) goto manifest_syntax_error;
547 p->zWiki[size] = 0;
548 break;
549 }
550
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
552 /*
553 ** Z <md5sum>
554 **
555 ** MD5 checksum on this control file. The checksum is over all
556 ** lines (other than PGP-signature lines) prior to the current
557 ** line. This must be the last record.
558 **
559 ** This card is required for all control file types except for
560 ** Manifest. It is not required for manifest only for historical
561 ** compatibility reasons.
562 */
563 case 'Z': {
564 int rc;
565 Blob hash;
566 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
@@ -370,10 +569,11 @@
569 if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
570 md5sum_finish(&hash);
571 rc = blob_compare(&hash, &a1);
572 blob_reset(&hash);
573 if( rc!=0 ) goto manifest_syntax_error;
574 seenZ = 1;
575 break;
576 }
577 default: {
578 goto manifest_syntax_error;
579 }
@@ -382,23 +582,62 @@
582 if( !seenHeader ) goto manifest_syntax_error;
583
584 if( p->nFile>0 ){
585 if( p->nCChild>0 ) goto manifest_syntax_error;
586 if( p->rDate==0.0 ) goto manifest_syntax_error;
587 if( p->nField>0 ) goto manifest_syntax_error;
588 if( p->zTicketUuid ) goto manifest_syntax_error;
589 if( p->nAttach>0 ) goto manifest_syntax_error;
590 if( p->zWiki ) goto manifest_syntax_error;
591 if( p->zWikiTitle ) goto manifest_syntax_error;
592 if( p->zTicketUuid ) goto manifest_syntax_error;
593 p->type = CFTYPE_MANIFEST;
594 }else if( p->nCChild>0 ){
595 if( p->rDate>0.0 ) goto manifest_syntax_error;
596 if( p->zComment!=0 ) goto manifest_syntax_error;
597 if( p->zUser!=0 ) goto manifest_syntax_error;
598 if( p->nTag>0 ) goto manifest_syntax_error;
599 if( p->nParent>0 ) goto manifest_syntax_error;
600 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
601 if( p->nField>0 ) goto manifest_syntax_error;
602 if( p->zTicketUuid ) goto manifest_syntax_error;
603 if( p->nAttach>0 ) goto manifest_syntax_error;
604 if( p->zWiki ) goto manifest_syntax_error;
605 if( p->zWikiTitle ) goto manifest_syntax_error;
606 if( !seenZ ) goto manifest_syntax_error;
607 p->type = CFTYPE_CLUSTER;
608 }else if( p->nField>0 ){
609 if( p->rDate==0.0 ) goto manifest_syntax_error;
610 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
611 if( p->zWiki ) goto manifest_syntax_error;
612 if( p->zWikiTitle ) goto manifest_syntax_error;
613 if( p->nCChild>0 ) goto manifest_syntax_error;
614 if( p->nTag>0 ) goto manifest_syntax_error;
615 if( p->zTicketUuid==0 ) goto manifest_syntax_error;
616 if( p->zUser==0 ) goto manifest_syntax_error;
617 if( !seenZ ) goto manifest_syntax_error;
618 p->type = CFTYPE_TICKET;
619 }else if( p->zWiki!=0 ){
620 if( p->rDate==0.0 ) goto manifest_syntax_error;
621 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
622 if( p->nCChild>0 ) goto manifest_syntax_error;
623 if( p->nTag>0 ) goto manifest_syntax_error;
624 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
625 if( p->zUser==0 ) goto manifest_syntax_error;
626 if( p->zWikiTitle==0 ) goto manifest_syntax_error;
627 if( !seenZ ) goto manifest_syntax_error;
628 p->type = CFTYPE_WIKI;
629 }else if( p->nTag>0 ){
630 if( p->rDate<=0.0 ) goto manifest_syntax_error;
631 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
632 if( p->nParent>0 ) goto manifest_syntax_error;
633 if( p->nAttach>0 ) goto manifest_syntax_error;
634 if( p->nField>0 ) goto manifest_syntax_error;
635 if( p->zWiki ) goto manifest_syntax_error;
636 if( p->zWikiTitle ) goto manifest_syntax_error;
637 if( p->zTicketUuid ) goto manifest_syntax_error;
638 if( !seenZ ) goto manifest_syntax_error;
639 p->type = CFTYPE_CONTROL;
640 }else{
641 goto manifest_syntax_error;
642 }
643
@@ -582,10 +821,22 @@
821 rid, m.rDate, tid);
822 }
823 if( parentid ){
824 tag_propagate_all(parentid);
825 }
826 }
827 if( m.type==CFTYPE_WIKI ){
828 char *zTag = mprintf("wiki-%s", m.zWikiTitle);
829 int tagid = tag_findid(zTag, 1);
830 int prior;
831 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
832 free(zTag);
833 prior = db_int(0, "SELECT rid FROM tagxref WHERE tagid=%d"
834 " ORDER BY mtime DESC LIMIT 1 OFFSET 1", tagid);
835 if( prior ){
836 content_deltify(prior, rid, 0);
837 }
838 }
839 db_end_transaction(0);
840 manifest_clear(&m);
841 return 1;
842 }
843
+1 -1
--- src/schema.c
+++ src/schema.c
@@ -224,11 +224,11 @@
224224
@ value TEXT, -- Value of the tag. Might be NULL.
225225
@ mtime TIMESTAMP, -- Time of addition or removal
226226
@ rid INTEGER REFERENCE blob, -- Baseline that tag added/removed from
227227
@ UNIQUE(rid, tagid)
228228
@ );
229
-@ CREATE INDEX tagxref_i1 ON tagxref(tagid);
229
+@ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
230230
;
231231
232232
/*
233233
** Predefined tagid values
234234
*/
235235
--- src/schema.c
+++ src/schema.c
@@ -224,11 +224,11 @@
224 @ value TEXT, -- Value of the tag. Might be NULL.
225 @ mtime TIMESTAMP, -- Time of addition or removal
226 @ rid INTEGER REFERENCE blob, -- Baseline that tag added/removed from
227 @ UNIQUE(rid, tagid)
228 @ );
229 @ CREATE INDEX tagxref_i1 ON tagxref(tagid);
230 ;
231
232 /*
233 ** Predefined tagid values
234 */
235
--- src/schema.c
+++ src/schema.c
@@ -224,11 +224,11 @@
224 @ value TEXT, -- Value of the tag. Might be NULL.
225 @ mtime TIMESTAMP, -- Time of addition or removal
226 @ rid INTEGER REFERENCE blob, -- Baseline that tag added/removed from
227 @ UNIQUE(rid, tagid)
228 @ );
229 @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
230 ;
231
232 /*
233 ** Predefined tagid values
234 */
235
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -758,11 +758,10 @@
758758
static void resolveHyperlink(const char *zTarget, Renderer *p){
759759
if( strncmp(zTarget, "http:", 5)==0
760760
|| strncmp(zTarget, "https:", 6)==0
761761
|| strncmp(zTarget, "ftp:", 4)==0
762762
|| strncmp(zTarget, "mailto:", 7)==0
763
- || strncmp(zTarget, "gopher:", 7)==0
764763
){
765764
blob_appendf(p->pOut, zTarget);
766765
}else{
767766
blob_appendf(p->pOut, "%s/wiki/%T", g.zBaseURL, zTarget);
768767
}
@@ -993,11 +992,10 @@
993992
popStack(&renderer);
994993
}
995994
blob_append(pOut, "\n", 1);
996995
free(renderer.aStack);
997996
}
998
-
999997
1000998
/*
1001999
** COMMAND: test-wiki-render
10021000
*/
10031001
void test_wiki_render(void){
10041002
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -758,11 +758,10 @@
758 static void resolveHyperlink(const char *zTarget, Renderer *p){
759 if( strncmp(zTarget, "http:", 5)==0
760 || strncmp(zTarget, "https:", 6)==0
761 || strncmp(zTarget, "ftp:", 4)==0
762 || strncmp(zTarget, "mailto:", 7)==0
763 || strncmp(zTarget, "gopher:", 7)==0
764 ){
765 blob_appendf(p->pOut, zTarget);
766 }else{
767 blob_appendf(p->pOut, "%s/wiki/%T", g.zBaseURL, zTarget);
768 }
@@ -993,11 +992,10 @@
993 popStack(&renderer);
994 }
995 blob_append(pOut, "\n", 1);
996 free(renderer.aStack);
997 }
998
999
1000 /*
1001 ** COMMAND: test-wiki-render
1002 */
1003 void test_wiki_render(void){
1004
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -758,11 +758,10 @@
758 static void resolveHyperlink(const char *zTarget, Renderer *p){
759 if( strncmp(zTarget, "http:", 5)==0
760 || strncmp(zTarget, "https:", 6)==0
761 || strncmp(zTarget, "ftp:", 4)==0
762 || strncmp(zTarget, "mailto:", 7)==0
 
763 ){
764 blob_appendf(p->pOut, zTarget);
765 }else{
766 blob_appendf(p->pOut, "%s/wiki/%T", g.zBaseURL, zTarget);
767 }
@@ -993,11 +992,10 @@
992 popStack(&renderer);
993 }
994 blob_append(pOut, "\n", 1);
995 free(renderer.aStack);
996 }
 
997
998 /*
999 ** COMMAND: test-wiki-render
1000 */
1001 void test_wiki_render(void){
1002

Keyboard Shortcuts

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