Fossil SCM

Enhance the control-artifact parser to optionally return an error when the parse fails. Fix a bug in the artifact parser which caused it to ignore Z-card checksum failures.

drh 2012-10-31 12:12 trunk
Commit aab9e66b8b2901eafe5fe87166b22dc9a99ab1f9
3 files changed +1 -1 +7 -5 +165 -136
+1 -1
--- src/attach.c
+++ src/attach.c
@@ -243,11 +243,11 @@
243243
int addCompress = 0;
244244
Manifest *pManifest;
245245
246246
db_begin_transaction();
247247
blob_init(&content, aContent, szContent);
248
- pManifest = manifest_parse(&content, 0);
248
+ pManifest = manifest_parse(&content, 0, 0);
249249
manifest_destroy(pManifest);
250250
blob_init(&content, aContent, szContent);
251251
if( pManifest ){
252252
blob_compress(&content, &content);
253253
addCompress = 1;
254254
--- src/attach.c
+++ src/attach.c
@@ -243,11 +243,11 @@
243 int addCompress = 0;
244 Manifest *pManifest;
245
246 db_begin_transaction();
247 blob_init(&content, aContent, szContent);
248 pManifest = manifest_parse(&content, 0);
249 manifest_destroy(pManifest);
250 blob_init(&content, aContent, szContent);
251 if( pManifest ){
252 blob_compress(&content, &content);
253 addCompress = 1;
254
--- src/attach.c
+++ src/attach.c
@@ -243,11 +243,11 @@
243 int addCompress = 0;
244 Manifest *pManifest;
245
246 db_begin_transaction();
247 blob_init(&content, aContent, szContent);
248 pManifest = manifest_parse(&content, 0, 0);
249 manifest_destroy(pManifest);
250 blob_init(&content, aContent, szContent);
251 if( pManifest ){
252 blob_compress(&content, &content);
253 addCompress = 1;
254
+7 -5
--- src/blob.c
+++ src/blob.c
@@ -659,17 +659,19 @@
659659
660660
/*
661661
** Do printf-style string rendering and append the results to a blob.
662662
*/
663663
void blob_appendf(Blob *pBlob, const char *zFormat, ...){
664
- va_list ap;
665
- va_start(ap, zFormat);
666
- vxprintf(pBlob, zFormat, ap);
667
- va_end(ap);
664
+ if( pBlob ){
665
+ va_list ap;
666
+ va_start(ap, zFormat);
667
+ vxprintf(pBlob, zFormat, ap);
668
+ va_end(ap);
669
+ }
668670
}
669671
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
670
- vxprintf(pBlob, zFormat, ap);
672
+ if( pBlob ) vxprintf(pBlob, zFormat, ap);
671673
}
672674
673675
/*
674676
** Initalize a blob to the data on an input channel. Return
675677
** the number of bytes read into the blob. Any prior content
676678
--- src/blob.c
+++ src/blob.c
@@ -659,17 +659,19 @@
659
660 /*
661 ** Do printf-style string rendering and append the results to a blob.
662 */
663 void blob_appendf(Blob *pBlob, const char *zFormat, ...){
664 va_list ap;
665 va_start(ap, zFormat);
666 vxprintf(pBlob, zFormat, ap);
667 va_end(ap);
 
 
668 }
669 void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
670 vxprintf(pBlob, zFormat, ap);
671 }
672
673 /*
674 ** Initalize a blob to the data on an input channel. Return
675 ** the number of bytes read into the blob. Any prior content
676
--- src/blob.c
+++ src/blob.c
@@ -659,17 +659,19 @@
659
660 /*
661 ** Do printf-style string rendering and append the results to a blob.
662 */
663 void blob_appendf(Blob *pBlob, const char *zFormat, ...){
664 if( pBlob ){
665 va_list ap;
666 va_start(ap, zFormat);
667 vxprintf(pBlob, zFormat, ap);
668 va_end(ap);
669 }
670 }
671 void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
672 if( pBlob ) vxprintf(pBlob, zFormat, ap);
673 }
674
675 /*
676 ** Initalize a blob to the data on an input channel. Return
677 ** the number of bytes read into the blob. Any prior content
678
+165 -136
--- src/manifest.c
+++ src/manifest.c
@@ -307,10 +307,15 @@
307307
c = 0;
308308
}
309309
return c;
310310
}
311311
312
+/*
313
+** Shorthand for a control-artifact parsing error
314
+*/
315
+#define SYNTAX(T) {zErr=(T); goto manifest_syntax_error;}
316
+
312317
/*
313318
** Parse a blob into a Manifest object. The Manifest object
314319
** takes over the input blob and will free it when the
315320
** Manifest object is freed. Zeros are inserted into the blob
316321
** as string terminators so that blob should not be used again.
@@ -334,11 +339,11 @@
334339
** Each card is divided into tokens by a single space character.
335340
** The first token is a single upper-case letter which is the card type.
336341
** The card type determines the other parameters to the card.
337342
** Cards must occur in lexicographical order.
338343
*/
339
-Manifest *manifest_parse(Blob *pContent, int rid){
344
+Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){
340345
Manifest *p;
341346
int seenZ = 0;
342347
int i, lineNo=0;
343348
ManifestText x;
344349
char cPrevType = 0;
@@ -347,10 +352,11 @@
347352
int n;
348353
char *zUuid;
349354
int sz = 0;
350355
int isRepeat;
351356
static Bag seen;
357
+ const char *zErr = 0;
352358
353359
if( rid==0 ){
354360
isRepeat = 1;
355361
}else if( bag_find(&seen, rid) ){
356362
isRepeat = 1;
@@ -365,27 +371,30 @@
365371
if( !isRepeat ) g.parseCnt[0]++;
366372
z = blob_materialize(pContent);
367373
n = blob_size(pContent);
368374
if( n<=0 || z[n-1]!='\n' ){
369375
blob_reset(pContent);
376
+ blob_appendf(pErr, n ? "not terminated with \\n" : "zero-length");
370377
return 0;
371378
}
372379
373380
/* Strip off the PGP signature if there is one. Then verify the
374381
** Z-card.
375382
*/
376383
remove_pgp_signature(&z, &n);
377
- if( verify_z_card(z, n)==0 ){
384
+ if( verify_z_card(z, n)==2 ){
378385
blob_reset(pContent);
386
+ blob_appendf(pErr, "incorrect Z-card cksum");
379387
return 0;
380388
}
381389
382390
/* Verify that the first few characters of the artifact look like
383391
** a control artifact.
384392
*/
385393
if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
386394
blob_reset(pContent);
395
+ blob_appendf(pErr, "line 1 not recognized");
387396
return 0;
388397
}
389398
390399
/* Allocate a Manifest object to hold the parsed control artifact.
391400
*/
@@ -420,19 +429,19 @@
420429
zSrc = next_token(&x, &nSrc);
421430
if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
422431
if( p->zAttachName!=0 ) goto manifest_syntax_error;
423432
defossilize(zName);
424433
if( !file_is_simple_pathname(zName) ){
425
- goto manifest_syntax_error;
434
+ SYNTAX("invalid filename on A-card");
426435
}
427436
defossilize(zTarget);
428437
if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
429438
&& !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
430
- goto manifest_syntax_error;
439
+ SYNTAX("invalid target on A-card");
431440
}
432441
if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
433
- goto manifest_syntax_error;
442
+ SYNTAX("invalid source on A-card");
434443
}
435444
p->zAttachName = (char*)file_tail(zName);
436445
p->zAttachSrc = zSrc;
437446
p->zAttachTarget = zTarget;
438447
break;
@@ -442,15 +451,16 @@
442451
** B <uuid>
443452
**
444453
** A B-line gives the UUID for the baselinen of a delta-manifest.
445454
*/
446455
case 'B': {
447
- if( p->zBaseline ) goto manifest_syntax_error;
456
+ if( p->zBaseline ) SYNTAX("more than one B-card");
448457
p->zBaseline = next_token(&x, &sz);
449
- if( p->zBaseline==0 ) goto manifest_syntax_error;
450
- if( sz!=UUID_SIZE ) goto manifest_syntax_error;
451
- if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error;
458
+ if( p->zBaseline==0 ) SYNTAX("missing UUID on B-card");
459
+ if( sz!=UUID_SIZE || !validate16(p->zBaseline, UUID_SIZE) ){
460
+ SYNTAX("invalid UUID on B-card");
461
+ }
452462
break;
453463
}
454464
455465
456466
/*
@@ -459,13 +469,13 @@
459469
** Comment text is fossil-encoded. There may be no more than
460470
** one C line. C lines are required for manifests and are
461471
** disallowed on all other control files.
462472
*/
463473
case 'C': {
464
- if( p->zComment!=0 ) goto manifest_syntax_error;
474
+ if( p->zComment!=0 ) SYNTAX("more than one C-card");
465475
p->zComment = next_token(&x, 0);
466
- if( p->zComment==0 ) goto manifest_syntax_error;
476
+ if( p->zComment==0 ) SYNTAX("missing comment text on C-card");
467477
defossilize(p->zComment);
468478
break;
469479
}
470480
471481
/*
@@ -474,13 +484,13 @@
474484
** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS
475485
** There can be no more than 1 D line. D lines are required
476486
** for all control files except for clusters.
477487
*/
478488
case 'D': {
479
- if( p->rDate>0.0 ) goto manifest_syntax_error;
489
+ if( p->rDate>0.0 ) SYNTAX("more than one D-card");
480490
p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0));
481
- if( p->rDate<=0.0 ) goto manifest_syntax_error;
491
+ if( p->rDate<=0.0 ) SYNTAX("cannot parse date on D-card");
482492
break;
483493
}
484494
485495
/*
486496
** E <timestamp> <uuid>
@@ -490,16 +500,17 @@
490500
** The event timestamp is distinct from the D timestamp. The D
491501
** timestamp is when the artifact was created whereas the E timestamp
492502
** is when the specific event is said to occur.
493503
*/
494504
case 'E': {
495
- if( p->rEventDate>0.0 ) goto manifest_syntax_error;
505
+ if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
496506
p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
497
- if( p->rEventDate<=0.0 ) goto manifest_syntax_error;
507
+ if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
498508
p->zEventId = next_token(&x, &sz);
499
- if( sz!=UUID_SIZE ) goto manifest_syntax_error;
500
- if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error;
509
+ if( sz!=UUID_SIZE || !validate16(p->zEventId, UUID_SIZE) ){
510
+ SYNTAX("malformed UUID on E-card");
511
+ }
501512
break;
502513
}
503514
504515
/*
505516
** F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
@@ -509,26 +520,26 @@
509520
** other control file. The filename and old-name are fossil-encoded.
510521
*/
511522
case 'F': {
512523
char *zName, *zPerm, *zPriorName;
513524
zName = next_token(&x,0);
514
- if( zName==0 ) goto manifest_syntax_error;
525
+ if( zName==0 ) SYNTAX("missing filename on F-card");
515526
defossilize(zName);
516527
if( !file_is_simple_pathname(zName) ){
517
- goto manifest_syntax_error;
528
+ SYNTAX("F-card filename is not a simple path");
518529
}
519530
zUuid = next_token(&x, &sz);
520531
if( p->zBaseline==0 || zUuid!=0 ){
521
- if( sz!=UUID_SIZE ) goto manifest_syntax_error;
522
- if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
532
+ if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size");
533
+ if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("F-card UUID invalid");
523534
}
524535
zPerm = next_token(&x,0);
525536
zPriorName = next_token(&x,0);
526537
if( zPriorName ){
527538
defossilize(zPriorName);
528539
if( !file_is_simple_pathname(zPriorName) ){
529
- goto manifest_syntax_error;
540
+ SYNTAX("F-card old filename is not a simple path");
530541
}
531542
}
532543
if( p->nFile>=p->nFileAlloc ){
533544
p->nFileAlloc = p->nFileAlloc*2 + 10;
534545
p->aFile = fossil_realloc(p->aFile,
@@ -538,11 +549,11 @@
538549
p->aFile[i].zName = zName;
539550
p->aFile[i].zUuid = zUuid;
540551
p->aFile[i].zPerm = zPerm;
541552
p->aFile[i].zPrior = zPriorName;
542553
if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
543
- goto manifest_syntax_error;
554
+ SYNTAX("incorrect F-card sort order");
544555
}
545556
break;
546557
}
547558
548559
/*
@@ -555,11 +566,11 @@
555566
*/
556567
case 'J': {
557568
char *zName, *zValue;
558569
zName = next_token(&x,0);
559570
zValue = next_token(&x,0);
560
- if( zName==0 ) goto manifest_syntax_error;
571
+ if( zName==0 ) SYNTAX("name missing from J-card");
561572
if( zValue==0 ) zValue = "";
562573
defossilize(zValue);
563574
if( p->nField>=p->nFieldAlloc ){
564575
p->nFieldAlloc = p->nFieldAlloc*2 + 10;
565576
p->aField = fossil_realloc(p->aField,
@@ -567,11 +578,11 @@
567578
}
568579
i = p->nField++;
569580
p->aField[i].zName = zName;
570581
p->aField[i].zValue = zValue;
571582
if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
572
- goto manifest_syntax_error;
583
+ SYNTAX("incorrect J-card sort order");
573584
}
574585
break;
575586
}
576587
577588
@@ -580,14 +591,16 @@
580591
**
581592
** A K-line gives the UUID for the ticket which this control file
582593
** is amending.
583594
*/
584595
case 'K': {
585
- if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
596
+ if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card");
586597
p->zTicketUuid = next_token(&x, &sz);
587
- if( sz!=UUID_SIZE ) goto manifest_syntax_error;
588
- if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error;
598
+ if( sz!=UUID_SIZE ) SYNTAX("K-card UUID is the wrong size");
599
+ if( !validate16(p->zTicketUuid, UUID_SIZE) ){
600
+ SYNTAX("invalid K-card UUID");
601
+ }
589602
break;
590603
}
591604
592605
/*
593606
** L <wikititle>
@@ -594,16 +607,16 @@
594607
**
595608
** The wiki page title is fossil-encoded. There may be no more than
596609
** one L line.
597610
*/
598611
case 'L': {
599
- if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
612
+ if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card");
600613
p->zWikiTitle = next_token(&x,0);
601
- if( p->zWikiTitle==0 ) goto manifest_syntax_error;
614
+ if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card");
602615
defossilize(p->zWikiTitle);
603616
if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
604
- goto manifest_syntax_error;
617
+ SYNTAX("L-card has malformed wiki name");
605618
}
606619
break;
607620
}
608621
609622
/*
@@ -612,22 +625,22 @@
612625
** An M-line identifies another artifact by its UUID. M-lines
613626
** occur in clusters only.
614627
*/
615628
case 'M': {
616629
zUuid = next_token(&x, &sz);
617
- if( zUuid==0 ) goto manifest_syntax_error;
618
- if( sz!=UUID_SIZE ) goto manifest_syntax_error;
619
- if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
630
+ if( zUuid==0 ) SYNTAX("missing UUID on M-card");
631
+ if( sz!=UUID_SIZE ) SYNTAX("wrong size for UUID on M-card");
632
+ if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("UUID invalid on M-card");
620633
if( p->nCChild>=p->nCChildAlloc ){
621634
p->nCChildAlloc = p->nCChildAlloc*2 + 10;
622635
p->azCChild = fossil_realloc(p->azCChild
623636
, p->nCChildAlloc*sizeof(p->azCChild[0]) );
624637
}
625638
i = p->nCChild++;
626639
p->azCChild[i] = zUuid;
627640
if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){
628
- goto manifest_syntax_error;
641
+ SYNTAX("M-card in the wrong order");
629642
}
630643
break;
631644
}
632645
633646
/*
@@ -637,12 +650,12 @@
637650
** this artifact. The first parent is the primary parent. All
638651
** others are parents by merge.
639652
*/
640653
case 'P': {
641654
while( (zUuid = next_token(&x, &sz))!=0 ){
642
- if( sz!=UUID_SIZE ) goto manifest_syntax_error;
643
- if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
655
+ if( sz!=UUID_SIZE ) SYNTAX("wrong size UUID on P-card");
656
+ if( !validate16(zUuid, UUID_SIZE) )SYNTAX("invalid UUID on P-card");
644657
if( p->nParent>=p->nParentAlloc ){
645658
p->nParentAlloc = p->nParentAlloc*2 + 5;
646659
p->azParent = fossil_realloc(p->azParent,
647660
p->nParentAlloc*sizeof(char*));
648661
}
@@ -657,23 +670,29 @@
657670
**
658671
** Specify one or a range of checkins that are cherrypicked into
659672
** this checkin ("+") or backed out of this checkin ("-").
660673
*/
661674
case 'Q': {
662
- if( (zUuid = next_token(&x, &sz))==0 ) goto manifest_syntax_error;
663
- if( sz!=UUID_SIZE+1 ) goto manifest_syntax_error;
664
- if( zUuid[0]!='+' && zUuid[0]!='-' ) goto manifest_syntax_error;
665
- if( !validate16(&zUuid[1], UUID_SIZE) ) goto manifest_syntax_error;
675
+ if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing UUID on Q-card");
676
+ if( sz!=UUID_SIZE+1 ) SYNTAX("wrong size UUID on Q-card");
677
+ if( zUuid[0]!='+' && zUuid[0]!='-' ){
678
+ SYNTAX("Q-card does not begin with '+' or '-'");
679
+ }
680
+ if( !validate16(&zUuid[1], UUID_SIZE) ){
681
+ SYNTAX("invalid UUID on Q-card");
682
+ }
666683
n = p->nCherrypick;
667684
p->nCherrypick++;
668685
p->aCherrypick = fossil_realloc(p->aCherrypick,
669686
p->nCherrypick*sizeof(p->aCherrypick[0]));
670687
p->aCherrypick[n].zCPTarget = zUuid;
671688
p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz);
672689
if( zUuid ){
673
- if( sz!=UUID_SIZE ) goto manifest_syntax_error;
674
- if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
690
+ if( sz!=UUID_SIZE ) SYNTAX("wrong size second UUID in Q-card");
691
+ if( !validate16(zUuid, UUID_SIZE) ){
692
+ SYNTAX("invalid second UUID on Q-card");
693
+ }
675694
}
676695
break;
677696
}
678697
679698
/*
@@ -681,14 +700,14 @@
681700
**
682701
** Specify the MD5 checksum over the name and content of all files
683702
** in the manifest.
684703
*/
685704
case 'R': {
686
- if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
705
+ if( p->zRepoCksum!=0 ) SYNTAX("more than on R-card");
687706
p->zRepoCksum = next_token(&x, &sz);
688
- if( sz!=32 ) goto manifest_syntax_error;
689
- if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
707
+ if( sz!=32 ) SYNTAX("wrong size cksum on R-card");
708
+ if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum");
690709
break;
691710
}
692711
693712
/*
694713
** T (+|*|-)<tagname> <uuid> ?<value>?
@@ -706,29 +725,29 @@
706725
** Tags are not allowed in clusters. Multiple T lines are allowed.
707726
*/
708727
case 'T': {
709728
char *zName, *zValue;
710729
zName = next_token(&x, 0);
711
- if( zName==0 ) goto manifest_syntax_error;
730
+ if( zName==0 ) SYNTAX("missing name on T-card");
712731
zUuid = next_token(&x, &sz);
713
- if( zUuid==0 ) goto manifest_syntax_error;
732
+ if( zUuid==0 ) SYNTAX("missing UUID on T-card");
714733
zValue = next_token(&x, 0);
715734
if( zValue ) defossilize(zValue);
716735
if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
717736
/* A valid uuid */
718737
}else if( sz==1 && zUuid[0]=='*' ){
719738
zUuid = 0;
720739
}else{
721
- goto manifest_syntax_error;
740
+ SYNTAX("malformed UUID on T-card");
722741
}
723742
defossilize(zName);
724743
if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
725
- goto manifest_syntax_error;
744
+ SYNTAX("T-card name does not begin with '-', '+', or '*'");
726745
}
727746
if( validate16(&zName[1], strlen(&zName[1])) ){
728747
/* Do not allow tags whose names look like UUIDs */
729
- goto manifest_syntax_error;
748
+ SYNTAX("T-card name looks like a UUID");
730749
}
731750
if( p->nTag>=p->nTagAlloc ){
732751
p->nTagAlloc = p->nTagAlloc*2 + 10;
733752
p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) );
734753
}
@@ -735,11 +754,11 @@
735754
i = p->nTag++;
736755
p->aTag[i].zName = zName;
737756
p->aTag[i].zUuid = zUuid;
738757
p->aTag[i].zValue = zValue;
739758
if( i>0 && fossil_strcmp(p->aTag[i-1].zName, zName)>=0 ){
740
- goto manifest_syntax_error;
759
+ SYNTAX("T-card in the wrong order");
741760
}
742761
break;
743762
}
744763
745764
/*
@@ -748,11 +767,11 @@
748767
** Identify the user who created this control file by their
749768
** login. Only one U line is allowed. Prohibited in clusters.
750769
** If the user name is omitted, take that to be "anonymous".
751770
*/
752771
case 'U': {
753
- if( p->zUser!=0 ) goto manifest_syntax_error;
772
+ if( p->zUser!=0 ) SYNTAX("more than on U-card");
754773
p->zUser = next_token(&x, 0);
755774
if( p->zUser==0 ){
756775
p->zUser = "anonymous";
757776
}else{
758777
defossilize(p->zUser);
@@ -767,25 +786,26 @@
767786
** page. There is always an extra \n before the start of the next
768787
** record.
769788
*/
770789
case 'W': {
771790
char *zSize;
772
- int size, c;
791
+ unsigned size, oldsize, c;
773792
Blob wiki;
774793
zSize = next_token(&x, 0);
775
- if( zSize==0 ) goto manifest_syntax_error;
776
- if( x.atEol==0 ) goto manifest_syntax_error;
777
- for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
778
- size = size*10 + c - '0';
794
+ if( zSize==0 ) SYNTAX("missing size on W-card");
795
+ if( x.atEol==0 ) SYNTAX("no content after W-card");
796
+ for(oldsize=size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
797
+ size = oldsize*10 + c - '0';
798
+ if( size<oldsize ) SYNTAX("size overflow on W-card");
799
+ oldsize = size;
779800
}
780
- if( size<0 ) goto manifest_syntax_error;
781
- if( p->zWiki!=0 ) goto manifest_syntax_error;
801
+ if( p->zWiki!=0 ) SYNTAX("more than one W-card");
782802
blob_zero(&wiki);
783
- if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error;
803
+ if( (&x.z[size+1])>=x.zEnd )SYNTAX("not enough content after W-card");
784804
p->zWiki = x.z;
785805
x.z += size;
786
- if( x.z[0]!='\n' ) goto manifest_syntax_error;
806
+ if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated");
787807
x.z[0] = 0;
788808
x.z++;
789809
break;
790810
}
791811
@@ -801,111 +821,117 @@
801821
** Manifest. It is not required for manifest only for historical
802822
** compatibility reasons.
803823
*/
804824
case 'Z': {
805825
zUuid = next_token(&x, &sz);
806
- if( sz!=32 ) goto manifest_syntax_error;
807
- if( !validate16(zUuid, 32) ) goto manifest_syntax_error;
826
+ if( sz!=32 ) SYNTAX("wrong size for Z-card cksum");
827
+ if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum");
808828
seenZ = 1;
809829
break;
810830
}
811831
default: {
812
- goto manifest_syntax_error;
832
+ SYNTAX("unrecognized card");
813833
}
814834
}
815835
}
816
- if( x.z<x.zEnd ) goto manifest_syntax_error;
836
+ if( x.z<x.zEnd ) SYNTAX("card in the wrong order");
817837
818838
if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
819
- if( p->nCChild>0 ) goto manifest_syntax_error;
820
- if( p->rDate<=0.0 ) goto manifest_syntax_error;
821
- if( p->nField>0 ) goto manifest_syntax_error;
822
- if( p->zTicketUuid ) goto manifest_syntax_error;
823
- if( p->zWiki ) goto manifest_syntax_error;
824
- if( p->zWikiTitle ) goto manifest_syntax_error;
825
- if( p->zEventId ) goto manifest_syntax_error;
826
- if( p->zTicketUuid ) goto manifest_syntax_error;
827
- if( p->zAttachName ) goto manifest_syntax_error;
839
+ if( p->nCChild>0 ) SYNTAX("M-card in check-in");
840
+ if( p->rDate<=0.0 ) SYNTAX("missing date for check-in");
841
+ if( p->nField>0 ) SYNTAX("J-card in check-in");
842
+ if( p->zTicketUuid ) SYNTAX("K-card in check-in");
843
+ if( p->zWiki ) SYNTAX("W-card in check-in");
844
+ if( p->zWikiTitle ) SYNTAX("L-card in check-in");
845
+ if( p->zEventId ) SYNTAX("E-card in check-in");
846
+ if( p->zTicketUuid ) SYNTAX("K-card in check-in");
847
+ if( p->zAttachName ) SYNTAX("A-card in check-in");
828848
p->type = CFTYPE_MANIFEST;
829849
}else if( p->nCChild>0 ){
830
- if( p->rDate>0.0 ) goto manifest_syntax_error;
831
- if( p->zComment!=0 ) goto manifest_syntax_error;
832
- if( p->zUser!=0 ) goto manifest_syntax_error;
833
- if( p->nTag>0 ) goto manifest_syntax_error;
834
- if( p->nParent>0 ) goto manifest_syntax_error;
835
- if( p->nField>0 ) goto manifest_syntax_error;
836
- if( p->zTicketUuid ) goto manifest_syntax_error;
837
- if( p->zWiki ) goto manifest_syntax_error;
838
- if( p->zWikiTitle ) goto manifest_syntax_error;
839
- if( p->zEventId ) goto manifest_syntax_error;
840
- if( p->zAttachName ) goto manifest_syntax_error;
841
- if( !seenZ ) goto manifest_syntax_error;
850
+ if( p->rDate>0.0
851
+ || p->zComment!=0
852
+ || p->zUser!=0
853
+ || p->nTag>0
854
+ || p->nParent>0
855
+ || p->nField>0
856
+ || p->zTicketUuid
857
+ || p->zWiki
858
+ || p->zWikiTitle
859
+ || p->zEventId
860
+ || p->zAttachName
861
+ ){
862
+ SYNTAX("cluster contains a card other than M- or Z-");
863
+ }
864
+ if( !seenZ ) SYNTAX("missing Z-card on cluster");
842865
p->type = CFTYPE_CLUSTER;
843866
}else if( p->nField>0 ){
844
- if( p->rDate<=0.0 ) goto manifest_syntax_error;
845
- if( p->zWiki ) goto manifest_syntax_error;
846
- if( p->zWikiTitle ) goto manifest_syntax_error;
847
- if( p->zEventId ) goto manifest_syntax_error;
848
- if( p->nCChild>0 ) goto manifest_syntax_error;
849
- if( p->nTag>0 ) goto manifest_syntax_error;
850
- if( p->zTicketUuid==0 ) goto manifest_syntax_error;
851
- if( p->zUser==0 ) goto manifest_syntax_error;
852
- if( p->zAttachName ) goto manifest_syntax_error;
853
- if( !seenZ ) goto manifest_syntax_error;
867
+ if( p->rDate<=0.0 ) SYNTAX("missing date for ticket");
868
+ if( p->zWiki ) SYNTAX("W-card in ticket");
869
+ if( p->zWikiTitle ) SYNTAX("L-card in ticket");
870
+ if( p->zEventId ) SYNTAX("E-card in ticket");
871
+ if( p->nCChild>0 ) SYNTAX("M-card in ticket");
872
+ if( p->nTag>0 ) SYNTAX("T-card in ticket");
873
+ if( p->zTicketUuid==0 ) SYNTAX("missing K-card in ticket");
874
+ if( p->zUser==0 ) SYNTAX("missing U-card in ticket");
875
+ if( p->zAttachName ) SYNTAX("A-card in ticket");
876
+ if( !seenZ ) SYNTAX("missing Z-card in ticket");
854877
p->type = CFTYPE_TICKET;
855878
}else if( p->zEventId ){
856
- if( p->rDate<=0.0 ) goto manifest_syntax_error;
857
- if( p->nCChild>0 ) goto manifest_syntax_error;
858
- if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
859
- if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
860
- if( p->zWiki==0 ) goto manifest_syntax_error;
861
- if( p->zAttachName ) goto manifest_syntax_error;
879
+ if( p->rDate<=0.0 ) SYNTAX("missing date for event");
880
+ if( p->nCChild>0 ) SYNTAX("M-card in event");
881
+ if( p->zTicketUuid!=0 ) SYNTAX("K-card in event");
882
+ if( p->zWikiTitle!=0 ) SYNTAX("L-card in event");
883
+ if( p->zWiki==0 ) SYNTAX("W-card in event");
884
+ if( p->zAttachName ) SYNTAX("A-card in event");
862885
for(i=0; i<p->nTag; i++){
863
- if( p->aTag[i].zName[0]!='+' ) goto manifest_syntax_error;
864
- if( p->aTag[i].zUuid!=0 ) goto manifest_syntax_error;
886
+ if( p->aTag[i].zName[0]!='+' ) SYNTAX("propagating tag in event");
887
+ if( p->aTag[i].zUuid!=0 ) SYNTAX("non-self-referential tag in event");
865888
}
866
- if( !seenZ ) goto manifest_syntax_error;
889
+ if( !seenZ ) SYNTAX("Z-card missing in event");
867890
p->type = CFTYPE_EVENT;
868891
}else if( p->zWiki!=0 ){
869
- if( p->rDate<=0.0 ) goto manifest_syntax_error;
870
- if( p->nCChild>0 ) goto manifest_syntax_error;
871
- if( p->nTag>0 ) goto manifest_syntax_error;
872
- if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
873
- if( p->zWikiTitle==0 ) goto manifest_syntax_error;
874
- if( p->zAttachName ) goto manifest_syntax_error;
875
- if( !seenZ ) goto manifest_syntax_error;
892
+ if( p->rDate<=0.0 ) SYNTAX("date missing on wiki");
893
+ if( p->nCChild>0 ) SYNTAX("M-card in wiki");
894
+ if( p->nTag>0 ) SYNTAX("T-card in wiki");
895
+ if( p->zTicketUuid!=0 ) SYNTAX("K-card in wiki");
896
+ if( p->zWikiTitle==0 ) SYNTAX("L-card in wiki");
897
+ if( p->zAttachName ) SYNTAX("A-card in wiki");
898
+ if( !seenZ ) SYNTAX("missing Z-card on wiki");
876899
p->type = CFTYPE_WIKI;
877900
}else if( p->nTag>0 ){
878
- if( p->rDate<=0.0 ) goto manifest_syntax_error;
879
- if( p->nParent>0 ) goto manifest_syntax_error;
880
- if( p->zWikiTitle ) goto manifest_syntax_error;
881
- if( p->zTicketUuid ) goto manifest_syntax_error;
882
- if( p->zAttachName ) goto manifest_syntax_error;
883
- if( !seenZ ) goto manifest_syntax_error;
901
+ if( p->rDate<=0.0 ) SYNTAX("date missing on tag");
902
+ if( p->nParent>0 ) SYNTAX("P-card on tag");
903
+ if( p->zWikiTitle ) SYNTAX("L-card on tag");
904
+ if( p->zTicketUuid ) SYNTAX("K-card in tag");
905
+ if( p->zAttachName ) SYNTAX("A-card in tag");
906
+ if( !seenZ ) SYNTAX("missing Z-card on tag");
884907
p->type = CFTYPE_CONTROL;
885908
}else if( p->zAttachName ){
886
- if( p->nCChild>0 ) goto manifest_syntax_error;
887
- if( p->rDate<=0.0 ) goto manifest_syntax_error;
888
- if( p->zTicketUuid ) goto manifest_syntax_error;
889
- if( p->zWikiTitle ) goto manifest_syntax_error;
890
- if( !seenZ ) goto manifest_syntax_error;
909
+ if( p->nCChild>0 ) SYNTAX("M-card in attachment");
910
+ if( p->rDate<=0.0 ) SYNTAX("missing date in attachment");
911
+ if( p->zTicketUuid ) SYNTAX("K-card in attachment");
912
+ if( p->zWikiTitle ) SYNTAX("L-card in attachment");
913
+ if( !seenZ ) SYNTAX("missing Z-card on attachment");
891914
p->type = CFTYPE_ATTACHMENT;
892915
}else{
893
- if( p->nCChild>0 ) goto manifest_syntax_error;
894
- if( p->rDate<=0.0 ) goto manifest_syntax_error;
895
- if( p->nField>0 ) goto manifest_syntax_error;
896
- if( p->zTicketUuid ) goto manifest_syntax_error;
897
- if( p->zWikiTitle ) goto manifest_syntax_error;
898
- if( p->zTicketUuid ) goto manifest_syntax_error;
916
+ if( p->nCChild>0 ) SYNTAX("M-card in check-in");
917
+ if( p->rDate<=0.0 ) SYNTAX("missing date in check-in");
918
+ if( p->nField>0 ) SYNTAX("J-card in check-in");
919
+ if( p->zTicketUuid ) SYNTAX("K-card in check-in");
920
+ if( p->zWikiTitle ) SYNTAX("L-card in check-in");
899921
p->type = CFTYPE_MANIFEST;
900922
}
901923
md5sum_init();
902924
if( !isRepeat ) g.parseCnt[p->type]++;
903925
return p;
904926
905927
manifest_syntax_error:
906
- /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
928
+ if( zErr ){
929
+ blob_appendf(pErr, "line %d: %s", lineNo, zErr);
930
+ }else{
931
+ blob_appendf(pErr, "unknown error on line %d", lineNo);
932
+ }
907933
md5sum_init();
908934
manifest_destroy(p);
909935
return 0;
910936
}
911937
@@ -924,11 +950,11 @@
924950
p = 0;
925951
}
926952
return p;
927953
}
928954
content_get(rid, &content);
929
- p = manifest_parse(&content, rid);
955
+ p = manifest_parse(&content, rid, 0);
930956
if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
931957
manifest_destroy(p);
932958
p = 0;
933959
}
934960
return p;
@@ -972,13 +998,16 @@
972998
}
973999
blob_read_from_file(&b, g.argv[2]);
9741000
if( g.argc>3 ) n = atoi(g.argv[3]);
9751001
for(i=0; i<n; i++){
9761002
Blob b2;
1003
+ Blob err;
9771004
blob_copy(&b2, &b);
978
- p = manifest_parse(&b2, 0);
979
- if( p==0 ) fossil_print("FAILED!\n");
1005
+ blob_zero(&err);
1006
+ p = manifest_parse(&b2, 0, &err);
1007
+ if( p==0 ) fossil_print("ERROR: %s\n", blob_str(&err));
1008
+ blob_reset(&err);
9801009
manifest_destroy(p);
9811010
}
9821011
}
9831012
9841013
/*
@@ -1299,11 +1328,11 @@
12991328
otherRid = cid;
13001329
}
13011330
if( (*ppOther = manifest_cache_find(otherRid))==0 ){
13021331
content_get(otherRid, &otherContent);
13031332
if( blob_size(&otherContent)==0 ) return;
1304
- *ppOther = manifest_parse(&otherContent, otherRid);
1333
+ *ppOther = manifest_parse(&otherContent, otherRid, 0);
13051334
if( *ppOther==0 ) return;
13061335
}
13071336
if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
13081337
manifest_destroy(*ppOther);
13091338
return;
@@ -1597,11 +1626,11 @@
15971626
Stmt q;
15981627
int parentid = 0;
15991628
16001629
if( (p = manifest_cache_find(rid))!=0 ){
16011630
blob_reset(pContent);
1602
- }else if( (p = manifest_parse(pContent, rid))==0 ){
1631
+ }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
16031632
assert( blob_is_reset(pContent) || pContent==0 );
16041633
return 0;
16051634
}
16061635
if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
16071636
manifest_destroy(p);
16081637
--- src/manifest.c
+++ src/manifest.c
@@ -307,10 +307,15 @@
307 c = 0;
308 }
309 return c;
310 }
311
 
 
 
 
 
312 /*
313 ** Parse a blob into a Manifest object. The Manifest object
314 ** takes over the input blob and will free it when the
315 ** Manifest object is freed. Zeros are inserted into the blob
316 ** as string terminators so that blob should not be used again.
@@ -334,11 +339,11 @@
334 ** Each card is divided into tokens by a single space character.
335 ** The first token is a single upper-case letter which is the card type.
336 ** The card type determines the other parameters to the card.
337 ** Cards must occur in lexicographical order.
338 */
339 Manifest *manifest_parse(Blob *pContent, int rid){
340 Manifest *p;
341 int seenZ = 0;
342 int i, lineNo=0;
343 ManifestText x;
344 char cPrevType = 0;
@@ -347,10 +352,11 @@
347 int n;
348 char *zUuid;
349 int sz = 0;
350 int isRepeat;
351 static Bag seen;
 
352
353 if( rid==0 ){
354 isRepeat = 1;
355 }else if( bag_find(&seen, rid) ){
356 isRepeat = 1;
@@ -365,27 +371,30 @@
365 if( !isRepeat ) g.parseCnt[0]++;
366 z = blob_materialize(pContent);
367 n = blob_size(pContent);
368 if( n<=0 || z[n-1]!='\n' ){
369 blob_reset(pContent);
 
370 return 0;
371 }
372
373 /* Strip off the PGP signature if there is one. Then verify the
374 ** Z-card.
375 */
376 remove_pgp_signature(&z, &n);
377 if( verify_z_card(z, n)==0 ){
378 blob_reset(pContent);
 
379 return 0;
380 }
381
382 /* Verify that the first few characters of the artifact look like
383 ** a control artifact.
384 */
385 if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
386 blob_reset(pContent);
 
387 return 0;
388 }
389
390 /* Allocate a Manifest object to hold the parsed control artifact.
391 */
@@ -420,19 +429,19 @@
420 zSrc = next_token(&x, &nSrc);
421 if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
422 if( p->zAttachName!=0 ) goto manifest_syntax_error;
423 defossilize(zName);
424 if( !file_is_simple_pathname(zName) ){
425 goto manifest_syntax_error;
426 }
427 defossilize(zTarget);
428 if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
429 && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
430 goto manifest_syntax_error;
431 }
432 if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
433 goto manifest_syntax_error;
434 }
435 p->zAttachName = (char*)file_tail(zName);
436 p->zAttachSrc = zSrc;
437 p->zAttachTarget = zTarget;
438 break;
@@ -442,15 +451,16 @@
442 ** B <uuid>
443 **
444 ** A B-line gives the UUID for the baselinen of a delta-manifest.
445 */
446 case 'B': {
447 if( p->zBaseline ) goto manifest_syntax_error;
448 p->zBaseline = next_token(&x, &sz);
449 if( p->zBaseline==0 ) goto manifest_syntax_error;
450 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
451 if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error;
 
452 break;
453 }
454
455
456 /*
@@ -459,13 +469,13 @@
459 ** Comment text is fossil-encoded. There may be no more than
460 ** one C line. C lines are required for manifests and are
461 ** disallowed on all other control files.
462 */
463 case 'C': {
464 if( p->zComment!=0 ) goto manifest_syntax_error;
465 p->zComment = next_token(&x, 0);
466 if( p->zComment==0 ) goto manifest_syntax_error;
467 defossilize(p->zComment);
468 break;
469 }
470
471 /*
@@ -474,13 +484,13 @@
474 ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS
475 ** There can be no more than 1 D line. D lines are required
476 ** for all control files except for clusters.
477 */
478 case 'D': {
479 if( p->rDate>0.0 ) goto manifest_syntax_error;
480 p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0));
481 if( p->rDate<=0.0 ) goto manifest_syntax_error;
482 break;
483 }
484
485 /*
486 ** E <timestamp> <uuid>
@@ -490,16 +500,17 @@
490 ** The event timestamp is distinct from the D timestamp. The D
491 ** timestamp is when the artifact was created whereas the E timestamp
492 ** is when the specific event is said to occur.
493 */
494 case 'E': {
495 if( p->rEventDate>0.0 ) goto manifest_syntax_error;
496 p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
497 if( p->rEventDate<=0.0 ) goto manifest_syntax_error;
498 p->zEventId = next_token(&x, &sz);
499 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
500 if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error;
 
501 break;
502 }
503
504 /*
505 ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
@@ -509,26 +520,26 @@
509 ** other control file. The filename and old-name are fossil-encoded.
510 */
511 case 'F': {
512 char *zName, *zPerm, *zPriorName;
513 zName = next_token(&x,0);
514 if( zName==0 ) goto manifest_syntax_error;
515 defossilize(zName);
516 if( !file_is_simple_pathname(zName) ){
517 goto manifest_syntax_error;
518 }
519 zUuid = next_token(&x, &sz);
520 if( p->zBaseline==0 || zUuid!=0 ){
521 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
522 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
523 }
524 zPerm = next_token(&x,0);
525 zPriorName = next_token(&x,0);
526 if( zPriorName ){
527 defossilize(zPriorName);
528 if( !file_is_simple_pathname(zPriorName) ){
529 goto manifest_syntax_error;
530 }
531 }
532 if( p->nFile>=p->nFileAlloc ){
533 p->nFileAlloc = p->nFileAlloc*2 + 10;
534 p->aFile = fossil_realloc(p->aFile,
@@ -538,11 +549,11 @@
538 p->aFile[i].zName = zName;
539 p->aFile[i].zUuid = zUuid;
540 p->aFile[i].zPerm = zPerm;
541 p->aFile[i].zPrior = zPriorName;
542 if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
543 goto manifest_syntax_error;
544 }
545 break;
546 }
547
548 /*
@@ -555,11 +566,11 @@
555 */
556 case 'J': {
557 char *zName, *zValue;
558 zName = next_token(&x,0);
559 zValue = next_token(&x,0);
560 if( zName==0 ) goto manifest_syntax_error;
561 if( zValue==0 ) zValue = "";
562 defossilize(zValue);
563 if( p->nField>=p->nFieldAlloc ){
564 p->nFieldAlloc = p->nFieldAlloc*2 + 10;
565 p->aField = fossil_realloc(p->aField,
@@ -567,11 +578,11 @@
567 }
568 i = p->nField++;
569 p->aField[i].zName = zName;
570 p->aField[i].zValue = zValue;
571 if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
572 goto manifest_syntax_error;
573 }
574 break;
575 }
576
577
@@ -580,14 +591,16 @@
580 **
581 ** A K-line gives the UUID for the ticket which this control file
582 ** is amending.
583 */
584 case 'K': {
585 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
586 p->zTicketUuid = next_token(&x, &sz);
587 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
588 if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error;
 
 
589 break;
590 }
591
592 /*
593 ** L <wikititle>
@@ -594,16 +607,16 @@
594 **
595 ** The wiki page title is fossil-encoded. There may be no more than
596 ** one L line.
597 */
598 case 'L': {
599 if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
600 p->zWikiTitle = next_token(&x,0);
601 if( p->zWikiTitle==0 ) goto manifest_syntax_error;
602 defossilize(p->zWikiTitle);
603 if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
604 goto manifest_syntax_error;
605 }
606 break;
607 }
608
609 /*
@@ -612,22 +625,22 @@
612 ** An M-line identifies another artifact by its UUID. M-lines
613 ** occur in clusters only.
614 */
615 case 'M': {
616 zUuid = next_token(&x, &sz);
617 if( zUuid==0 ) goto manifest_syntax_error;
618 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
619 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
620 if( p->nCChild>=p->nCChildAlloc ){
621 p->nCChildAlloc = p->nCChildAlloc*2 + 10;
622 p->azCChild = fossil_realloc(p->azCChild
623 , p->nCChildAlloc*sizeof(p->azCChild[0]) );
624 }
625 i = p->nCChild++;
626 p->azCChild[i] = zUuid;
627 if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){
628 goto manifest_syntax_error;
629 }
630 break;
631 }
632
633 /*
@@ -637,12 +650,12 @@
637 ** this artifact. The first parent is the primary parent. All
638 ** others are parents by merge.
639 */
640 case 'P': {
641 while( (zUuid = next_token(&x, &sz))!=0 ){
642 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
643 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
644 if( p->nParent>=p->nParentAlloc ){
645 p->nParentAlloc = p->nParentAlloc*2 + 5;
646 p->azParent = fossil_realloc(p->azParent,
647 p->nParentAlloc*sizeof(char*));
648 }
@@ -657,23 +670,29 @@
657 **
658 ** Specify one or a range of checkins that are cherrypicked into
659 ** this checkin ("+") or backed out of this checkin ("-").
660 */
661 case 'Q': {
662 if( (zUuid = next_token(&x, &sz))==0 ) goto manifest_syntax_error;
663 if( sz!=UUID_SIZE+1 ) goto manifest_syntax_error;
664 if( zUuid[0]!='+' && zUuid[0]!='-' ) goto manifest_syntax_error;
665 if( !validate16(&zUuid[1], UUID_SIZE) ) goto manifest_syntax_error;
 
 
 
 
666 n = p->nCherrypick;
667 p->nCherrypick++;
668 p->aCherrypick = fossil_realloc(p->aCherrypick,
669 p->nCherrypick*sizeof(p->aCherrypick[0]));
670 p->aCherrypick[n].zCPTarget = zUuid;
671 p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz);
672 if( zUuid ){
673 if( sz!=UUID_SIZE ) goto manifest_syntax_error;
674 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
 
 
675 }
676 break;
677 }
678
679 /*
@@ -681,14 +700,14 @@
681 **
682 ** Specify the MD5 checksum over the name and content of all files
683 ** in the manifest.
684 */
685 case 'R': {
686 if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
687 p->zRepoCksum = next_token(&x, &sz);
688 if( sz!=32 ) goto manifest_syntax_error;
689 if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
690 break;
691 }
692
693 /*
694 ** T (+|*|-)<tagname> <uuid> ?<value>?
@@ -706,29 +725,29 @@
706 ** Tags are not allowed in clusters. Multiple T lines are allowed.
707 */
708 case 'T': {
709 char *zName, *zValue;
710 zName = next_token(&x, 0);
711 if( zName==0 ) goto manifest_syntax_error;
712 zUuid = next_token(&x, &sz);
713 if( zUuid==0 ) goto manifest_syntax_error;
714 zValue = next_token(&x, 0);
715 if( zValue ) defossilize(zValue);
716 if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
717 /* A valid uuid */
718 }else if( sz==1 && zUuid[0]=='*' ){
719 zUuid = 0;
720 }else{
721 goto manifest_syntax_error;
722 }
723 defossilize(zName);
724 if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
725 goto manifest_syntax_error;
726 }
727 if( validate16(&zName[1], strlen(&zName[1])) ){
728 /* Do not allow tags whose names look like UUIDs */
729 goto manifest_syntax_error;
730 }
731 if( p->nTag>=p->nTagAlloc ){
732 p->nTagAlloc = p->nTagAlloc*2 + 10;
733 p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) );
734 }
@@ -735,11 +754,11 @@
735 i = p->nTag++;
736 p->aTag[i].zName = zName;
737 p->aTag[i].zUuid = zUuid;
738 p->aTag[i].zValue = zValue;
739 if( i>0 && fossil_strcmp(p->aTag[i-1].zName, zName)>=0 ){
740 goto manifest_syntax_error;
741 }
742 break;
743 }
744
745 /*
@@ -748,11 +767,11 @@
748 ** Identify the user who created this control file by their
749 ** login. Only one U line is allowed. Prohibited in clusters.
750 ** If the user name is omitted, take that to be "anonymous".
751 */
752 case 'U': {
753 if( p->zUser!=0 ) goto manifest_syntax_error;
754 p->zUser = next_token(&x, 0);
755 if( p->zUser==0 ){
756 p->zUser = "anonymous";
757 }else{
758 defossilize(p->zUser);
@@ -767,25 +786,26 @@
767 ** page. There is always an extra \n before the start of the next
768 ** record.
769 */
770 case 'W': {
771 char *zSize;
772 int size, c;
773 Blob wiki;
774 zSize = next_token(&x, 0);
775 if( zSize==0 ) goto manifest_syntax_error;
776 if( x.atEol==0 ) goto manifest_syntax_error;
777 for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
778 size = size*10 + c - '0';
 
 
779 }
780 if( size<0 ) goto manifest_syntax_error;
781 if( p->zWiki!=0 ) goto manifest_syntax_error;
782 blob_zero(&wiki);
783 if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error;
784 p->zWiki = x.z;
785 x.z += size;
786 if( x.z[0]!='\n' ) goto manifest_syntax_error;
787 x.z[0] = 0;
788 x.z++;
789 break;
790 }
791
@@ -801,111 +821,117 @@
801 ** Manifest. It is not required for manifest only for historical
802 ** compatibility reasons.
803 */
804 case 'Z': {
805 zUuid = next_token(&x, &sz);
806 if( sz!=32 ) goto manifest_syntax_error;
807 if( !validate16(zUuid, 32) ) goto manifest_syntax_error;
808 seenZ = 1;
809 break;
810 }
811 default: {
812 goto manifest_syntax_error;
813 }
814 }
815 }
816 if( x.z<x.zEnd ) goto manifest_syntax_error;
817
818 if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
819 if( p->nCChild>0 ) goto manifest_syntax_error;
820 if( p->rDate<=0.0 ) goto manifest_syntax_error;
821 if( p->nField>0 ) goto manifest_syntax_error;
822 if( p->zTicketUuid ) goto manifest_syntax_error;
823 if( p->zWiki ) goto manifest_syntax_error;
824 if( p->zWikiTitle ) goto manifest_syntax_error;
825 if( p->zEventId ) goto manifest_syntax_error;
826 if( p->zTicketUuid ) goto manifest_syntax_error;
827 if( p->zAttachName ) goto manifest_syntax_error;
828 p->type = CFTYPE_MANIFEST;
829 }else if( p->nCChild>0 ){
830 if( p->rDate>0.0 ) goto manifest_syntax_error;
831 if( p->zComment!=0 ) goto manifest_syntax_error;
832 if( p->zUser!=0 ) goto manifest_syntax_error;
833 if( p->nTag>0 ) goto manifest_syntax_error;
834 if( p->nParent>0 ) goto manifest_syntax_error;
835 if( p->nField>0 ) goto manifest_syntax_error;
836 if( p->zTicketUuid ) goto manifest_syntax_error;
837 if( p->zWiki ) goto manifest_syntax_error;
838 if( p->zWikiTitle ) goto manifest_syntax_error;
839 if( p->zEventId ) goto manifest_syntax_error;
840 if( p->zAttachName ) goto manifest_syntax_error;
841 if( !seenZ ) goto manifest_syntax_error;
 
 
 
842 p->type = CFTYPE_CLUSTER;
843 }else if( p->nField>0 ){
844 if( p->rDate<=0.0 ) goto manifest_syntax_error;
845 if( p->zWiki ) goto manifest_syntax_error;
846 if( p->zWikiTitle ) goto manifest_syntax_error;
847 if( p->zEventId ) goto manifest_syntax_error;
848 if( p->nCChild>0 ) goto manifest_syntax_error;
849 if( p->nTag>0 ) goto manifest_syntax_error;
850 if( p->zTicketUuid==0 ) goto manifest_syntax_error;
851 if( p->zUser==0 ) goto manifest_syntax_error;
852 if( p->zAttachName ) goto manifest_syntax_error;
853 if( !seenZ ) goto manifest_syntax_error;
854 p->type = CFTYPE_TICKET;
855 }else if( p->zEventId ){
856 if( p->rDate<=0.0 ) goto manifest_syntax_error;
857 if( p->nCChild>0 ) goto manifest_syntax_error;
858 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
859 if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
860 if( p->zWiki==0 ) goto manifest_syntax_error;
861 if( p->zAttachName ) goto manifest_syntax_error;
862 for(i=0; i<p->nTag; i++){
863 if( p->aTag[i].zName[0]!='+' ) goto manifest_syntax_error;
864 if( p->aTag[i].zUuid!=0 ) goto manifest_syntax_error;
865 }
866 if( !seenZ ) goto manifest_syntax_error;
867 p->type = CFTYPE_EVENT;
868 }else if( p->zWiki!=0 ){
869 if( p->rDate<=0.0 ) goto manifest_syntax_error;
870 if( p->nCChild>0 ) goto manifest_syntax_error;
871 if( p->nTag>0 ) goto manifest_syntax_error;
872 if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
873 if( p->zWikiTitle==0 ) goto manifest_syntax_error;
874 if( p->zAttachName ) goto manifest_syntax_error;
875 if( !seenZ ) goto manifest_syntax_error;
876 p->type = CFTYPE_WIKI;
877 }else if( p->nTag>0 ){
878 if( p->rDate<=0.0 ) goto manifest_syntax_error;
879 if( p->nParent>0 ) goto manifest_syntax_error;
880 if( p->zWikiTitle ) goto manifest_syntax_error;
881 if( p->zTicketUuid ) goto manifest_syntax_error;
882 if( p->zAttachName ) goto manifest_syntax_error;
883 if( !seenZ ) goto manifest_syntax_error;
884 p->type = CFTYPE_CONTROL;
885 }else if( p->zAttachName ){
886 if( p->nCChild>0 ) goto manifest_syntax_error;
887 if( p->rDate<=0.0 ) goto manifest_syntax_error;
888 if( p->zTicketUuid ) goto manifest_syntax_error;
889 if( p->zWikiTitle ) goto manifest_syntax_error;
890 if( !seenZ ) goto manifest_syntax_error;
891 p->type = CFTYPE_ATTACHMENT;
892 }else{
893 if( p->nCChild>0 ) goto manifest_syntax_error;
894 if( p->rDate<=0.0 ) goto manifest_syntax_error;
895 if( p->nField>0 ) goto manifest_syntax_error;
896 if( p->zTicketUuid ) goto manifest_syntax_error;
897 if( p->zWikiTitle ) goto manifest_syntax_error;
898 if( p->zTicketUuid ) goto manifest_syntax_error;
899 p->type = CFTYPE_MANIFEST;
900 }
901 md5sum_init();
902 if( !isRepeat ) g.parseCnt[p->type]++;
903 return p;
904
905 manifest_syntax_error:
906 /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
 
 
 
 
907 md5sum_init();
908 manifest_destroy(p);
909 return 0;
910 }
911
@@ -924,11 +950,11 @@
924 p = 0;
925 }
926 return p;
927 }
928 content_get(rid, &content);
929 p = manifest_parse(&content, rid);
930 if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
931 manifest_destroy(p);
932 p = 0;
933 }
934 return p;
@@ -972,13 +998,16 @@
972 }
973 blob_read_from_file(&b, g.argv[2]);
974 if( g.argc>3 ) n = atoi(g.argv[3]);
975 for(i=0; i<n; i++){
976 Blob b2;
 
977 blob_copy(&b2, &b);
978 p = manifest_parse(&b2, 0);
979 if( p==0 ) fossil_print("FAILED!\n");
 
 
980 manifest_destroy(p);
981 }
982 }
983
984 /*
@@ -1299,11 +1328,11 @@
1299 otherRid = cid;
1300 }
1301 if( (*ppOther = manifest_cache_find(otherRid))==0 ){
1302 content_get(otherRid, &otherContent);
1303 if( blob_size(&otherContent)==0 ) return;
1304 *ppOther = manifest_parse(&otherContent, otherRid);
1305 if( *ppOther==0 ) return;
1306 }
1307 if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
1308 manifest_destroy(*ppOther);
1309 return;
@@ -1597,11 +1626,11 @@
1597 Stmt q;
1598 int parentid = 0;
1599
1600 if( (p = manifest_cache_find(rid))!=0 ){
1601 blob_reset(pContent);
1602 }else if( (p = manifest_parse(pContent, rid))==0 ){
1603 assert( blob_is_reset(pContent) || pContent==0 );
1604 return 0;
1605 }
1606 if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
1607 manifest_destroy(p);
1608
--- src/manifest.c
+++ src/manifest.c
@@ -307,10 +307,15 @@
307 c = 0;
308 }
309 return c;
310 }
311
312 /*
313 ** Shorthand for a control-artifact parsing error
314 */
315 #define SYNTAX(T) {zErr=(T); goto manifest_syntax_error;}
316
317 /*
318 ** Parse a blob into a Manifest object. The Manifest object
319 ** takes over the input blob and will free it when the
320 ** Manifest object is freed. Zeros are inserted into the blob
321 ** as string terminators so that blob should not be used again.
@@ -334,11 +339,11 @@
339 ** Each card is divided into tokens by a single space character.
340 ** The first token is a single upper-case letter which is the card type.
341 ** The card type determines the other parameters to the card.
342 ** Cards must occur in lexicographical order.
343 */
344 Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){
345 Manifest *p;
346 int seenZ = 0;
347 int i, lineNo=0;
348 ManifestText x;
349 char cPrevType = 0;
@@ -347,10 +352,11 @@
352 int n;
353 char *zUuid;
354 int sz = 0;
355 int isRepeat;
356 static Bag seen;
357 const char *zErr = 0;
358
359 if( rid==0 ){
360 isRepeat = 1;
361 }else if( bag_find(&seen, rid) ){
362 isRepeat = 1;
@@ -365,27 +371,30 @@
371 if( !isRepeat ) g.parseCnt[0]++;
372 z = blob_materialize(pContent);
373 n = blob_size(pContent);
374 if( n<=0 || z[n-1]!='\n' ){
375 blob_reset(pContent);
376 blob_appendf(pErr, n ? "not terminated with \\n" : "zero-length");
377 return 0;
378 }
379
380 /* Strip off the PGP signature if there is one. Then verify the
381 ** Z-card.
382 */
383 remove_pgp_signature(&z, &n);
384 if( verify_z_card(z, n)==2 ){
385 blob_reset(pContent);
386 blob_appendf(pErr, "incorrect Z-card cksum");
387 return 0;
388 }
389
390 /* Verify that the first few characters of the artifact look like
391 ** a control artifact.
392 */
393 if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
394 blob_reset(pContent);
395 blob_appendf(pErr, "line 1 not recognized");
396 return 0;
397 }
398
399 /* Allocate a Manifest object to hold the parsed control artifact.
400 */
@@ -420,19 +429,19 @@
429 zSrc = next_token(&x, &nSrc);
430 if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
431 if( p->zAttachName!=0 ) goto manifest_syntax_error;
432 defossilize(zName);
433 if( !file_is_simple_pathname(zName) ){
434 SYNTAX("invalid filename on A-card");
435 }
436 defossilize(zTarget);
437 if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
438 && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
439 SYNTAX("invalid target on A-card");
440 }
441 if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
442 SYNTAX("invalid source on A-card");
443 }
444 p->zAttachName = (char*)file_tail(zName);
445 p->zAttachSrc = zSrc;
446 p->zAttachTarget = zTarget;
447 break;
@@ -442,15 +451,16 @@
451 ** B <uuid>
452 **
453 ** A B-line gives the UUID for the baselinen of a delta-manifest.
454 */
455 case 'B': {
456 if( p->zBaseline ) SYNTAX("more than one B-card");
457 p->zBaseline = next_token(&x, &sz);
458 if( p->zBaseline==0 ) SYNTAX("missing UUID on B-card");
459 if( sz!=UUID_SIZE || !validate16(p->zBaseline, UUID_SIZE) ){
460 SYNTAX("invalid UUID on B-card");
461 }
462 break;
463 }
464
465
466 /*
@@ -459,13 +469,13 @@
469 ** Comment text is fossil-encoded. There may be no more than
470 ** one C line. C lines are required for manifests and are
471 ** disallowed on all other control files.
472 */
473 case 'C': {
474 if( p->zComment!=0 ) SYNTAX("more than one C-card");
475 p->zComment = next_token(&x, 0);
476 if( p->zComment==0 ) SYNTAX("missing comment text on C-card");
477 defossilize(p->zComment);
478 break;
479 }
480
481 /*
@@ -474,13 +484,13 @@
484 ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS
485 ** There can be no more than 1 D line. D lines are required
486 ** for all control files except for clusters.
487 */
488 case 'D': {
489 if( p->rDate>0.0 ) SYNTAX("more than one D-card");
490 p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0));
491 if( p->rDate<=0.0 ) SYNTAX("cannot parse date on D-card");
492 break;
493 }
494
495 /*
496 ** E <timestamp> <uuid>
@@ -490,16 +500,17 @@
500 ** The event timestamp is distinct from the D timestamp. The D
501 ** timestamp is when the artifact was created whereas the E timestamp
502 ** is when the specific event is said to occur.
503 */
504 case 'E': {
505 if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
506 p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
507 if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
508 p->zEventId = next_token(&x, &sz);
509 if( sz!=UUID_SIZE || !validate16(p->zEventId, UUID_SIZE) ){
510 SYNTAX("malformed UUID on E-card");
511 }
512 break;
513 }
514
515 /*
516 ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
@@ -509,26 +520,26 @@
520 ** other control file. The filename and old-name are fossil-encoded.
521 */
522 case 'F': {
523 char *zName, *zPerm, *zPriorName;
524 zName = next_token(&x,0);
525 if( zName==0 ) SYNTAX("missing filename on F-card");
526 defossilize(zName);
527 if( !file_is_simple_pathname(zName) ){
528 SYNTAX("F-card filename is not a simple path");
529 }
530 zUuid = next_token(&x, &sz);
531 if( p->zBaseline==0 || zUuid!=0 ){
532 if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size");
533 if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("F-card UUID invalid");
534 }
535 zPerm = next_token(&x,0);
536 zPriorName = next_token(&x,0);
537 if( zPriorName ){
538 defossilize(zPriorName);
539 if( !file_is_simple_pathname(zPriorName) ){
540 SYNTAX("F-card old filename is not a simple path");
541 }
542 }
543 if( p->nFile>=p->nFileAlloc ){
544 p->nFileAlloc = p->nFileAlloc*2 + 10;
545 p->aFile = fossil_realloc(p->aFile,
@@ -538,11 +549,11 @@
549 p->aFile[i].zName = zName;
550 p->aFile[i].zUuid = zUuid;
551 p->aFile[i].zPerm = zPerm;
552 p->aFile[i].zPrior = zPriorName;
553 if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
554 SYNTAX("incorrect F-card sort order");
555 }
556 break;
557 }
558
559 /*
@@ -555,11 +566,11 @@
566 */
567 case 'J': {
568 char *zName, *zValue;
569 zName = next_token(&x,0);
570 zValue = next_token(&x,0);
571 if( zName==0 ) SYNTAX("name missing from J-card");
572 if( zValue==0 ) zValue = "";
573 defossilize(zValue);
574 if( p->nField>=p->nFieldAlloc ){
575 p->nFieldAlloc = p->nFieldAlloc*2 + 10;
576 p->aField = fossil_realloc(p->aField,
@@ -567,11 +578,11 @@
578 }
579 i = p->nField++;
580 p->aField[i].zName = zName;
581 p->aField[i].zValue = zValue;
582 if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
583 SYNTAX("incorrect J-card sort order");
584 }
585 break;
586 }
587
588
@@ -580,14 +591,16 @@
591 **
592 ** A K-line gives the UUID for the ticket which this control file
593 ** is amending.
594 */
595 case 'K': {
596 if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card");
597 p->zTicketUuid = next_token(&x, &sz);
598 if( sz!=UUID_SIZE ) SYNTAX("K-card UUID is the wrong size");
599 if( !validate16(p->zTicketUuid, UUID_SIZE) ){
600 SYNTAX("invalid K-card UUID");
601 }
602 break;
603 }
604
605 /*
606 ** L <wikititle>
@@ -594,16 +607,16 @@
607 **
608 ** The wiki page title is fossil-encoded. There may be no more than
609 ** one L line.
610 */
611 case 'L': {
612 if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card");
613 p->zWikiTitle = next_token(&x,0);
614 if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card");
615 defossilize(p->zWikiTitle);
616 if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
617 SYNTAX("L-card has malformed wiki name");
618 }
619 break;
620 }
621
622 /*
@@ -612,22 +625,22 @@
625 ** An M-line identifies another artifact by its UUID. M-lines
626 ** occur in clusters only.
627 */
628 case 'M': {
629 zUuid = next_token(&x, &sz);
630 if( zUuid==0 ) SYNTAX("missing UUID on M-card");
631 if( sz!=UUID_SIZE ) SYNTAX("wrong size for UUID on M-card");
632 if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("UUID invalid on M-card");
633 if( p->nCChild>=p->nCChildAlloc ){
634 p->nCChildAlloc = p->nCChildAlloc*2 + 10;
635 p->azCChild = fossil_realloc(p->azCChild
636 , p->nCChildAlloc*sizeof(p->azCChild[0]) );
637 }
638 i = p->nCChild++;
639 p->azCChild[i] = zUuid;
640 if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){
641 SYNTAX("M-card in the wrong order");
642 }
643 break;
644 }
645
646 /*
@@ -637,12 +650,12 @@
650 ** this artifact. The first parent is the primary parent. All
651 ** others are parents by merge.
652 */
653 case 'P': {
654 while( (zUuid = next_token(&x, &sz))!=0 ){
655 if( sz!=UUID_SIZE ) SYNTAX("wrong size UUID on P-card");
656 if( !validate16(zUuid, UUID_SIZE) )SYNTAX("invalid UUID on P-card");
657 if( p->nParent>=p->nParentAlloc ){
658 p->nParentAlloc = p->nParentAlloc*2 + 5;
659 p->azParent = fossil_realloc(p->azParent,
660 p->nParentAlloc*sizeof(char*));
661 }
@@ -657,23 +670,29 @@
670 **
671 ** Specify one or a range of checkins that are cherrypicked into
672 ** this checkin ("+") or backed out of this checkin ("-").
673 */
674 case 'Q': {
675 if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing UUID on Q-card");
676 if( sz!=UUID_SIZE+1 ) SYNTAX("wrong size UUID on Q-card");
677 if( zUuid[0]!='+' && zUuid[0]!='-' ){
678 SYNTAX("Q-card does not begin with '+' or '-'");
679 }
680 if( !validate16(&zUuid[1], UUID_SIZE) ){
681 SYNTAX("invalid UUID on Q-card");
682 }
683 n = p->nCherrypick;
684 p->nCherrypick++;
685 p->aCherrypick = fossil_realloc(p->aCherrypick,
686 p->nCherrypick*sizeof(p->aCherrypick[0]));
687 p->aCherrypick[n].zCPTarget = zUuid;
688 p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz);
689 if( zUuid ){
690 if( sz!=UUID_SIZE ) SYNTAX("wrong size second UUID in Q-card");
691 if( !validate16(zUuid, UUID_SIZE) ){
692 SYNTAX("invalid second UUID on Q-card");
693 }
694 }
695 break;
696 }
697
698 /*
@@ -681,14 +700,14 @@
700 **
701 ** Specify the MD5 checksum over the name and content of all files
702 ** in the manifest.
703 */
704 case 'R': {
705 if( p->zRepoCksum!=0 ) SYNTAX("more than on R-card");
706 p->zRepoCksum = next_token(&x, &sz);
707 if( sz!=32 ) SYNTAX("wrong size cksum on R-card");
708 if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum");
709 break;
710 }
711
712 /*
713 ** T (+|*|-)<tagname> <uuid> ?<value>?
@@ -706,29 +725,29 @@
725 ** Tags are not allowed in clusters. Multiple T lines are allowed.
726 */
727 case 'T': {
728 char *zName, *zValue;
729 zName = next_token(&x, 0);
730 if( zName==0 ) SYNTAX("missing name on T-card");
731 zUuid = next_token(&x, &sz);
732 if( zUuid==0 ) SYNTAX("missing UUID on T-card");
733 zValue = next_token(&x, 0);
734 if( zValue ) defossilize(zValue);
735 if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
736 /* A valid uuid */
737 }else if( sz==1 && zUuid[0]=='*' ){
738 zUuid = 0;
739 }else{
740 SYNTAX("malformed UUID on T-card");
741 }
742 defossilize(zName);
743 if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
744 SYNTAX("T-card name does not begin with '-', '+', or '*'");
745 }
746 if( validate16(&zName[1], strlen(&zName[1])) ){
747 /* Do not allow tags whose names look like UUIDs */
748 SYNTAX("T-card name looks like a UUID");
749 }
750 if( p->nTag>=p->nTagAlloc ){
751 p->nTagAlloc = p->nTagAlloc*2 + 10;
752 p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) );
753 }
@@ -735,11 +754,11 @@
754 i = p->nTag++;
755 p->aTag[i].zName = zName;
756 p->aTag[i].zUuid = zUuid;
757 p->aTag[i].zValue = zValue;
758 if( i>0 && fossil_strcmp(p->aTag[i-1].zName, zName)>=0 ){
759 SYNTAX("T-card in the wrong order");
760 }
761 break;
762 }
763
764 /*
@@ -748,11 +767,11 @@
767 ** Identify the user who created this control file by their
768 ** login. Only one U line is allowed. Prohibited in clusters.
769 ** If the user name is omitted, take that to be "anonymous".
770 */
771 case 'U': {
772 if( p->zUser!=0 ) SYNTAX("more than on U-card");
773 p->zUser = next_token(&x, 0);
774 if( p->zUser==0 ){
775 p->zUser = "anonymous";
776 }else{
777 defossilize(p->zUser);
@@ -767,25 +786,26 @@
786 ** page. There is always an extra \n before the start of the next
787 ** record.
788 */
789 case 'W': {
790 char *zSize;
791 unsigned size, oldsize, c;
792 Blob wiki;
793 zSize = next_token(&x, 0);
794 if( zSize==0 ) SYNTAX("missing size on W-card");
795 if( x.atEol==0 ) SYNTAX("no content after W-card");
796 for(oldsize=size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
797 size = oldsize*10 + c - '0';
798 if( size<oldsize ) SYNTAX("size overflow on W-card");
799 oldsize = size;
800 }
801 if( p->zWiki!=0 ) SYNTAX("more than one W-card");
 
802 blob_zero(&wiki);
803 if( (&x.z[size+1])>=x.zEnd )SYNTAX("not enough content after W-card");
804 p->zWiki = x.z;
805 x.z += size;
806 if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated");
807 x.z[0] = 0;
808 x.z++;
809 break;
810 }
811
@@ -801,111 +821,117 @@
821 ** Manifest. It is not required for manifest only for historical
822 ** compatibility reasons.
823 */
824 case 'Z': {
825 zUuid = next_token(&x, &sz);
826 if( sz!=32 ) SYNTAX("wrong size for Z-card cksum");
827 if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum");
828 seenZ = 1;
829 break;
830 }
831 default: {
832 SYNTAX("unrecognized card");
833 }
834 }
835 }
836 if( x.z<x.zEnd ) SYNTAX("card in the wrong order");
837
838 if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
839 if( p->nCChild>0 ) SYNTAX("M-card in check-in");
840 if( p->rDate<=0.0 ) SYNTAX("missing date for check-in");
841 if( p->nField>0 ) SYNTAX("J-card in check-in");
842 if( p->zTicketUuid ) SYNTAX("K-card in check-in");
843 if( p->zWiki ) SYNTAX("W-card in check-in");
844 if( p->zWikiTitle ) SYNTAX("L-card in check-in");
845 if( p->zEventId ) SYNTAX("E-card in check-in");
846 if( p->zTicketUuid ) SYNTAX("K-card in check-in");
847 if( p->zAttachName ) SYNTAX("A-card in check-in");
848 p->type = CFTYPE_MANIFEST;
849 }else if( p->nCChild>0 ){
850 if( p->rDate>0.0
851 || p->zComment!=0
852 || p->zUser!=0
853 || p->nTag>0
854 || p->nParent>0
855 || p->nField>0
856 || p->zTicketUuid
857 || p->zWiki
858 || p->zWikiTitle
859 || p->zEventId
860 || p->zAttachName
861 ){
862 SYNTAX("cluster contains a card other than M- or Z-");
863 }
864 if( !seenZ ) SYNTAX("missing Z-card on cluster");
865 p->type = CFTYPE_CLUSTER;
866 }else if( p->nField>0 ){
867 if( p->rDate<=0.0 ) SYNTAX("missing date for ticket");
868 if( p->zWiki ) SYNTAX("W-card in ticket");
869 if( p->zWikiTitle ) SYNTAX("L-card in ticket");
870 if( p->zEventId ) SYNTAX("E-card in ticket");
871 if( p->nCChild>0 ) SYNTAX("M-card in ticket");
872 if( p->nTag>0 ) SYNTAX("T-card in ticket");
873 if( p->zTicketUuid==0 ) SYNTAX("missing K-card in ticket");
874 if( p->zUser==0 ) SYNTAX("missing U-card in ticket");
875 if( p->zAttachName ) SYNTAX("A-card in ticket");
876 if( !seenZ ) SYNTAX("missing Z-card in ticket");
877 p->type = CFTYPE_TICKET;
878 }else if( p->zEventId ){
879 if( p->rDate<=0.0 ) SYNTAX("missing date for event");
880 if( p->nCChild>0 ) SYNTAX("M-card in event");
881 if( p->zTicketUuid!=0 ) SYNTAX("K-card in event");
882 if( p->zWikiTitle!=0 ) SYNTAX("L-card in event");
883 if( p->zWiki==0 ) SYNTAX("W-card in event");
884 if( p->zAttachName ) SYNTAX("A-card in event");
885 for(i=0; i<p->nTag; i++){
886 if( p->aTag[i].zName[0]!='+' ) SYNTAX("propagating tag in event");
887 if( p->aTag[i].zUuid!=0 ) SYNTAX("non-self-referential tag in event");
888 }
889 if( !seenZ ) SYNTAX("Z-card missing in event");
890 p->type = CFTYPE_EVENT;
891 }else if( p->zWiki!=0 ){
892 if( p->rDate<=0.0 ) SYNTAX("date missing on wiki");
893 if( p->nCChild>0 ) SYNTAX("M-card in wiki");
894 if( p->nTag>0 ) SYNTAX("T-card in wiki");
895 if( p->zTicketUuid!=0 ) SYNTAX("K-card in wiki");
896 if( p->zWikiTitle==0 ) SYNTAX("L-card in wiki");
897 if( p->zAttachName ) SYNTAX("A-card in wiki");
898 if( !seenZ ) SYNTAX("missing Z-card on wiki");
899 p->type = CFTYPE_WIKI;
900 }else if( p->nTag>0 ){
901 if( p->rDate<=0.0 ) SYNTAX("date missing on tag");
902 if( p->nParent>0 ) SYNTAX("P-card on tag");
903 if( p->zWikiTitle ) SYNTAX("L-card on tag");
904 if( p->zTicketUuid ) SYNTAX("K-card in tag");
905 if( p->zAttachName ) SYNTAX("A-card in tag");
906 if( !seenZ ) SYNTAX("missing Z-card on tag");
907 p->type = CFTYPE_CONTROL;
908 }else if( p->zAttachName ){
909 if( p->nCChild>0 ) SYNTAX("M-card in attachment");
910 if( p->rDate<=0.0 ) SYNTAX("missing date in attachment");
911 if( p->zTicketUuid ) SYNTAX("K-card in attachment");
912 if( p->zWikiTitle ) SYNTAX("L-card in attachment");
913 if( !seenZ ) SYNTAX("missing Z-card on attachment");
914 p->type = CFTYPE_ATTACHMENT;
915 }else{
916 if( p->nCChild>0 ) SYNTAX("M-card in check-in");
917 if( p->rDate<=0.0 ) SYNTAX("missing date in check-in");
918 if( p->nField>0 ) SYNTAX("J-card in check-in");
919 if( p->zTicketUuid ) SYNTAX("K-card in check-in");
920 if( p->zWikiTitle ) SYNTAX("L-card in check-in");
 
921 p->type = CFTYPE_MANIFEST;
922 }
923 md5sum_init();
924 if( !isRepeat ) g.parseCnt[p->type]++;
925 return p;
926
927 manifest_syntax_error:
928 if( zErr ){
929 blob_appendf(pErr, "line %d: %s", lineNo, zErr);
930 }else{
931 blob_appendf(pErr, "unknown error on line %d", lineNo);
932 }
933 md5sum_init();
934 manifest_destroy(p);
935 return 0;
936 }
937
@@ -924,11 +950,11 @@
950 p = 0;
951 }
952 return p;
953 }
954 content_get(rid, &content);
955 p = manifest_parse(&content, rid, 0);
956 if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
957 manifest_destroy(p);
958 p = 0;
959 }
960 return p;
@@ -972,13 +998,16 @@
998 }
999 blob_read_from_file(&b, g.argv[2]);
1000 if( g.argc>3 ) n = atoi(g.argv[3]);
1001 for(i=0; i<n; i++){
1002 Blob b2;
1003 Blob err;
1004 blob_copy(&b2, &b);
1005 blob_zero(&err);
1006 p = manifest_parse(&b2, 0, &err);
1007 if( p==0 ) fossil_print("ERROR: %s\n", blob_str(&err));
1008 blob_reset(&err);
1009 manifest_destroy(p);
1010 }
1011 }
1012
1013 /*
@@ -1299,11 +1328,11 @@
1328 otherRid = cid;
1329 }
1330 if( (*ppOther = manifest_cache_find(otherRid))==0 ){
1331 content_get(otherRid, &otherContent);
1332 if( blob_size(&otherContent)==0 ) return;
1333 *ppOther = manifest_parse(&otherContent, otherRid, 0);
1334 if( *ppOther==0 ) return;
1335 }
1336 if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
1337 manifest_destroy(*ppOther);
1338 return;
@@ -1597,11 +1626,11 @@
1626 Stmt q;
1627 int parentid = 0;
1628
1629 if( (p = manifest_cache_find(rid))!=0 ){
1630 blob_reset(pContent);
1631 }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
1632 assert( blob_is_reset(pContent) || pContent==0 );
1633 return 0;
1634 }
1635 if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
1636 manifest_destroy(p);
1637

Keyboard Shortcuts

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