Fossil

Check-in [e1d15514]
Login

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

Overview
Comment:merge from trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wolfgangHelpCmd
Files: files | file ages | folders
SHA1:e1d15514fba78718c82831236a01a513ecef459a
User & Date: wolfgang 2010-10-09 15:39:43
Context
2010-10-09
20:13
show references to commandline on webpages; add httptrace to windows http server check-in: d1d1cd12 user: wolfgang tags: wolfgangHelpCmd
15:39
merge from trunk check-in: e1d15514 user: wolfgang tags: wolfgangHelpCmd
15:34
link the command line help to the gui pages, where appropriate check-in: a5cd7927 user: wolfgang tags: wolfgangHelpCmd
13:04
Add a compile-time option to ignore the control file checksum (for a modest performance increase while parsing control files.) Enhance the test-parse-manifest command to facilitate performance studies. check-in: 71298828 user: drh tags: trunk
2010-10-08
19:55
Rebuild faster by caching the last few manifest parses. check-in: 0a55d162 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/manifest.c.

81
82
83
84
85
86
87












88
89
90
91
92
93
94
..
95
96
97
98
99
100
101



























































102
103
104
105
106
107
108
...
567
568
569
570
571
572
573

574
575

576
577
578
579

580
581
582
583

584
585
586
587
588
589
590
...
684
685
686
687
688
689
690
691
692
693
694
695
696
697


698
699
700
701
702




703
704

705
706
707
708
709
710
711
...
808
809
810
811
812
813
814

815
816
817
818
819
820
821
822
823
824
825
826
827


828
829

830
831
832
833
834
835
836
...
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
....
1025
1026
1027
1028
1029
1030
1031


1032
1033
1034
1035
1036
1037
1038
1039
....
1261
1262
1263
1264
1265
1266
1267



1268

1269
1270
1271
1272
1273
1274
1275
  struct { 
    char *zName;           /* Key or field name */
    char *zValue;          /* Value of the field */
  } *aField;            /* One for each J card */
};
#endif














/*
** Clear the memory allocated in a manifest object
*/
void manifest_clear(Manifest *p){
  blob_reset(&p->content);
  free(p->aFile);
................................................................................
  free(p->azParent);
  free(p->azCChild);
  free(p->aTag);
  free(p->aField);
  memset(p, 0, sizeof(*p));
}




























































/*
** Parse a blob into a Manifest object.  The Manifest object
** takes over the input blob and will free it when the
** Manifest object is freed.  Zeros are inserted into the blob
** as string terminators so that blob should not be used again.
**
** Return TRUE if the content really is a control file of some
................................................................................
      ** line.  This must be the last record.
      **
      ** This card is required for all control file types except for
      ** Manifest.  It is not required for manifest only for historical
      ** compatibility reasons.
      */
      case 'Z': {

        int rc;
        Blob hash;

        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
        if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;

        md5sum_finish(&hash);
        rc = blob_compare(&hash, &a1);
        blob_reset(&hash);
        if( rc!=0 ) goto manifest_syntax_error;

        seenZ = 1;
        break;
      }
      default: {
        goto manifest_syntax_error;
      }
    }
................................................................................
  manifest_clear(p);
  return 0;
}

/*
** COMMAND: test-parse-manifest
**
** Usage: %fossil test-parse-manifest FILENAME
**
** Parse the manifest and discarded.  Use for testing only.
*/
void manifest_test_parse_cmd(void){
  Manifest m;
  Blob b;


  if( g.argc!=3 ){
    usage("FILENAME");
  }
  db_must_be_within_tree();
  blob_read_from_file(&b, g.argv[2]);




  manifest_parse(&m, &b);
  manifest_clear(&m);

}

/*
** Translate a filename into a filename-id (fnid).  Create a new fnid
** if no previously exists.
*/
static int filename_to_fnid(const char *zFilename){
................................................................................
** Deleted files have mlink.fid=0.
** Added files have mlink.pid=0.
** Edited files have both mlink.pid!=0 and mlink.fid!=0
*/
static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
  Manifest other;
  Blob otherContent;

  int i, j;

  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", cid) ){
    return;
  }
  assert( pParent==0 || pChild==0 );
  if( pParent==0 ){
    pParent = &other;
    content_get(pid, &otherContent);
  }else{
    pChild = &other;
    content_get(cid, &otherContent);
  }


  if( blob_size(&otherContent)==0 ) return;
  if( manifest_parse(&other, &otherContent)==0 ) return;

  content_deltify(pid, cid, 0);

  /* Use the iRename fields to find the cross-linkage between
  ** renamed files.  */
  for(j=0; j<pChild->nFile; j++){
    const char *zPrior = pChild->aFile[j].zPrior;
    if( zPrior && zPrior[0] ){
................................................................................
      add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, 
                    pChild->aFile[j].zName, pParent->aFile[rn].zName);
    }else{
      add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
    }
    j++;
  }
  manifest_clear(&other);
}

/*
** True if manifest_crosslink_begin() has been called but
** manifest_crosslink_end() is still pending.
*/
static int manifest_crosslink_busy = 0;
................................................................................
*/
int manifest_crosslink(int rid, Blob *pContent){
  int i;
  Manifest m;
  Stmt q;
  int parentid = 0;



  if( manifest_parse(&m, pContent)==0 ){
    return 0;
  }
  if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){
    manifest_clear(&m);
    return 0;
  }
  db_begin_transaction();
................................................................................
        "VALUES('t',%.17g,%d,%Q,%Q)",
        m.rDate, rid, m.zUser, zComment
      );
      free(zComment);
    }
  }
  db_end_transaction(0);



  manifest_clear(&m);

  return 1;
}

/*
** Given a checkin name, load and parse the manifest for that checkin.
** Throw a fatal error if anything goes wrong.
*/







>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>


>




>




>







 







|






>
>
|




>
>
>
>
|
|
>







 







>








|


|

>
>
|
|
>







 







|







 







>
>
|







 







>
>
>
|
>







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
...
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
...
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
...
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
...
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
....
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
....
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
  struct { 
    char *zName;           /* Key or field name */
    char *zValue;          /* Value of the field */
  } *aField;            /* One for each J card */
};
#endif

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


/*
** Clear the memory allocated in a manifest object
*/
void manifest_clear(Manifest *p){
  blob_reset(&p->content);
  free(p->aFile);
................................................................................
  free(p->azParent);
  free(p->azCChild);
  free(p->aTag);
  free(p->aField);
  memset(p, 0, sizeof(*p));
}

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

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

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

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

/*
** Parse a blob into a Manifest object.  The Manifest object
** takes over the input blob and will free it when the
** Manifest object is freed.  Zeros are inserted into the blob
** as string terminators so that blob should not be used again.
**
** Return TRUE if the content really is a control file of some
................................................................................
      ** line.  This must be the last record.
      **
      ** This card is required for all control file types except for
      ** Manifest.  It is not required for manifest only for historical
      ** compatibility reasons.
      */
      case 'Z': {
#ifndef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
        int rc;
        Blob hash;
#endif
        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
        if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error;
#ifndef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
        md5sum_finish(&hash);
        rc = blob_compare(&hash, &a1);
        blob_reset(&hash);
        if( rc!=0 ) goto manifest_syntax_error;
#endif
        seenZ = 1;
        break;
      }
      default: {
        goto manifest_syntax_error;
      }
    }
................................................................................
  manifest_clear(p);
  return 0;
}

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

/*
** Translate a filename into a filename-id (fnid).  Create a new fnid
** if no previously exists.
*/
static int filename_to_fnid(const char *zFilename){
................................................................................
** Deleted files have mlink.fid=0.
** Added files have mlink.pid=0.
** Edited files have both mlink.pid!=0 and mlink.fid!=0
*/
static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
  Manifest other;
  Blob otherContent;
  int otherRid;
  int i, j;

  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", cid) ){
    return;
  }
  assert( pParent==0 || pChild==0 );
  if( pParent==0 ){
    pParent = &other;
    otherRid = pid;
  }else{
    pChild = &other;
    otherRid = cid;
  }
  if( manifest_cache_find(otherRid, &other)==0 ){
    content_get(otherRid, &otherContent);
    if( blob_size(&otherContent)==0 ) return;
    if( manifest_parse(&other, &otherContent)==0 ) return;
  }
  content_deltify(pid, cid, 0);

  /* Use the iRename fields to find the cross-linkage between
  ** renamed files.  */
  for(j=0; j<pChild->nFile; j++){
    const char *zPrior = pChild->aFile[j].zPrior;
    if( zPrior && zPrior[0] ){
................................................................................
      add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, 
                    pChild->aFile[j].zName, pParent->aFile[rn].zName);
    }else{
      add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
    }
    j++;
  }
  manifest_cache_insert(otherRid, &other);
}

/*
** True if manifest_crosslink_begin() has been called but
** manifest_crosslink_end() is still pending.
*/
static int manifest_crosslink_busy = 0;
................................................................................
*/
int manifest_crosslink(int rid, Blob *pContent){
  int i;
  Manifest m;
  Stmt q;
  int parentid = 0;

  if( manifest_cache_find(rid, &m) ){
    blob_reset(pContent);
  }else if( manifest_parse(&m, pContent)==0 ){
    return 0;
  }
  if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){
    manifest_clear(&m);
    return 0;
  }
  db_begin_transaction();
................................................................................
        "VALUES('t',%.17g,%d,%Q,%Q)",
        m.rDate, rid, m.zUser, zComment
      );
      free(zComment);
    }
  }
  db_end_transaction(0);
  if( m.type==CFTYPE_MANIFEST ){
    manifest_cache_insert(rid, &m);
  }else{
    manifest_clear(&m);
  }
  return 1;
}

/*
** Given a checkin name, load and parse the manifest for that checkin.
** Throw a fatal error if anything goes wrong.
*/