Fossil SCM

Add support for unversioned files stored in the repository.

drh 2016-08-17 15:42 trunk merge
Commit 27d743e7083578210e7284300682d2334f221886
+28
--- src/blob.c
+++ src/blob.c
@@ -653,19 +653,47 @@
653653
*/
654654
int blob_is_uuid(Blob *pBlob){
655655
return blob_size(pBlob)==UUID_SIZE
656656
&& validate16(blob_buffer(pBlob), UUID_SIZE);
657657
}
658
+
659
+/*
660
+** Return true if the blob contains a valid filename
661
+*/
662
+int blob_is_filename(Blob *pBlob){
663
+ return file_is_simple_pathname(blob_str(pBlob), 1);
664
+}
658665
659666
/*
660667
** Return true if the blob contains a valid 32-bit integer. Store
661668
** the integer value in *pValue.
662669
*/
663670
int blob_is_int(Blob *pBlob, int *pValue){
664671
const char *z = blob_buffer(pBlob);
665672
int i, n, c, v;
666673
n = blob_size(pBlob);
674
+ v = 0;
675
+ for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
676
+ v = v*10 + c - '0';
677
+ }
678
+ if( i==n ){
679
+ *pValue = v;
680
+ return 1;
681
+ }else{
682
+ return 0;
683
+ }
684
+}
685
+
686
+/*
687
+** Return true if the blob contains a valid 64-bit integer. Store
688
+** the integer value in *pValue.
689
+*/
690
+int blob_is_int64(Blob *pBlob, sqlite3_int64 *pValue){
691
+ const char *z = blob_buffer(pBlob);
692
+ int i, n, c;
693
+ sqlite3_int64 v;
694
+ n = blob_size(pBlob);
667695
v = 0;
668696
for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
669697
v = v*10 + c - '0';
670698
}
671699
if( i==n ){
672700
--- src/blob.c
+++ src/blob.c
@@ -653,19 +653,47 @@
653 */
654 int blob_is_uuid(Blob *pBlob){
655 return blob_size(pBlob)==UUID_SIZE
656 && validate16(blob_buffer(pBlob), UUID_SIZE);
657 }
 
 
 
 
 
 
 
658
659 /*
660 ** Return true if the blob contains a valid 32-bit integer. Store
661 ** the integer value in *pValue.
662 */
663 int blob_is_int(Blob *pBlob, int *pValue){
664 const char *z = blob_buffer(pBlob);
665 int i, n, c, v;
666 n = blob_size(pBlob);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667 v = 0;
668 for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
669 v = v*10 + c - '0';
670 }
671 if( i==n ){
672
--- src/blob.c
+++ src/blob.c
@@ -653,19 +653,47 @@
653 */
654 int blob_is_uuid(Blob *pBlob){
655 return blob_size(pBlob)==UUID_SIZE
656 && validate16(blob_buffer(pBlob), UUID_SIZE);
657 }
658
659 /*
660 ** Return true if the blob contains a valid filename
661 */
662 int blob_is_filename(Blob *pBlob){
663 return file_is_simple_pathname(blob_str(pBlob), 1);
664 }
665
666 /*
667 ** Return true if the blob contains a valid 32-bit integer. Store
668 ** the integer value in *pValue.
669 */
670 int blob_is_int(Blob *pBlob, int *pValue){
671 const char *z = blob_buffer(pBlob);
672 int i, n, c, v;
673 n = blob_size(pBlob);
674 v = 0;
675 for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
676 v = v*10 + c - '0';
677 }
678 if( i==n ){
679 *pValue = v;
680 return 1;
681 }else{
682 return 0;
683 }
684 }
685
686 /*
687 ** Return true if the blob contains a valid 64-bit integer. Store
688 ** the integer value in *pValue.
689 */
690 int blob_is_int64(Blob *pBlob, sqlite3_int64 *pValue){
691 const char *z = blob_buffer(pBlob);
692 int i, n, c;
693 sqlite3_int64 v;
694 n = blob_size(pBlob);
695 v = 0;
696 for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
697 v = v*10 + c - '0';
698 }
699 if( i==n ){
700
+15 -8
--- src/content.c
+++ src/content.c
@@ -452,10 +452,24 @@
452452
** Turn dephantomization processing on or off.
453453
*/
454454
void content_enable_dephantomize(int onoff){
455455
ignoreDephantomizations = !onoff;
456456
}
457
+
458
+/*
459
+** Make sure the g.rcvid global variable has been initialized.
460
+*/
461
+void content_rcvid_init(void){
462
+ if( g.rcvid==0 ){
463
+ db_multi_exec(
464
+ "INSERT INTO rcvfrom(uid, mtime, nonce, ipaddr)"
465
+ "VALUES(%d, julianday('now'), %Q, %Q)",
466
+ g.userUid, g.zNonce, g.zIpAddr
467
+ );
468
+ g.rcvid = db_last_insert_rowid();
469
+ }
470
+}
457471
458472
/*
459473
** Write content into the database. Return the record ID. If the
460474
** content is already in the database, just return the record ID.
461475
**
@@ -530,18 +544,11 @@
530544
markAsUnclustered = 1;
531545
}
532546
db_finalize(&s1);
533547
534548
/* Construct a received-from ID if we do not already have one */
535
- if( g.rcvid==0 ){
536
- db_multi_exec(
537
- "INSERT INTO rcvfrom(uid, mtime, nonce, ipaddr)"
538
- "VALUES(%d, julianday('now'), %Q, %Q)",
539
- g.userUid, g.zNonce, g.zIpAddr
540
- );
541
- g.rcvid = db_last_insert_rowid();
542
- }
549
+ content_rcvid_init();
543550
544551
if( nBlob ){
545552
cmpr = pBlob[0];
546553
}else{
547554
blob_compress(pBlob, &cmpr);
548555
--- src/content.c
+++ src/content.c
@@ -452,10 +452,24 @@
452 ** Turn dephantomization processing on or off.
453 */
454 void content_enable_dephantomize(int onoff){
455 ignoreDephantomizations = !onoff;
456 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457
458 /*
459 ** Write content into the database. Return the record ID. If the
460 ** content is already in the database, just return the record ID.
461 **
@@ -530,18 +544,11 @@
530 markAsUnclustered = 1;
531 }
532 db_finalize(&s1);
533
534 /* Construct a received-from ID if we do not already have one */
535 if( g.rcvid==0 ){
536 db_multi_exec(
537 "INSERT INTO rcvfrom(uid, mtime, nonce, ipaddr)"
538 "VALUES(%d, julianday('now'), %Q, %Q)",
539 g.userUid, g.zNonce, g.zIpAddr
540 );
541 g.rcvid = db_last_insert_rowid();
542 }
543
544 if( nBlob ){
545 cmpr = pBlob[0];
546 }else{
547 blob_compress(pBlob, &cmpr);
548
--- src/content.c
+++ src/content.c
@@ -452,10 +452,24 @@
452 ** Turn dephantomization processing on or off.
453 */
454 void content_enable_dephantomize(int onoff){
455 ignoreDephantomizations = !onoff;
456 }
457
458 /*
459 ** Make sure the g.rcvid global variable has been initialized.
460 */
461 void content_rcvid_init(void){
462 if( g.rcvid==0 ){
463 db_multi_exec(
464 "INSERT INTO rcvfrom(uid, mtime, nonce, ipaddr)"
465 "VALUES(%d, julianday('now'), %Q, %Q)",
466 g.userUid, g.zNonce, g.zIpAddr
467 );
468 g.rcvid = db_last_insert_rowid();
469 }
470 }
471
472 /*
473 ** Write content into the database. Return the record ID. If the
474 ** content is already in the database, just return the record ID.
475 **
@@ -530,18 +544,11 @@
544 markAsUnclustered = 1;
545 }
546 db_finalize(&s1);
547
548 /* Construct a received-from ID if we do not already have one */
549 content_rcvid_init();
 
 
 
 
 
 
 
550
551 if( nBlob ){
552 cmpr = pBlob[0];
553 }else{
554 blob_compress(pBlob, &cmpr);
555
+35 -16
--- src/doc.c
+++ src/doc.c
@@ -522,12 +522,13 @@
522522
}
523523
blob_append(cgi_output_blob(), &z[base], i-base);
524524
}
525525
526526
/*
527
+** WEBPAGE: uv
527528
** WEBPAGE: doc
528
-** URL: /doc?name=CHECKIN/FILE
529
+** URL: /uv/FILE
529530
** URL: /doc/CHECKIN/FILE
530531
**
531532
** CHECKIN can be either tag or SHA1 hash or timestamp identifying a
532533
** particular check, or the name of a branch (meaning the most recent
533534
** check-in on that branch) or one of various magic words:
@@ -578,10 +579,12 @@
578579
int rid = 0; /* Artifact of file */
579580
int i; /* Loop counter */
580581
Blob filebody; /* Content of the documentation file */
581582
Blob title; /* Document title */
582583
int nMiss = (-1); /* Failed attempts to find the document */
584
+ int isUV = g.zPath[0]=='u'; /* True for /uv. False for /doc */
585
+ const char *zDfltTitle;
583586
static const char *const azSuffix[] = {
584587
"index.html", "index.wiki", "index.md"
585588
#ifdef FOSSIL_ENABLE_TH1_DOCS
586589
, "index.th1"
587590
#endif
@@ -588,29 +591,38 @@
588591
};
589592
590593
login_check_credentials();
591594
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
592595
blob_init(&title, 0, 0);
596
+ zDfltTitle = isUV ? "" : "Documentation";
593597
db_begin_transaction();
594598
while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
595599
zName = P("name");
596
- if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki";
597
- for(i=0; zName[i] && zName[i]!='/'; i++){}
598
- zCheckin = mprintf("%.*s", i, zName);
599
- if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){
600
- zCheckin = "tip";
600
+ if( isUV ){
601
+ i = 0;
602
+ }else{
603
+ if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki";
604
+ for(i=0; zName[i] && zName[i]!='/'; i++){}
605
+ zCheckin = mprintf("%.*s", i, zName);
606
+ if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){
607
+ zCheckin = "tip";
608
+ }
601609
}
602610
if( nMiss==ArraySize(azSuffix) ){
603611
zName = "404.md";
604612
}else if( zName[i]==0 ){
605613
assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
606614
zName = azSuffix[nMiss];
607
- }else{
615
+ }else if( !isUV ){
608616
zName += i;
609617
}
610618
while( zName[0]=='/' ){ zName++; }
611
- g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
619
+ if( isUV ){
620
+ g.zPath = mprintf("%s/%s", g.zPath, zName);
621
+ }else{
622
+ g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
623
+ }
612624
if( nMiss==0 ) zOrigName = zName;
613625
if( !file_is_simple_pathname(zName, 1) ){
614626
if( sqlite3_strglob("*/", zName)==0 ){
615627
assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
616628
zName = mprintf("%s%s", zName, azSuffix[nMiss]);
@@ -619,11 +631,16 @@
619631
}
620632
}else{
621633
goto doc_not_found;
622634
}
623635
}
624
- if( fossil_strcmp(zCheckin,"ckout")==0 ){
636
+ if( isUV ){
637
+ if( unversioned_content(zName, &filebody)==0 ){
638
+ rid = 1;
639
+ zDfltTitle = zName;
640
+ }
641
+ }else if( fossil_strcmp(zCheckin,"ckout")==0 ){
625642
/* Read from the local checkout */
626643
char *zFullpath;
627644
db_must_be_within_tree();
628645
zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
629646
if( file_isfile(zFullpath)
@@ -645,22 +662,24 @@
645662
zMime = nMiss==0 ? P("mimetype") : 0;
646663
if( zMime==0 ){
647664
zMime = mimetype_from_name(zName);
648665
}
649666
Th_Store("doc_name", zName);
650
- Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
651
- " FROM blob WHERE rid=%d", vid));
652
- Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
653
- " WHERE objid=%d AND type='ci'", vid));
667
+ if( vid ){
668
+ Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
669
+ " FROM blob WHERE rid=%d", vid));
670
+ Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
671
+ " WHERE objid=%d AND type='ci'", vid));
672
+ }
654673
if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
655674
Blob tail;
656675
style_adunit_config(ADUNIT_RIGHT_OK);
657676
if( wiki_find_title(&filebody, &title, &tail) ){
658677
style_header("%s", blob_str(&title));
659678
wiki_convert(&tail, 0, WIKI_BUTTONS);
660679
}else{
661
- style_header("Documentation");
680
+ style_header("%s", zDfltTitle);
662681
wiki_convert(&filebody, 0, WIKI_BUTTONS);
663682
}
664683
style_footer();
665684
}else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
666685
Blob tail = BLOB_INITIALIZER;
@@ -667,16 +686,16 @@
667686
markdown_to_html(&filebody, &title, &tail);
668687
if( blob_size(&title)>0 ){
669688
style_header("%s", blob_str(&title));
670689
}else{
671690
style_header("%s", nMiss>=ArraySize(azSuffix)?
672
- "Not Found" : "Documentation");
691
+ "Not Found" : zDfltTitle);
673692
}
674693
convert_href_and_output(&tail);
675694
style_footer();
676695
}else if( fossil_strcmp(zMime, "text/plain")==0 ){
677
- style_header("Documentation");
696
+ style_header("%s", zDfltTitle);
678697
@ <blockquote><pre>
679698
@ %h(blob_str(&filebody))
680699
@ </pre></blockquote>
681700
style_footer();
682701
}else if( fossil_strcmp(zMime, "text/html")==0
683702
--- src/doc.c
+++ src/doc.c
@@ -522,12 +522,13 @@
522 }
523 blob_append(cgi_output_blob(), &z[base], i-base);
524 }
525
526 /*
 
527 ** WEBPAGE: doc
528 ** URL: /doc?name=CHECKIN/FILE
529 ** URL: /doc/CHECKIN/FILE
530 **
531 ** CHECKIN can be either tag or SHA1 hash or timestamp identifying a
532 ** particular check, or the name of a branch (meaning the most recent
533 ** check-in on that branch) or one of various magic words:
@@ -578,10 +579,12 @@
578 int rid = 0; /* Artifact of file */
579 int i; /* Loop counter */
580 Blob filebody; /* Content of the documentation file */
581 Blob title; /* Document title */
582 int nMiss = (-1); /* Failed attempts to find the document */
 
 
583 static const char *const azSuffix[] = {
584 "index.html", "index.wiki", "index.md"
585 #ifdef FOSSIL_ENABLE_TH1_DOCS
586 , "index.th1"
587 #endif
@@ -588,29 +591,38 @@
588 };
589
590 login_check_credentials();
591 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
592 blob_init(&title, 0, 0);
 
593 db_begin_transaction();
594 while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
595 zName = P("name");
596 if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki";
597 for(i=0; zName[i] && zName[i]!='/'; i++){}
598 zCheckin = mprintf("%.*s", i, zName);
599 if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){
600 zCheckin = "tip";
 
 
 
 
601 }
602 if( nMiss==ArraySize(azSuffix) ){
603 zName = "404.md";
604 }else if( zName[i]==0 ){
605 assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
606 zName = azSuffix[nMiss];
607 }else{
608 zName += i;
609 }
610 while( zName[0]=='/' ){ zName++; }
611 g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
 
 
 
 
612 if( nMiss==0 ) zOrigName = zName;
613 if( !file_is_simple_pathname(zName, 1) ){
614 if( sqlite3_strglob("*/", zName)==0 ){
615 assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
616 zName = mprintf("%s%s", zName, azSuffix[nMiss]);
@@ -619,11 +631,16 @@
619 }
620 }else{
621 goto doc_not_found;
622 }
623 }
624 if( fossil_strcmp(zCheckin,"ckout")==0 ){
 
 
 
 
 
625 /* Read from the local checkout */
626 char *zFullpath;
627 db_must_be_within_tree();
628 zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
629 if( file_isfile(zFullpath)
@@ -645,22 +662,24 @@
645 zMime = nMiss==0 ? P("mimetype") : 0;
646 if( zMime==0 ){
647 zMime = mimetype_from_name(zName);
648 }
649 Th_Store("doc_name", zName);
650 Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
651 " FROM blob WHERE rid=%d", vid));
652 Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
653 " WHERE objid=%d AND type='ci'", vid));
 
 
654 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
655 Blob tail;
656 style_adunit_config(ADUNIT_RIGHT_OK);
657 if( wiki_find_title(&filebody, &title, &tail) ){
658 style_header("%s", blob_str(&title));
659 wiki_convert(&tail, 0, WIKI_BUTTONS);
660 }else{
661 style_header("Documentation");
662 wiki_convert(&filebody, 0, WIKI_BUTTONS);
663 }
664 style_footer();
665 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
666 Blob tail = BLOB_INITIALIZER;
@@ -667,16 +686,16 @@
667 markdown_to_html(&filebody, &title, &tail);
668 if( blob_size(&title)>0 ){
669 style_header("%s", blob_str(&title));
670 }else{
671 style_header("%s", nMiss>=ArraySize(azSuffix)?
672 "Not Found" : "Documentation");
673 }
674 convert_href_and_output(&tail);
675 style_footer();
676 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
677 style_header("Documentation");
678 @ <blockquote><pre>
679 @ %h(blob_str(&filebody))
680 @ </pre></blockquote>
681 style_footer();
682 }else if( fossil_strcmp(zMime, "text/html")==0
683
--- src/doc.c
+++ src/doc.c
@@ -522,12 +522,13 @@
522 }
523 blob_append(cgi_output_blob(), &z[base], i-base);
524 }
525
526 /*
527 ** WEBPAGE: uv
528 ** WEBPAGE: doc
529 ** URL: /uv/FILE
530 ** URL: /doc/CHECKIN/FILE
531 **
532 ** CHECKIN can be either tag or SHA1 hash or timestamp identifying a
533 ** particular check, or the name of a branch (meaning the most recent
534 ** check-in on that branch) or one of various magic words:
@@ -578,10 +579,12 @@
579 int rid = 0; /* Artifact of file */
580 int i; /* Loop counter */
581 Blob filebody; /* Content of the documentation file */
582 Blob title; /* Document title */
583 int nMiss = (-1); /* Failed attempts to find the document */
584 int isUV = g.zPath[0]=='u'; /* True for /uv. False for /doc */
585 const char *zDfltTitle;
586 static const char *const azSuffix[] = {
587 "index.html", "index.wiki", "index.md"
588 #ifdef FOSSIL_ENABLE_TH1_DOCS
589 , "index.th1"
590 #endif
@@ -588,29 +591,38 @@
591 };
592
593 login_check_credentials();
594 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
595 blob_init(&title, 0, 0);
596 zDfltTitle = isUV ? "" : "Documentation";
597 db_begin_transaction();
598 while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
599 zName = P("name");
600 if( isUV ){
601 i = 0;
602 }else{
603 if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki";
604 for(i=0; zName[i] && zName[i]!='/'; i++){}
605 zCheckin = mprintf("%.*s", i, zName);
606 if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){
607 zCheckin = "tip";
608 }
609 }
610 if( nMiss==ArraySize(azSuffix) ){
611 zName = "404.md";
612 }else if( zName[i]==0 ){
613 assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
614 zName = azSuffix[nMiss];
615 }else if( !isUV ){
616 zName += i;
617 }
618 while( zName[0]=='/' ){ zName++; }
619 if( isUV ){
620 g.zPath = mprintf("%s/%s", g.zPath, zName);
621 }else{
622 g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
623 }
624 if( nMiss==0 ) zOrigName = zName;
625 if( !file_is_simple_pathname(zName, 1) ){
626 if( sqlite3_strglob("*/", zName)==0 ){
627 assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
628 zName = mprintf("%s%s", zName, azSuffix[nMiss]);
@@ -619,11 +631,16 @@
631 }
632 }else{
633 goto doc_not_found;
634 }
635 }
636 if( isUV ){
637 if( unversioned_content(zName, &filebody)==0 ){
638 rid = 1;
639 zDfltTitle = zName;
640 }
641 }else if( fossil_strcmp(zCheckin,"ckout")==0 ){
642 /* Read from the local checkout */
643 char *zFullpath;
644 db_must_be_within_tree();
645 zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
646 if( file_isfile(zFullpath)
@@ -645,22 +662,24 @@
662 zMime = nMiss==0 ? P("mimetype") : 0;
663 if( zMime==0 ){
664 zMime = mimetype_from_name(zName);
665 }
666 Th_Store("doc_name", zName);
667 if( vid ){
668 Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
669 " FROM blob WHERE rid=%d", vid));
670 Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
671 " WHERE objid=%d AND type='ci'", vid));
672 }
673 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
674 Blob tail;
675 style_adunit_config(ADUNIT_RIGHT_OK);
676 if( wiki_find_title(&filebody, &title, &tail) ){
677 style_header("%s", blob_str(&title));
678 wiki_convert(&tail, 0, WIKI_BUTTONS);
679 }else{
680 style_header("%s", zDfltTitle);
681 wiki_convert(&filebody, 0, WIKI_BUTTONS);
682 }
683 style_footer();
684 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
685 Blob tail = BLOB_INITIALIZER;
@@ -667,16 +686,16 @@
686 markdown_to_html(&filebody, &title, &tail);
687 if( blob_size(&title)>0 ){
688 style_header("%s", blob_str(&title));
689 }else{
690 style_header("%s", nMiss>=ArraySize(azSuffix)?
691 "Not Found" : zDfltTitle);
692 }
693 convert_href_and_output(&tail);
694 style_footer();
695 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
696 style_header("%s", zDfltTitle);
697 @ <blockquote><pre>
698 @ %h(blob_str(&filebody))
699 @ </pre></blockquote>
700 style_footer();
701 }else if( fossil_strcmp(zMime, "text/html")==0
702
+2 -1
--- src/login.c
+++ src/login.c
@@ -1095,11 +1095,11 @@
10951095
p->RdWiki = p->WrWiki = p->NewWiki =
10961096
p->ApndWiki = p->Hyperlink = p->Clone =
10971097
p->NewTkt = p->Password = p->RdAddr =
10981098
p->TktFmt = p->Attach = p->ApndTkt =
10991099
p->ModWiki = p->ModTkt = p->Delete =
1100
- p->Private = 1;
1100
+ p->WrUnver = p->Private = 1;
11011101
/* Fall thru into Read/Write */
11021102
case 'i': p->Read = p->Write = 1; break;
11031103
case 'o': p->Read = 1; break;
11041104
case 'z': p->Zip = 1; break;
11051105
@@ -1122,10 +1122,11 @@
11221122
case 'c': p->ApndTkt = 1; break;
11231123
case 'q': p->ModTkt = 1; break;
11241124
case 't': p->TktFmt = 1; break;
11251125
case 'b': p->Attach = 1; break;
11261126
case 'x': p->Private = 1; break;
1127
+ case 'y': p->WrUnver = 1; break;
11271128
11281129
/* The "u" privileges is a little different. It recursively
11291130
** inherits all privileges of the user named "reader" */
11301131
case 'u': {
11311132
if( (flags & LOGIN_IGNORE_UV)==0 ){
11321133
--- src/login.c
+++ src/login.c
@@ -1095,11 +1095,11 @@
1095 p->RdWiki = p->WrWiki = p->NewWiki =
1096 p->ApndWiki = p->Hyperlink = p->Clone =
1097 p->NewTkt = p->Password = p->RdAddr =
1098 p->TktFmt = p->Attach = p->ApndTkt =
1099 p->ModWiki = p->ModTkt = p->Delete =
1100 p->Private = 1;
1101 /* Fall thru into Read/Write */
1102 case 'i': p->Read = p->Write = 1; break;
1103 case 'o': p->Read = 1; break;
1104 case 'z': p->Zip = 1; break;
1105
@@ -1122,10 +1122,11 @@
1122 case 'c': p->ApndTkt = 1; break;
1123 case 'q': p->ModTkt = 1; break;
1124 case 't': p->TktFmt = 1; break;
1125 case 'b': p->Attach = 1; break;
1126 case 'x': p->Private = 1; break;
 
1127
1128 /* The "u" privileges is a little different. It recursively
1129 ** inherits all privileges of the user named "reader" */
1130 case 'u': {
1131 if( (flags & LOGIN_IGNORE_UV)==0 ){
1132
--- src/login.c
+++ src/login.c
@@ -1095,11 +1095,11 @@
1095 p->RdWiki = p->WrWiki = p->NewWiki =
1096 p->ApndWiki = p->Hyperlink = p->Clone =
1097 p->NewTkt = p->Password = p->RdAddr =
1098 p->TktFmt = p->Attach = p->ApndTkt =
1099 p->ModWiki = p->ModTkt = p->Delete =
1100 p->WrUnver = p->Private = 1;
1101 /* Fall thru into Read/Write */
1102 case 'i': p->Read = p->Write = 1; break;
1103 case 'o': p->Read = 1; break;
1104 case 'z': p->Zip = 1; break;
1105
@@ -1122,10 +1122,11 @@
1122 case 'c': p->ApndTkt = 1; break;
1123 case 'q': p->ModTkt = 1; break;
1124 case 't': p->TktFmt = 1; break;
1125 case 'b': p->Attach = 1; break;
1126 case 'x': p->Private = 1; break;
1127 case 'y': p->WrUnver = 1; break;
1128
1129 /* The "u" privileges is a little different. It recursively
1130 ** inherits all privileges of the user named "reader" */
1131 case 'u': {
1132 if( (flags & LOGIN_IGNORE_UV)==0 ){
1133
+1
--- src/main.c
+++ src/main.c
@@ -91,10 +91,11 @@
9191
char Attach; /* b: add attachments */
9292
char TktFmt; /* t: create new ticket report formats */
9393
char RdAddr; /* e: read email addresses or other private data */
9494
char Zip; /* z: download zipped artifact via /zip URL */
9595
char Private; /* x: can send and receive private content */
96
+ char WrUnver; /* y: can push unversioned content */
9697
};
9798
9899
#ifdef FOSSIL_ENABLE_TCL
99100
/*
100101
** All Tcl related context information is in this structure. This structure
101102
--- src/main.c
+++ src/main.c
@@ -91,10 +91,11 @@
91 char Attach; /* b: add attachments */
92 char TktFmt; /* t: create new ticket report formats */
93 char RdAddr; /* e: read email addresses or other private data */
94 char Zip; /* z: download zipped artifact via /zip URL */
95 char Private; /* x: can send and receive private content */
 
96 };
97
98 #ifdef FOSSIL_ENABLE_TCL
99 /*
100 ** All Tcl related context information is in this structure. This structure
101
--- src/main.c
+++ src/main.c
@@ -91,10 +91,11 @@
91 char Attach; /* b: add attachments */
92 char TktFmt; /* t: create new ticket report formats */
93 char RdAddr; /* e: read email addresses or other private data */
94 char Zip; /* z: download zipped artifact via /zip URL */
95 char Private; /* x: can send and receive private content */
96 char WrUnver; /* y: can push unversioned content */
97 };
98
99 #ifdef FOSSIL_ENABLE_TCL
100 /*
101 ** All Tcl related context information is in this structure. This structure
102
+12
--- src/main.mk
+++ src/main.mk
@@ -117,10 +117,11 @@
117117
$(SRCDIR)/timeline.c \
118118
$(SRCDIR)/tkt.c \
119119
$(SRCDIR)/tktsetup.c \
120120
$(SRCDIR)/undo.c \
121121
$(SRCDIR)/unicode.c \
122
+ $(SRCDIR)/unversioned.c \
122123
$(SRCDIR)/update.c \
123124
$(SRCDIR)/url.c \
124125
$(SRCDIR)/user.c \
125126
$(SRCDIR)/utf8.c \
126127
$(SRCDIR)/util.c \
@@ -290,10 +291,11 @@
290291
$(OBJDIR)/timeline_.c \
291292
$(OBJDIR)/tkt_.c \
292293
$(OBJDIR)/tktsetup_.c \
293294
$(OBJDIR)/undo_.c \
294295
$(OBJDIR)/unicode_.c \
296
+ $(OBJDIR)/unversioned_.c \
295297
$(OBJDIR)/update_.c \
296298
$(OBJDIR)/url_.c \
297299
$(OBJDIR)/user_.c \
298300
$(OBJDIR)/utf8_.c \
299301
$(OBJDIR)/util_.c \
@@ -412,10 +414,11 @@
412414
$(OBJDIR)/timeline.o \
413415
$(OBJDIR)/tkt.o \
414416
$(OBJDIR)/tktsetup.o \
415417
$(OBJDIR)/undo.o \
416418
$(OBJDIR)/unicode.o \
419
+ $(OBJDIR)/unversioned.o \
417420
$(OBJDIR)/update.o \
418421
$(OBJDIR)/url.o \
419422
$(OBJDIR)/user.o \
420423
$(OBJDIR)/utf8.o \
421424
$(OBJDIR)/util.o \
@@ -688,10 +691,11 @@
688691
$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
689692
$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
690693
$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
691694
$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
692695
$(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
696
+ $(OBJDIR)/unversioned_.c:$(OBJDIR)/unversioned.h \
693697
$(OBJDIR)/update_.c:$(OBJDIR)/update.h \
694698
$(OBJDIR)/url_.c:$(OBJDIR)/url.h \
695699
$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
696700
$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
697701
$(OBJDIR)/util_.c:$(OBJDIR)/util.h \
@@ -1549,10 +1553,18 @@
15491553
15501554
$(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
15511555
$(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
15521556
15531557
$(OBJDIR)/unicode.h: $(OBJDIR)/headers
1558
+
1559
+$(OBJDIR)/unversioned_.c: $(SRCDIR)/unversioned.c $(OBJDIR)/translate
1560
+ $(OBJDIR)/translate $(SRCDIR)/unversioned.c >$@
1561
+
1562
+$(OBJDIR)/unversioned.o: $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.h $(SRCDIR)/config.h
1563
+ $(XTCC) -o $(OBJDIR)/unversioned.o -c $(OBJDIR)/unversioned_.c
1564
+
1565
+$(OBJDIR)/unversioned.h: $(OBJDIR)/headers
15541566
15551567
$(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
15561568
$(OBJDIR)/translate $(SRCDIR)/update.c >$@
15571569
15581570
$(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
15591571
--- src/main.mk
+++ src/main.mk
@@ -117,10 +117,11 @@
117 $(SRCDIR)/timeline.c \
118 $(SRCDIR)/tkt.c \
119 $(SRCDIR)/tktsetup.c \
120 $(SRCDIR)/undo.c \
121 $(SRCDIR)/unicode.c \
 
122 $(SRCDIR)/update.c \
123 $(SRCDIR)/url.c \
124 $(SRCDIR)/user.c \
125 $(SRCDIR)/utf8.c \
126 $(SRCDIR)/util.c \
@@ -290,10 +291,11 @@
290 $(OBJDIR)/timeline_.c \
291 $(OBJDIR)/tkt_.c \
292 $(OBJDIR)/tktsetup_.c \
293 $(OBJDIR)/undo_.c \
294 $(OBJDIR)/unicode_.c \
 
295 $(OBJDIR)/update_.c \
296 $(OBJDIR)/url_.c \
297 $(OBJDIR)/user_.c \
298 $(OBJDIR)/utf8_.c \
299 $(OBJDIR)/util_.c \
@@ -412,10 +414,11 @@
412 $(OBJDIR)/timeline.o \
413 $(OBJDIR)/tkt.o \
414 $(OBJDIR)/tktsetup.o \
415 $(OBJDIR)/undo.o \
416 $(OBJDIR)/unicode.o \
 
417 $(OBJDIR)/update.o \
418 $(OBJDIR)/url.o \
419 $(OBJDIR)/user.o \
420 $(OBJDIR)/utf8.o \
421 $(OBJDIR)/util.o \
@@ -688,10 +691,11 @@
688 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
689 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
690 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
691 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
692 $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
 
693 $(OBJDIR)/update_.c:$(OBJDIR)/update.h \
694 $(OBJDIR)/url_.c:$(OBJDIR)/url.h \
695 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
696 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
697 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
@@ -1549,10 +1553,18 @@
1549
1550 $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
1551 $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
1552
1553 $(OBJDIR)/unicode.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1554
1555 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
1556 $(OBJDIR)/translate $(SRCDIR)/update.c >$@
1557
1558 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
1559
--- src/main.mk
+++ src/main.mk
@@ -117,10 +117,11 @@
117 $(SRCDIR)/timeline.c \
118 $(SRCDIR)/tkt.c \
119 $(SRCDIR)/tktsetup.c \
120 $(SRCDIR)/undo.c \
121 $(SRCDIR)/unicode.c \
122 $(SRCDIR)/unversioned.c \
123 $(SRCDIR)/update.c \
124 $(SRCDIR)/url.c \
125 $(SRCDIR)/user.c \
126 $(SRCDIR)/utf8.c \
127 $(SRCDIR)/util.c \
@@ -290,10 +291,11 @@
291 $(OBJDIR)/timeline_.c \
292 $(OBJDIR)/tkt_.c \
293 $(OBJDIR)/tktsetup_.c \
294 $(OBJDIR)/undo_.c \
295 $(OBJDIR)/unicode_.c \
296 $(OBJDIR)/unversioned_.c \
297 $(OBJDIR)/update_.c \
298 $(OBJDIR)/url_.c \
299 $(OBJDIR)/user_.c \
300 $(OBJDIR)/utf8_.c \
301 $(OBJDIR)/util_.c \
@@ -412,10 +414,11 @@
414 $(OBJDIR)/timeline.o \
415 $(OBJDIR)/tkt.o \
416 $(OBJDIR)/tktsetup.o \
417 $(OBJDIR)/undo.o \
418 $(OBJDIR)/unicode.o \
419 $(OBJDIR)/unversioned.o \
420 $(OBJDIR)/update.o \
421 $(OBJDIR)/url.o \
422 $(OBJDIR)/user.o \
423 $(OBJDIR)/utf8.o \
424 $(OBJDIR)/util.o \
@@ -688,10 +691,11 @@
691 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
692 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
693 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
694 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
695 $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
696 $(OBJDIR)/unversioned_.c:$(OBJDIR)/unversioned.h \
697 $(OBJDIR)/update_.c:$(OBJDIR)/update.h \
698 $(OBJDIR)/url_.c:$(OBJDIR)/url.h \
699 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
700 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
701 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
@@ -1549,10 +1553,18 @@
1553
1554 $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
1555 $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
1556
1557 $(OBJDIR)/unicode.h: $(OBJDIR)/headers
1558
1559 $(OBJDIR)/unversioned_.c: $(SRCDIR)/unversioned.c $(OBJDIR)/translate
1560 $(OBJDIR)/translate $(SRCDIR)/unversioned.c >$@
1561
1562 $(OBJDIR)/unversioned.o: $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.h $(SRCDIR)/config.h
1563 $(XTCC) -o $(OBJDIR)/unversioned.o -c $(OBJDIR)/unversioned_.c
1564
1565 $(OBJDIR)/unversioned.h: $(OBJDIR)/headers
1566
1567 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
1568 $(OBJDIR)/translate $(SRCDIR)/update.c >$@
1569
1570 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
1571
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -123,10 +123,11 @@
123123
timeline
124124
tkt
125125
tktsetup
126126
undo
127127
unicode
128
+ unversioned
128129
update
129130
url
130131
user
131132
utf8
132133
util
133134
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -123,10 +123,11 @@
123 timeline
124 tkt
125 tktsetup
126 undo
127 unicode
 
128 update
129 url
130 user
131 utf8
132 util
133
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -123,10 +123,11 @@
123 timeline
124 tkt
125 tktsetup
126 undo
127 unicode
128 unversioned
129 update
130 url
131 user
132 utf8
133 util
134
--- src/setup.c
+++ src/setup.c
@@ -294,10 +294,12 @@
294294
@ user <tt>developer</tt></td></tr>
295295
@ <tr><th valign="top">w</th>
296296
@ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
297297
@ <tr><th valign="top">x</th>
298298
@ <td><i>Private:</i> Push and/or pull private branches</td></tr>
299
+ @ <tr><th valign="top">y</th>
300
+ @ <td><i>Write-Unver:</i> Push unversioned files</td></tr>
299301
@ <tr><th valign="top">z</th>
300302
@ <td><i>Zip download:</i> Download a ZIP archive or tarball</td></tr>
301303
@ </table>
302304
}
303305
@@ -714,10 +716,13 @@
714716
@ onchange="updateCapabilityString()" />
715717
@ Ticket Report%s(B('t'))</label><br />
716718
@ <label><input type="checkbox" name="ax"%s(oa['x'])
717719
@ onchange="updateCapabilityString()" />
718720
@ Private%s(B('x'))</label><br />
721
+ @ <label><input type="checkbox" name="ay"%s(oa['y'])
722
+ @ onchange="updateCapabilityString()" />
723
+ @ Write Unverioned%s(B('y'))</label><br />
719724
@ <label><input type="checkbox" name="az"%s(oa['z'])
720725
@ onchange="updateCapabilityString()" />
721726
@ Download Zip%s(B('z'))</label>
722727
@ </td></tr>
723728
@ </table>
724729
--- src/setup.c
+++ src/setup.c
@@ -294,10 +294,12 @@
294 @ user <tt>developer</tt></td></tr>
295 @ <tr><th valign="top">w</th>
296 @ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
297 @ <tr><th valign="top">x</th>
298 @ <td><i>Private:</i> Push and/or pull private branches</td></tr>
 
 
299 @ <tr><th valign="top">z</th>
300 @ <td><i>Zip download:</i> Download a ZIP archive or tarball</td></tr>
301 @ </table>
302 }
303
@@ -714,10 +716,13 @@
714 @ onchange="updateCapabilityString()" />
715 @ Ticket Report%s(B('t'))</label><br />
716 @ <label><input type="checkbox" name="ax"%s(oa['x'])
717 @ onchange="updateCapabilityString()" />
718 @ Private%s(B('x'))</label><br />
 
 
 
719 @ <label><input type="checkbox" name="az"%s(oa['z'])
720 @ onchange="updateCapabilityString()" />
721 @ Download Zip%s(B('z'))</label>
722 @ </td></tr>
723 @ </table>
724
--- src/setup.c
+++ src/setup.c
@@ -294,10 +294,12 @@
294 @ user <tt>developer</tt></td></tr>
295 @ <tr><th valign="top">w</th>
296 @ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
297 @ <tr><th valign="top">x</th>
298 @ <td><i>Private:</i> Push and/or pull private branches</td></tr>
299 @ <tr><th valign="top">y</th>
300 @ <td><i>Write-Unver:</i> Push unversioned files</td></tr>
301 @ <tr><th valign="top">z</th>
302 @ <td><i>Zip download:</i> Download a ZIP archive or tarball</td></tr>
303 @ </table>
304 }
305
@@ -714,10 +716,13 @@
716 @ onchange="updateCapabilityString()" />
717 @ Ticket Report%s(B('t'))</label><br />
718 @ <label><input type="checkbox" name="ax"%s(oa['x'])
719 @ onchange="updateCapabilityString()" />
720 @ Private%s(B('x'))</label><br />
721 @ <label><input type="checkbox" name="ay"%s(oa['y'])
722 @ onchange="updateCapabilityString()" />
723 @ Write Unverioned%s(B('y'))</label><br />
724 @ <label><input type="checkbox" name="az"%s(oa['z'])
725 @ onchange="updateCapabilityString()" />
726 @ Download Zip%s(B('z'))</label>
727 @ </td></tr>
728 @ </table>
729
--- src/sitemap.c
+++ src/sitemap.c
@@ -101,10 +101,13 @@
101101
@ <li>%z(href("%R/timeline?y=t"))Recent activity</a></li>
102102
@ <li>%z(href("%R/attachlist"))List of Attachments</a></li>
103103
@ </ul>
104104
@ </li>
105105
}
106
+ if( g.perm.Read ){
107
+ @ <li>%z(href("%R/uvlist"))Unversioned Files</a>
108
+ }
106109
if( srchFlags ){
107110
@ <li>%z(href("%R/search"))Full-Text Search</a></li>
108111
}
109112
@ <li>%z(href("%R/login"))Login/Logout/Change Password</a></li>
110113
if( g.perm.Read ){
111114
--- src/sitemap.c
+++ src/sitemap.c
@@ -101,10 +101,13 @@
101 @ <li>%z(href("%R/timeline?y=t"))Recent activity</a></li>
102 @ <li>%z(href("%R/attachlist"))List of Attachments</a></li>
103 @ </ul>
104 @ </li>
105 }
 
 
 
106 if( srchFlags ){
107 @ <li>%z(href("%R/search"))Full-Text Search</a></li>
108 }
109 @ <li>%z(href("%R/login"))Login/Logout/Change Password</a></li>
110 if( g.perm.Read ){
111
--- src/sitemap.c
+++ src/sitemap.c
@@ -101,10 +101,13 @@
101 @ <li>%z(href("%R/timeline?y=t"))Recent activity</a></li>
102 @ <li>%z(href("%R/attachlist"))List of Attachments</a></li>
103 @ </ul>
104 @ </li>
105 }
106 if( g.perm.Read ){
107 @ <li>%z(href("%R/uvlist"))Unversioned Files</a>
108 }
109 if( srchFlags ){
110 @ <li>%z(href("%R/search"))Full-Text Search</a></li>
111 }
112 @ <li>%z(href("%R/login"))Login/Logout/Change Password</a></li>
113 if( g.perm.Read ){
114
+21
--- src/stat.c
+++ src/stat.c
@@ -120,10 +120,31 @@
120120
b = 1;
121121
}
122122
a = t/fsize;
123123
@ %d(a):%d(b)
124124
@ </td></tr>
125
+ }
126
+ if( db_table_exists("repository","unversioned") ){
127
+ Stmt q;
128
+ char zStored[100];
129
+ db_prepare(&q,
130
+ "SELECT count(*), sum(sz), sum(length(content))"
131
+ " FROM unversioned"
132
+ " WHERE length(hash)>1"
133
+ );
134
+ if( db_step(&q)==SQLITE_ROW && (n = db_column_int(&q,0))>0 ){
135
+ sqlite3_int64 iSz, iStored;
136
+ iSz = db_column_int64(&q,1);
137
+ iStored = db_column_int64(&q,2);
138
+ approxSizeName(sizeof(zBuf), zBuf, iSz);
139
+ approxSizeName(sizeof(zStored), zStored, iStored);
140
+ @ <tr><th>Unversioned&nbsp;Files:</th><td>
141
+ @ %z(href("%R/uvlist"))%d(n) files</a>,
142
+ @ total size %s(zBuf) uncompressed, %s(zStored) compressed
143
+ @ </td></tr>
144
+ }
145
+ db_finalize(&q);
125146
}
126147
@ <tr><th>Number&nbsp;Of&nbsp;Check-ins:</th><td>
127148
n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/");
128149
@ %d(n)
129150
@ </td></tr>
130151
--- src/stat.c
+++ src/stat.c
@@ -120,10 +120,31 @@
120 b = 1;
121 }
122 a = t/fsize;
123 @ %d(a):%d(b)
124 @ </td></tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125 }
126 @ <tr><th>Number&nbsp;Of&nbsp;Check-ins:</th><td>
127 n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/");
128 @ %d(n)
129 @ </td></tr>
130
--- src/stat.c
+++ src/stat.c
@@ -120,10 +120,31 @@
120 b = 1;
121 }
122 a = t/fsize;
123 @ %d(a):%d(b)
124 @ </td></tr>
125 }
126 if( db_table_exists("repository","unversioned") ){
127 Stmt q;
128 char zStored[100];
129 db_prepare(&q,
130 "SELECT count(*), sum(sz), sum(length(content))"
131 " FROM unversioned"
132 " WHERE length(hash)>1"
133 );
134 if( db_step(&q)==SQLITE_ROW && (n = db_column_int(&q,0))>0 ){
135 sqlite3_int64 iSz, iStored;
136 iSz = db_column_int64(&q,1);
137 iStored = db_column_int64(&q,2);
138 approxSizeName(sizeof(zBuf), zBuf, iSz);
139 approxSizeName(sizeof(zStored), zStored, iStored);
140 @ <tr><th>Unversioned&nbsp;Files:</th><td>
141 @ %z(href("%R/uvlist"))%d(n) files</a>,
142 @ total size %s(zBuf) uncompressed, %s(zStored) compressed
143 @ </td></tr>
144 }
145 db_finalize(&q);
146 }
147 @ <tr><th>Number&nbsp;Of&nbsp;Check-ins:</th><td>
148 n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/");
149 @ %d(n)
150 @ </td></tr>
151
+34 -11
--- src/sync.c
+++ src/sync.c
@@ -113,11 +113,15 @@
113113
** This routine processes the command-line argument for push, pull,
114114
** and sync. If a command-line argument is given, that is the URL
115115
** of a server to sync against. If no argument is given, use the
116116
** most recently synced URL. Remember the current URL for next time.
117117
*/
118
-static void process_sync_args(unsigned *pConfigFlags, unsigned *pSyncFlags){
118
+static void process_sync_args(
119
+ unsigned *pConfigFlags, /* Write configuration flags here */
120
+ unsigned *pSyncFlags, /* Write sync flags here */
121
+ int uvOnly /* Special handling flags for UV sync */
122
+){
119123
const char *zUrl = 0;
120124
const char *zHttpAuth = 0;
121125
unsigned configSync = 0;
122126
unsigned urlFlags = URL_REMEMBER | URL_PROMPT_PW;
123127
int urlOptional = 0;
@@ -126,25 +130,33 @@
126130
urlFlags = 0;
127131
}
128132
zHttpAuth = find_option("httpauth","B",1);
129133
if( find_option("once",0,0)!=0 ) urlFlags &= ~URL_REMEMBER;
130134
if( (*pSyncFlags) & SYNC_FROMPARENT ) urlFlags &= ~URL_REMEMBER;
135
+ if( !uvOnly ){
136
+ if( find_option("private",0,0)!=0 ){
137
+ *pSyncFlags |= SYNC_PRIVATE;
138
+ }
139
+ /* The --verily option to sync, push, and pull forces extra igot cards
140
+ ** to be exchanged. This can overcome malfunctions in the sync protocol.
141
+ */
142
+ if( find_option("verily",0,0)!=0 ){
143
+ *pSyncFlags |= SYNC_RESYNC;
144
+ }
145
+ }
146
+ if( find_option("uv",0,0)!=0 ){
147
+ *pSyncFlags |= SYNC_UNVERSIONED;
148
+ }
131149
if( find_option("private",0,0)!=0 ){
132150
*pSyncFlags |= SYNC_PRIVATE;
133151
}
134152
if( find_option("verbose","v",0)!=0 ){
135153
*pSyncFlags |= SYNC_VERBOSE;
136154
}
137
- /* The --verily option to sync, push, and pull forces extra igot cards
138
- ** to be exchanged. This can overcome malfunctions in the sync protocol.
139
- */
140
- if( find_option("verily",0,0)!=0 ){
141
- *pSyncFlags |= SYNC_RESYNC;
142
- }
143155
url_proxy_options();
144156
clone_ssh_find_options();
145
- db_find_and_open_repository(0, 0);
157
+ if( !uvOnly ) db_find_and_open_repository(0, 0);
146158
db_open_config(0, 0);
147159
if( g.argc==2 ){
148160
if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
149161
}else if( g.argc==3 ){
150162
zUrl = g.argv[2];
@@ -209,11 +221,11 @@
209221
unsigned configFlags = 0;
210222
unsigned syncFlags = SYNC_PULL;
211223
if( find_option("from-parent-project",0,0)!=0 ){
212224
syncFlags |= SYNC_FROMPARENT;
213225
}
214
- process_sync_args(&configFlags, &syncFlags);
226
+ process_sync_args(&configFlags, &syncFlags, 0);
215227
216228
/* We should be done with options.. */
217229
verify_all_options();
218230
219231
client_sync(syncFlags, configFlags, 0);
@@ -251,11 +263,11 @@
251263
** See also: clone, config push, pull, remote-url, sync
252264
*/
253265
void push_cmd(void){
254266
unsigned configFlags = 0;
255267
unsigned syncFlags = SYNC_PUSH;
256
- process_sync_args(&configFlags, &syncFlags);
268
+ process_sync_args(&configFlags, &syncFlags, 0);
257269
258270
/* We should be done with options.. */
259271
verify_all_options();
260272
261273
if( db_get_boolean("dont-push",0) ){
@@ -296,11 +308,11 @@
296308
** See also: clone, pull, push, remote-url
297309
*/
298310
void sync_cmd(void){
299311
unsigned configFlags = 0;
300312
unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
301
- process_sync_args(&configFlags, &syncFlags);
313
+ process_sync_args(&configFlags, &syncFlags, 0);
302314
303315
/* We should be done with options.. */
304316
verify_all_options();
305317
306318
if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
@@ -307,10 +319,21 @@
307319
client_sync(syncFlags, configFlags, 0);
308320
if( (syncFlags & SYNC_PUSH)==0 ){
309321
fossil_warning("pull only: the 'dont-push' option is set");
310322
}
311323
}
324
+
325
+/*
326
+** Handle the "fossil unversioned sync" command.
327
+*/
328
+void sync_unversioned(void){
329
+ unsigned configFlags = 0;
330
+ unsigned syncFlags = SYNC_UNVERSIONED;
331
+ process_sync_args(&configFlags, &syncFlags, 1);
332
+ verify_all_options();
333
+ client_sync(syncFlags, 0, 0);
334
+}
312335
313336
/*
314337
** COMMAND: remote-url
315338
**
316339
** Usage: %fossil remote-url ?URL|off?
317340
318341
ADDED src/unversioned.c
--- src/sync.c
+++ src/sync.c
@@ -113,11 +113,15 @@
113 ** This routine processes the command-line argument for push, pull,
114 ** and sync. If a command-line argument is given, that is the URL
115 ** of a server to sync against. If no argument is given, use the
116 ** most recently synced URL. Remember the current URL for next time.
117 */
118 static void process_sync_args(unsigned *pConfigFlags, unsigned *pSyncFlags){
 
 
 
 
119 const char *zUrl = 0;
120 const char *zHttpAuth = 0;
121 unsigned configSync = 0;
122 unsigned urlFlags = URL_REMEMBER | URL_PROMPT_PW;
123 int urlOptional = 0;
@@ -126,25 +130,33 @@
126 urlFlags = 0;
127 }
128 zHttpAuth = find_option("httpauth","B",1);
129 if( find_option("once",0,0)!=0 ) urlFlags &= ~URL_REMEMBER;
130 if( (*pSyncFlags) & SYNC_FROMPARENT ) urlFlags &= ~URL_REMEMBER;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131 if( find_option("private",0,0)!=0 ){
132 *pSyncFlags |= SYNC_PRIVATE;
133 }
134 if( find_option("verbose","v",0)!=0 ){
135 *pSyncFlags |= SYNC_VERBOSE;
136 }
137 /* The --verily option to sync, push, and pull forces extra igot cards
138 ** to be exchanged. This can overcome malfunctions in the sync protocol.
139 */
140 if( find_option("verily",0,0)!=0 ){
141 *pSyncFlags |= SYNC_RESYNC;
142 }
143 url_proxy_options();
144 clone_ssh_find_options();
145 db_find_and_open_repository(0, 0);
146 db_open_config(0, 0);
147 if( g.argc==2 ){
148 if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
149 }else if( g.argc==3 ){
150 zUrl = g.argv[2];
@@ -209,11 +221,11 @@
209 unsigned configFlags = 0;
210 unsigned syncFlags = SYNC_PULL;
211 if( find_option("from-parent-project",0,0)!=0 ){
212 syncFlags |= SYNC_FROMPARENT;
213 }
214 process_sync_args(&configFlags, &syncFlags);
215
216 /* We should be done with options.. */
217 verify_all_options();
218
219 client_sync(syncFlags, configFlags, 0);
@@ -251,11 +263,11 @@
251 ** See also: clone, config push, pull, remote-url, sync
252 */
253 void push_cmd(void){
254 unsigned configFlags = 0;
255 unsigned syncFlags = SYNC_PUSH;
256 process_sync_args(&configFlags, &syncFlags);
257
258 /* We should be done with options.. */
259 verify_all_options();
260
261 if( db_get_boolean("dont-push",0) ){
@@ -296,11 +308,11 @@
296 ** See also: clone, pull, push, remote-url
297 */
298 void sync_cmd(void){
299 unsigned configFlags = 0;
300 unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
301 process_sync_args(&configFlags, &syncFlags);
302
303 /* We should be done with options.. */
304 verify_all_options();
305
306 if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
@@ -307,10 +319,21 @@
307 client_sync(syncFlags, configFlags, 0);
308 if( (syncFlags & SYNC_PUSH)==0 ){
309 fossil_warning("pull only: the 'dont-push' option is set");
310 }
311 }
 
 
 
 
 
 
 
 
 
 
 
312
313 /*
314 ** COMMAND: remote-url
315 **
316 ** Usage: %fossil remote-url ?URL|off?
317
318 DDED src/unversioned.c
--- src/sync.c
+++ src/sync.c
@@ -113,11 +113,15 @@
113 ** This routine processes the command-line argument for push, pull,
114 ** and sync. If a command-line argument is given, that is the URL
115 ** of a server to sync against. If no argument is given, use the
116 ** most recently synced URL. Remember the current URL for next time.
117 */
118 static void process_sync_args(
119 unsigned *pConfigFlags, /* Write configuration flags here */
120 unsigned *pSyncFlags, /* Write sync flags here */
121 int uvOnly /* Special handling flags for UV sync */
122 ){
123 const char *zUrl = 0;
124 const char *zHttpAuth = 0;
125 unsigned configSync = 0;
126 unsigned urlFlags = URL_REMEMBER | URL_PROMPT_PW;
127 int urlOptional = 0;
@@ -126,25 +130,33 @@
130 urlFlags = 0;
131 }
132 zHttpAuth = find_option("httpauth","B",1);
133 if( find_option("once",0,0)!=0 ) urlFlags &= ~URL_REMEMBER;
134 if( (*pSyncFlags) & SYNC_FROMPARENT ) urlFlags &= ~URL_REMEMBER;
135 if( !uvOnly ){
136 if( find_option("private",0,0)!=0 ){
137 *pSyncFlags |= SYNC_PRIVATE;
138 }
139 /* The --verily option to sync, push, and pull forces extra igot cards
140 ** to be exchanged. This can overcome malfunctions in the sync protocol.
141 */
142 if( find_option("verily",0,0)!=0 ){
143 *pSyncFlags |= SYNC_RESYNC;
144 }
145 }
146 if( find_option("uv",0,0)!=0 ){
147 *pSyncFlags |= SYNC_UNVERSIONED;
148 }
149 if( find_option("private",0,0)!=0 ){
150 *pSyncFlags |= SYNC_PRIVATE;
151 }
152 if( find_option("verbose","v",0)!=0 ){
153 *pSyncFlags |= SYNC_VERBOSE;
154 }
 
 
 
 
 
 
155 url_proxy_options();
156 clone_ssh_find_options();
157 if( !uvOnly ) db_find_and_open_repository(0, 0);
158 db_open_config(0, 0);
159 if( g.argc==2 ){
160 if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
161 }else if( g.argc==3 ){
162 zUrl = g.argv[2];
@@ -209,11 +221,11 @@
221 unsigned configFlags = 0;
222 unsigned syncFlags = SYNC_PULL;
223 if( find_option("from-parent-project",0,0)!=0 ){
224 syncFlags |= SYNC_FROMPARENT;
225 }
226 process_sync_args(&configFlags, &syncFlags, 0);
227
228 /* We should be done with options.. */
229 verify_all_options();
230
231 client_sync(syncFlags, configFlags, 0);
@@ -251,11 +263,11 @@
263 ** See also: clone, config push, pull, remote-url, sync
264 */
265 void push_cmd(void){
266 unsigned configFlags = 0;
267 unsigned syncFlags = SYNC_PUSH;
268 process_sync_args(&configFlags, &syncFlags, 0);
269
270 /* We should be done with options.. */
271 verify_all_options();
272
273 if( db_get_boolean("dont-push",0) ){
@@ -296,11 +308,11 @@
308 ** See also: clone, pull, push, remote-url
309 */
310 void sync_cmd(void){
311 unsigned configFlags = 0;
312 unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
313 process_sync_args(&configFlags, &syncFlags, 0);
314
315 /* We should be done with options.. */
316 verify_all_options();
317
318 if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
@@ -307,10 +319,21 @@
319 client_sync(syncFlags, configFlags, 0);
320 if( (syncFlags & SYNC_PUSH)==0 ){
321 fossil_warning("pull only: the 'dont-push' option is set");
322 }
323 }
324
325 /*
326 ** Handle the "fossil unversioned sync" command.
327 */
328 void sync_unversioned(void){
329 unsigned configFlags = 0;
330 unsigned syncFlags = SYNC_UNVERSIONED;
331 process_sync_args(&configFlags, &syncFlags, 1);
332 verify_all_options();
333 client_sync(syncFlags, 0, 0);
334 }
335
336 /*
337 ** COMMAND: remote-url
338 **
339 ** Usage: %fossil remote-url ?URL|off?
340
341 DDED src/unversioned.c
--- a/src/unversioned.c
+++ b/src/unversioned.c
@@ -0,0 +1,43 @@
1
+id="uvtab" output_table_sorting_java ); "SELECT"
2
+ ,"
3
+ "uvtab" output_tabid%s ORDER BY %s",
4
+ "uvtab" output_table_sorting_java );ting_java ); );
5
+ id="uvtab" output_tid="uvtab" output_ta1);
6
+ isDeleted = zHash==0if( (n++)==0 ){
7
+ id="uvtab" output_table_sorti @ <th> SHA1
8
+ }
9
+ =0 ){
10
+ id="uvtab" id="uvtab" output_table_sorting_java ); "SELECT"
11
+ ,"
12
+ "uvtab" output_tabid%s ORDER BY %s",
13
+ "uvtab" output_table_sorting_java );ting_java ); );
14
+ id="uvtab" output_tid="uvtab" output_ta1);
15
+ isDeleted = zHash==0if( (n++)==0 ){
16
+ id="uvtab" output_table_sorti @ <th> SHA1
17
+ }
18
+ name TEXT PRIMARY KEY, WITHOUT ROWID
19
+**mtime/zHashmtime/zHash URL.
20
+**
21
+** rm Remove an of all"add" and "rm"Blob hash;
22
+ Bl Kktt}r>
23
+ IS NULL,"
24
+ " sz"
25
+ "java ); "SELECT"
26
+id="uvtab" output_table_sorting_java ); "SELECT"
27
+ ,"
28
+ "uvtab" output_tabid%s ORDER BY %s",
29
+ "uvtab" output_table_sorting_java );ting_java ); );
30
+ id="uvtab" output_tid="uvtab" output_ta1);
31
+ isDeletedid="uvtab" o"%w" /*works-like:"%fossil_fat); );
32
+ id="uvtab" output_tid="uvtab" output_ta1);
33
+ isDeleted = zHash==0if( (n++)==0 ){
34
+ id="uvtab" output_table_sorti @ <th> SHA1
35
+ }
36
+ =0 ){
37
+ id="uvtab" id="uvtab" output_table_sorting_java ); "SELECT"
38
+ ,"
39
+ "uvtab" output_tabid%s ORDER BY %s",
40
+ "uvtab" output_table_sorting_java );ting_java ); );
41
+ id="uvtab" output_tid="uvtab" output_ta1);
42
+ isDeleted = zHash==0if( (n++)==0 ){
43
+ id="uvtab" output_table_sid="uvtab" output_table
--- a/src/unversioned.c
+++ b/src/unversioned.c
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/unversioned.c
+++ b/src/unversioned.c
@@ -0,0 +1,43 @@
1 id="uvtab" output_table_sorting_java ); "SELECT"
2 ,"
3 "uvtab" output_tabid%s ORDER BY %s",
4 "uvtab" output_table_sorting_java );ting_java ); );
5 id="uvtab" output_tid="uvtab" output_ta1);
6 isDeleted = zHash==0if( (n++)==0 ){
7 id="uvtab" output_table_sorti @ <th> SHA1
8 }
9 =0 ){
10 id="uvtab" id="uvtab" output_table_sorting_java ); "SELECT"
11 ,"
12 "uvtab" output_tabid%s ORDER BY %s",
13 "uvtab" output_table_sorting_java );ting_java ); );
14 id="uvtab" output_tid="uvtab" output_ta1);
15 isDeleted = zHash==0if( (n++)==0 ){
16 id="uvtab" output_table_sorti @ <th> SHA1
17 }
18 name TEXT PRIMARY KEY, WITHOUT ROWID
19 **mtime/zHashmtime/zHash URL.
20 **
21 ** rm Remove an of all"add" and "rm"Blob hash;
22 Bl Kktt}r>
23 IS NULL,"
24 " sz"
25 "java ); "SELECT"
26 id="uvtab" output_table_sorting_java ); "SELECT"
27 ,"
28 "uvtab" output_tabid%s ORDER BY %s",
29 "uvtab" output_table_sorting_java );ting_java ); );
30 id="uvtab" output_tid="uvtab" output_ta1);
31 isDeletedid="uvtab" o"%w" /*works-like:"%fossil_fat); );
32 id="uvtab" output_tid="uvtab" output_ta1);
33 isDeleted = zHash==0if( (n++)==0 ){
34 id="uvtab" output_table_sorti @ <th> SHA1
35 }
36 =0 ){
37 id="uvtab" id="uvtab" output_table_sorting_java ); "SELECT"
38 ,"
39 "uvtab" output_tabid%s ORDER BY %s",
40 "uvtab" output_table_sorting_java );ting_java ); );
41 id="uvtab" output_tid="uvtab" output_ta1);
42 isDeleted = zHash==0if( (n++)==0 ){
43 id="uvtab" output_table_sid="uvtab" output_table
+408 -16
--- src/xfer.c
+++ src/xfer.c
@@ -2,11 +2,11 @@
22
** Copyright (c) 2007 D. Richard Hipp
33
**
44
** This program is free software; you can redistribute it and/or
55
** modify it under the terms of the Simplified BSD License (also
66
** known as the "2-Clause License" or "FreeBSD License".)
7
-
7
+**
88
** This program is distributed in the hope that it will be useful,
99
** but without any warranty; without even the implied warranty of
1010
** merchantability or fitness for a particular purpose.
1111
**
1212
** Author contact information:
@@ -282,10 +282,126 @@
282282
Th_AppendToList(pzUuidList, pnUuidList, blob_str(&pXfer->aToken[1]),
283283
blob_size(&pXfer->aToken[1]));
284284
remote_has(rid);
285285
blob_reset(&content);
286286
}
287
+
288
+/*
289
+** The aToken[0..nToken-1] blob array is a parse of a "uvfile" line
290
+** message. This routine finishes parsing that message and adds the
291
+** unversioned file to the "unversioned" table.
292
+**
293
+** The file line is in one of the following two forms:
294
+**
295
+** uvfile NAME MTIME HASH SIZE FLAGS
296
+** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
297
+**
298
+** If the 0x0001 bit of FLAGS is set, that means the file has been
299
+** deleted, SIZE is zero, the HASH is "-", and the "\n CONTENT" is omitted.
300
+**
301
+** SIZE is the number of bytes of CONTENT. The CONTENT is uncompressed.
302
+** HASH is the SHA1 hash of CONTENT.
303
+**
304
+** If the 0x0004 bit of FLAGS is set, that means the CONTENT is omitted.
305
+** The sender might have omitted the content because it is too big to
306
+** transmit, or because it is unchanged and this record exists purely
307
+** to update the MTIME.
308
+*/
309
+static void xfer_accept_unversioned_file(Xfer *pXfer, int isWriter){
310
+ sqlite3_int64 mtime; /* The MTIME */
311
+ Blob *pHash; /* The HASH value */
312
+ int sz; /* The SIZE */
313
+ int flags; /* The FLAGS */
314
+ Blob content; /* The CONTENT */
315
+ Blob hash; /* Hash computed from CONTENT to compare with HASH */
316
+ Blob x; /* Compressed content */
317
+ Stmt q; /* SQL statements for comparison and insert */
318
+ int isDelete; /* HASH is "-" indicating this is a delete */
319
+ int nullContent; /* True of CONTENT is NULL */
320
+ int iStatus; /* Result from unversioned_status() */
321
+
322
+ pHash = &pXfer->aToken[3];
323
+ if( pXfer->nToken==5
324
+ || !blob_is_filename(&pXfer->aToken[1])
325
+ || !blob_is_int64(&pXfer->aToken[2], &mtime)
326
+ || (!blob_eq(pHash,"-") && !blob_is_uuid(pHash))
327
+ || !blob_is_int(&pXfer->aToken[4], &sz)
328
+ || !blob_is_int(&pXfer->aToken[5], &flags)
329
+ ){
330
+ blob_appendf(&pXfer->err, "malformed uvfile line");
331
+ return;
332
+ }
333
+ blob_init(&content, 0, 0);
334
+ blob_init(&hash, 0, 0);
335
+ blob_init(&x, 0, 0);
336
+ if( sz>0 && (flags & 0x0005)==0 ){
337
+ blob_extract(pXfer->pIn, sz, &content);
338
+ nullContent = 0;
339
+ sha1sum_blob(&content, &hash);
340
+ if( blob_compare(&hash, pHash)!=0 ){
341
+ blob_appendf(&pXfer->err, "in uvfile line, HASH does not match CONTENT");
342
+ goto end_accept_unversioned_file;
343
+ }
344
+ }else{
345
+ nullContent = 1;
346
+ }
347
+
348
+ /* The isWriter flag must be true in order to land the new file */
349
+ if( !isWriter ) goto end_accept_unversioned_file;
350
+
351
+ /* Check to see if current content really should be overwritten. Ideally,
352
+ ** a uvfile card should never have been sent unless the overwrite should
353
+ ** occur. But do not trust the sender. Double-check.
354
+ */
355
+ iStatus = unversioned_status(blob_str(&pXfer->aToken[1]), mtime,
356
+ blob_str(pHash));
357
+ if( iStatus>=3 ) goto end_accept_unversioned_file;
358
+
359
+ /* Store the content */
360
+ isDelete = blob_eq(pHash, "-");
361
+ if( isDelete ){
362
+ db_prepare(&q,
363
+ "UPDATE unversioned"
364
+ " SET rcvid=:rcvid, mtime=:mtime, hash=NULL,"
365
+ " sz=0, encoding=0, content=NULL"
366
+ " WHERE name=:name"
367
+ );
368
+ db_bind_int(&q, ":rcvid", g.rcvid);
369
+ }else if( iStatus==4 ){
370
+ db_prepare(&q, "UPDATE unversioned SET mtime=:mtime WHERE name=:name");
371
+ }else{
372
+ db_prepare(&q,
373
+ "REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,encoding,content)"
374
+ " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)"
375
+ );
376
+ db_bind_int(&q, ":rcvid", g.rcvid);
377
+ db_bind_text(&q, ":hash", blob_str(pHash));
378
+ db_bind_int(&q, ":sz", blob_size(&content));
379
+ if( !nullContent ){
380
+ blob_compress(&content, &x);
381
+ if( blob_size(&x) < 0.8*blob_size(&content) ){
382
+ db_bind_blob(&q, ":content", &x);
383
+ db_bind_int(&q, ":encoding", 1);
384
+ }else{
385
+ db_bind_blob(&q, ":content", &content);
386
+ db_bind_int(&q, ":encoding", 0);
387
+ }
388
+ }else{
389
+ db_bind_int(&q, ":encoding", 0);
390
+ }
391
+ }
392
+ db_bind_text(&q, ":name", blob_str(&pXfer->aToken[1]));
393
+ db_bind_int64(&q, ":mtime", mtime);
394
+ db_step(&q);
395
+ db_finalize(&q);
396
+ db_unset("uv-hash", 0);
397
+
398
+end_accept_unversioned_file:
399
+ blob_reset(&x);
400
+ blob_reset(&content);
401
+ blob_reset(&hash);
402
+}
287403
288404
/*
289405
** Try to send a file as a delta against its parent.
290406
** If successful, return the number of bytes in the delta.
291407
** If we cannot generate an appropriate delta, then send
@@ -524,10 +640,71 @@
524640
blob_reset(&fullContent);
525641
}
526642
}
527643
db_reset(&q1);
528644
}
645
+
646
+/*
647
+** Send the unversioned file identified by zName by generating the
648
+** appropriate "uvfile" card.
649
+**
650
+** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
651
+**
652
+** If the noContent flag is set, omit the CONTENT and set the 0x0004
653
+** flag in FLAGS.
654
+*/
655
+static void send_unversioned_file(
656
+ Xfer *pXfer, /* Transfer context */
657
+ const char *zName, /* Name of unversioned file to be sent */
658
+ int noContent /* True to omit the content */
659
+){
660
+ Stmt q1;
661
+
662
+ if( blob_size(pXfer->pOut)>=pXfer->mxSend ) noContent = 1;
663
+ if( noContent ){
664
+ db_prepare(&q1,
665
+ "SELECT mtime, hash, encoding, sz FROM unversioned WHERE name=%Q",
666
+ zName
667
+ );
668
+ }else{
669
+ db_prepare(&q1,
670
+ "SELECT mtime, hash, encoding, sz, content FROM unversioned"
671
+ " WHERE name=%Q",
672
+ zName
673
+ );
674
+ }
675
+ if( db_step(&q1)==SQLITE_ROW ){
676
+ sqlite3_int64 mtime = db_column_int64(&q1, 0);
677
+ const char *zHash = db_column_text(&q1, 1);
678
+ if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
679
+ /* If we have already reached the send size limit, send a (short)
680
+ ** uvigot card rather than a uvfile card. This only happens on the
681
+ ** server side. The uvigot card will provoke the client to resend
682
+ ** another uvgimme on the next cycle. */
683
+ blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
684
+ zName, mtime, zHash, db_column_int(&q1,3));
685
+ }else{
686
+ blob_appendf(pXfer->pOut, "uvfile %s %lld", zName, mtime);
687
+ if( zHash==0 ){
688
+ blob_append(pXfer->pOut, " - 0 1\n", -1);
689
+ }else if( noContent ){
690
+ blob_appendf(pXfer->pOut, " %s %d 4\n", zHash, db_column_int(&q1,3));
691
+ }else{
692
+ Blob content;
693
+ blob_init(&content, 0, 0);
694
+ db_column_blob(&q1, 4, &content);
695
+ if( db_column_int(&q1, 2) ){
696
+ blob_uncompress(&content, &content);
697
+ }
698
+ blob_appendf(pXfer->pOut, " %s %d 0\n", zHash, blob_size(&content));
699
+ blob_append(pXfer->pOut, blob_buffer(&content), blob_size(&content));
700
+ blob_reset(&content);
701
+ }
702
+ }
703
+ }
704
+ db_finalize(&q1);
705
+}
529706
530707
/*
531708
** Send a gimme message for every phantom.
532709
**
533710
** Except: do not request shunned artifacts. And do not request
@@ -593,11 +770,13 @@
593770
Stmt q;
594771
int rc = -1;
595772
char *zLogin = blob_terminate(pLogin);
596773
defossilize(zLogin);
597774
598
- if( fossil_strcmp(zLogin, "nobody")==0 || fossil_strcmp(zLogin,"anonymous")==0 ){
775
+ if( fossil_strcmp(zLogin, "nobody")==0
776
+ || fossil_strcmp(zLogin,"anonymous")==0
777
+ ){
599778
return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */
600779
}
601780
if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0
602781
&& db_get_boolean("remote_user_ok",0) ){
603782
return 0; /* Accept Basic Authorization */
@@ -833,10 +1012,40 @@
8331012
blob_size(&content), blob_str(&content));
8341013
blob_reset(&content);
8351014
}
8361015
}
8371016
1017
+
1018
+/*
1019
+** pXfer is a "pragma uv-hash HASH" card.
1020
+**
1021
+** If HASH is different from the unversioned content hash on this server,
1022
+** then send a bunch of uvigot cards, one for each entry unversioned file
1023
+** on this server.
1024
+*/
1025
+static void send_unversioned_catalog(Xfer *pXfer){
1026
+ unversioned_schema();
1027
+ if( !blob_eq(&pXfer->aToken[2], unversioned_content_hash(0)) ){
1028
+ int nUvIgot = 0;
1029
+ Stmt uvq;
1030
+ db_prepare(&uvq,
1031
+ "SELECT name, mtime, hash, sz FROM unversioned"
1032
+ );
1033
+ while( db_step(&uvq)==SQLITE_ROW ){
1034
+ const char *zName = db_column_text(&uvq,0);
1035
+ sqlite3_int64 mtime = db_column_int64(&uvq,1);
1036
+ const char *zHash = db_column_text(&uvq,2);
1037
+ int sz = db_column_int(&uvq,3);
1038
+ nUvIgot++;
1039
+ if( zHash==0 ){ sz = 0; zHash = "-"; }
1040
+ blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
1041
+ zName, mtime, zHash, sz);
1042
+ }
1043
+ db_finalize(&uvq);
1044
+ }
1045
+}
1046
+
8381047
/*
8391048
** Called when there is an attempt to transfer private content to and
8401049
** from a server without authorization.
8411050
*/
8421051
static void server_private_xfer_not_authorized(void){
@@ -1026,10 +1235,24 @@
10261235
@ error %T(blob_str(&xfer.err))
10271236
nErr++;
10281237
break;
10291238
}
10301239
}else
1240
+
1241
+ /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
1242
+ **
1243
+ ** Accept an unversioned file from the client.
1244
+ */
1245
+ if( blob_eq(&xfer.aToken[0], "uvfile") ){
1246
+ xfer_accept_unversioned_file(&xfer, g.perm.WrUnver);
1247
+ if( blob_size(&xfer.err) ){
1248
+ cgi_reset_content();
1249
+ @ error %T(blob_str(&xfer.err))
1250
+ nErr++;
1251
+ break;
1252
+ }
1253
+ }else
10311254
10321255
/* gimme UUID
10331256
**
10341257
** Client is requesting a file. Send it.
10351258
*/
@@ -1043,10 +1266,21 @@
10431266
if( rid ){
10441267
send_file(&xfer, rid, &xfer.aToken[1], deltaFlag);
10451268
}
10461269
}
10471270
}else
1271
+
1272
+ /* uvgimme NAME
1273
+ **
1274
+ ** Client is requesting an unversioned file. Send it.
1275
+ */
1276
+ if( blob_eq(&xfer.aToken[0], "uvgimme")
1277
+ && xfer.nToken==2
1278
+ && blob_is_filename(&xfer.aToken[1])
1279
+ ){
1280
+ send_unversioned_file(&xfer, blob_str(&xfer.aToken[1]), 0);
1281
+ }else
10481282
10491283
/* igot UUID ?ISPRIVATE?
10501284
**
10511285
** Client announces that it has a particular file. If the ISPRIVATE
10521286
** argument exists and is non-zero, then the file is a private file.
@@ -1225,11 +1459,10 @@
12251459
blob_reset(&content);
12261460
blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
12271461
}else
12281462
12291463
1230
-
12311464
/* cookie TEXT
12321465
**
12331466
** A cookie contains a arbitrary-length argument that is server-defined.
12341467
** The argument must be encoded so as not to contain any whitespace.
12351468
** The server can optionally send a cookie to the client. The client
@@ -1269,10 +1502,11 @@
12691502
** The client issue pragmas to try to influence the behavior of the
12701503
** server. These are requests only. Unknown pragmas are silently
12711504
** ignored.
12721505
*/
12731506
if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
1507
+
12741508
/* pragma send-private
12751509
**
12761510
** If the user has the "x" privilege (which must be set explicitly -
12771511
** it is not automatic with "a" or "s") then this pragma causes
12781512
** private information to be pulled in addition to public records.
@@ -1283,17 +1517,36 @@
12831517
server_private_xfer_not_authorized();
12841518
}else{
12851519
xfer.syncPrivate = 1;
12861520
}
12871521
}
1522
+
12881523
/* pragma send-catalog
12891524
**
12901525
** Send igot cards for all known artifacts.
12911526
*/
12921527
if( blob_eq(&xfer.aToken[1], "send-catalog") ){
12931528
xfer.resync = 0x7fffffff;
12941529
}
1530
+
1531
+ /* pragma uv-hash HASH
1532
+ **
1533
+ ** The client wants to make sure that unversioned files are all synced.
1534
+ ** If the HASH does not match, send a complete catalog of
1535
+ ** "uvigot" cards.
1536
+ */
1537
+ if( blob_eq(&xfer.aToken[1], "uv-hash")
1538
+ && blob_is_uuid(&xfer.aToken[2])
1539
+ ){
1540
+ if( g.perm.Read && g.perm.WrUnver ){
1541
+ @ pragma uv-push-ok
1542
+ send_unversioned_catalog(&xfer);
1543
+ }else if( g.perm.Read ){
1544
+ @ pragma uv-pull-only
1545
+ send_unversioned_catalog(&xfer);
1546
+ }
1547
+ }
12951548
}else
12961549
12971550
/* Unknown message
12981551
*/
12991552
{
@@ -1391,17 +1644,18 @@
13911644
13921645
#if INTERFACE
13931646
/*
13941647
** Flag options for controlling client_sync()
13951648
*/
1396
-#define SYNC_PUSH 0x0001
1397
-#define SYNC_PULL 0x0002
1398
-#define SYNC_CLONE 0x0004
1399
-#define SYNC_PRIVATE 0x0008
1400
-#define SYNC_VERBOSE 0x0010
1401
-#define SYNC_RESYNC 0x0020
1402
-#define SYNC_FROMPARENT 0x0080
1649
+#define SYNC_PUSH 0x0001
1650
+#define SYNC_PULL 0x0002
1651
+#define SYNC_CLONE 0x0004
1652
+#define SYNC_PRIVATE 0x0008
1653
+#define SYNC_VERBOSE 0x0010
1654
+#define SYNC_RESYNC 0x0020
1655
+#define SYNC_UNVERSIONED 0x0040
1656
+#define SYNC_FROMPARENT 0x0080
14031657
#endif
14041658
14051659
/*
14061660
** Floating-point absolute value
14071661
*/
@@ -1424,11 +1678,11 @@
14241678
){
14251679
int go = 1; /* Loop until zero */
14261680
int nCardSent = 0; /* Number of cards sent */
14271681
int nCardRcvd = 0; /* Number of cards received */
14281682
int nCycle = 0; /* Number of round trips to the server */
1429
- int size; /* Size of a config value */
1683
+ int size; /* Size of a config value or uvfile */
14301684
int origConfigRcvMask; /* Original value of configRcvMask */
14311685
int nFileRecv; /* Number of files received */
14321686
int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
14331687
const char *zCookie; /* Server cookie */
14341688
i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
@@ -1445,13 +1699,19 @@
14451699
int nRoundtrip= 0; /* Number of HTTP requests */
14461700
int nArtifactSent = 0; /* Total artifacts sent */
14471701
int nArtifactRcvd = 0; /* Total artifacts received */
14481702
const char *zOpType = 0;/* Push, Pull, Sync, Clone */
14491703
double rSkew = 0.0; /* Maximum time skew */
1704
+ int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
1705
+ int uvStatus = 0; /* 0: no I/O. 1: pull-only 2: push-and-pull */
1706
+ int uvDoPush = 0; /* Generate uvfile messages to send to server */
1707
+ int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
1708
+ int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
1709
+ sqlite3_int64 mtime; /* Modification time on a UV file */
14501710
14511711
if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
1452
- if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0
1712
+ if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
14531713
&& configRcvMask==0 && configSendMask==0 ) return 0;
14541714
if( syncFlags & SYNC_FROMPARENT ){
14551715
configRcvMask = 0;
14561716
configSendMask = 0;
14571717
syncFlags &= ~(SYNC_PUSH);
@@ -1485,10 +1745,24 @@
14851745
14861746
/* Send the send-private pragma if we are trying to sync private data */
14871747
if( syncFlags & SYNC_PRIVATE ){
14881748
blob_append(&send, "pragma send-private\n", -1);
14891749
}
1750
+
1751
+ /* When syncing unversioned files, create a TEMP table in which to store
1752
+ ** the names of files that do not need to be sent from client to server.
1753
+ */
1754
+ if( syncFlags & SYNC_UNVERSIONED ){
1755
+ db_multi_exec(
1756
+ "CREATE TEMP TABLE uv_tosend("
1757
+ " name TEXT PRIMARY KEY,"
1758
+ " mtimeOnly BOOLEAN"
1759
+ ") WITHOUT ROWID;"
1760
+ "INSERT INTO uv_toSend(name,mtimeOnly)"
1761
+ " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;"
1762
+ );
1763
+ }
14901764
14911765
/*
14921766
** Always begin with a clone, pull, or push message
14931767
*/
14941768
if( syncFlags & SYNC_CLONE ){
@@ -1526,11 +1800,11 @@
15261800
db_multi_exec(
15271801
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
15281802
);
15291803
manifest_crosslink_begin();
15301804
1531
- /* Send make the most recently received cookie. Let the server
1805
+ /* Send back the most recently received cookie. Let the server
15321806
** figure out if this is a cookie that it cares about.
15331807
*/
15341808
zCookie = db_get("cookie", 0);
15351809
if( zCookie ){
15361810
blob_appendf(&send, "cookie %s\n", zCookie);
@@ -1570,10 +1844,23 @@
15701844
configure_prepare_to_receive(overwrite);
15711845
}
15721846
origConfigRcvMask = configRcvMask;
15731847
configRcvMask = 0;
15741848
}
1849
+
1850
+ /* Send a request to sync unversioned files. On a clone, delay sending
1851
+ ** this until the second cycle since the login card might fail on
1852
+ ** the first cycle.
1853
+ */
1854
+ if( (syncFlags & SYNC_UNVERSIONED)!=0
1855
+ && ((syncFlags & SYNC_CLONE)==0 || nCycle>0)
1856
+ && !uvHashSent
1857
+ ){
1858
+ blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0));
1859
+ nCardSent++;
1860
+ uvHashSent = 1;
1861
+ }
15751862
15761863
/* Send configuration parameters being pushed */
15771864
if( configSendMask ){
15781865
if( zOpType==0 ) zOpType = "Push";
15791866
if( configSendMask & CONFIGSET_OLDFORMAT ){
@@ -1587,10 +1874,31 @@
15871874
}else{
15881875
nCardSent += configure_send_group(xfer.pOut, configSendMask, 0);
15891876
}
15901877
configSendMask = 0;
15911878
}
1879
+
1880
+ /* Send unversioned files present here on the client but missing or
1881
+ ** obsolete on the server.
1882
+ */
1883
+ if( uvDoPush ){
1884
+ Stmt uvq;
1885
+ int rc = SQLITE_OK;
1886
+ assert( (syncFlags & SYNC_UNVERSIONED)!=0 );
1887
+ assert( uvStatus==2 );
1888
+ db_prepare(&uvq, "SELECT name, mtimeOnly FROM uv_tosend");
1889
+ while( (rc = db_step(&uvq))==SQLITE_ROW ){
1890
+ const char *zName = db_column_text(&uvq, 0);
1891
+ send_unversioned_file(&xfer, zName, db_column_int(&uvq,1));
1892
+ nCardSent++;
1893
+ nArtifactSent++;
1894
+ db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
1895
+ if( blob_size(xfer.pOut)>xfer.mxSend ) break;
1896
+ }
1897
+ db_finalize(&uvq);
1898
+ if( rc==SQLITE_DONE ) uvDoPush = 0;
1899
+ }
15921900
15931901
/* Append randomness to the end of the message. This makes all
15941902
** messages unique so that that the login-card nonce will always
15951903
** be unique.
15961904
*/
@@ -1647,10 +1955,12 @@
16471955
if( syncFlags & SYNC_PUSH ){
16481956
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
16491957
nCardSent++;
16501958
}
16511959
go = 0;
1960
+ nUvGimmeSent = 0;
1961
+ nUvFileRcvd = 0;
16521962
16531963
/* Process the reply that came back from the server */
16541964
while( blob_line(&recv, &xfer.line) ){
16551965
if( blob_buffer(&xfer.line)[0]=='#' ){
16561966
const char *zLine = blob_buffer(&xfer.line);
@@ -1659,11 +1969,13 @@
16591969
double rDiff;
16601970
sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]);
16611971
rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g",
16621972
zTime, rArrivalTime);
16631973
if( rDiff>9e98 || rDiff<-9e98 ) rDiff = 0.0;
1664
- if( rDiff*24.0*3600.0 >= -(blob_size(&recv)/5000.0 + 20) ) rDiff = 0.0;
1974
+ if( rDiff*24.0*3600.0 >= -(blob_size(&recv)/5000.0 + 20) ){
1975
+ rDiff = 0.0;
1976
+ }
16651977
if( fossil_fabs(rDiff)>fossil_fabs(rSkew) ) rSkew = rDiff;
16661978
}
16671979
nCardRcvd++;
16681980
continue;
16691981
}
@@ -1695,10 +2007,20 @@
16952007
*/
16962008
if( blob_eq(&xfer.aToken[0],"cfile") ){
16972009
xfer_accept_compressed_file(&xfer, 0, 0);
16982010
nArtifactRcvd++;
16992011
}else
2012
+
2013
+ /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
2014
+ **
2015
+ ** Accept an unversioned file from the client.
2016
+ */
2017
+ if( blob_eq(&xfer.aToken[0], "uvfile") ){
2018
+ xfer_accept_unversioned_file(&xfer, 1);
2019
+ nArtifactRcvd++;
2020
+ nUvFileRcvd++;
2021
+ }else
17002022
17012023
/* gimme UUID
17022024
**
17032025
** Server is requesting a file. If the file is a manifest, assume
17042026
** that the server will also want to know all of the content files
@@ -1742,10 +2064,63 @@
17422064
if( rid ) newPhantom = 1;
17432065
}
17442066
remote_has(rid);
17452067
}else
17462068
2069
+ /* uvigot NAME MTIME HASH SIZE
2070
+ **
2071
+ ** Server announces that it has a particular unversioned file. The
2072
+ ** server will only send this card if the client had previously sent
2073
+ ** a "pragma uv-hash" card with a hash that does not match.
2074
+ **
2075
+ ** If the identified file needs to be transferred, then setup for the
2076
+ ** transfer. Generate a "uvgimme" card in the reply if the server
2077
+ ** version is newer than the client. Generate a "uvfile" card if
2078
+ ** the client version is newer than the server. If HASH is "-"
2079
+ ** (indicating that the file has been deleted) and MTIME is newer,
2080
+ ** then do the deletion.
2081
+ */
2082
+ if( xfer.nToken==5
2083
+ && blob_eq(&xfer.aToken[0], "uvigot")
2084
+ && blob_is_filename(&xfer.aToken[1])
2085
+ && blob_is_int64(&xfer.aToken[2], &mtime)
2086
+ && blob_is_int(&xfer.aToken[4], &size)
2087
+ && (blob_eq(&xfer.aToken[3],"-") || blob_is_uuid(&xfer.aToken[3]))
2088
+ ){
2089
+ const char *zName = blob_str(&xfer.aToken[1]);
2090
+ const char *zHash = blob_str(&xfer.aToken[3]);
2091
+ int iStatus;
2092
+ if( uvStatus==0 ) uvStatus = 2;
2093
+ iStatus = unversioned_status(zName, mtime, zHash);
2094
+ if( iStatus<=1 ){
2095
+ if( zHash[0]!='-' ){
2096
+ blob_appendf(xfer.pOut, "uvgimme %s\n", zName);
2097
+ nCardSent++;
2098
+ nUvGimmeSent++;
2099
+ }else if( iStatus==1 ){
2100
+ db_multi_exec(
2101
+ "UPDATE unversioned"
2102
+ " SET mtime=%lld, hash=NULL, sz=0, encoding=0, content=NULL"
2103
+ " WHERE name=%Q", mtime, zName
2104
+ );
2105
+ db_unset("uv-hash", 0);
2106
+ }
2107
+ }else if( iStatus==2 ){
2108
+ db_multi_exec(
2109
+ "UPDATE unversioned SET mtime=%lld WHERE name=%Q", mtime, zName
2110
+ );
2111
+ db_unset("uv-hash", 0);
2112
+ }
2113
+ if( iStatus<=3 ){
2114
+ db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
2115
+ }else if( iStatus==4 ){
2116
+ db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q",zName);
2117
+ }else if( iStatus==5 ){
2118
+ db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)",
2119
+ zName);
2120
+ }
2121
+ }else
17472122
17482123
/* push SERVERCODE PRODUCTCODE
17492124
**
17502125
** Should only happen in response to a clone. This message tells
17512126
** the client what product to use for the new database.
@@ -1828,11 +2203,12 @@
18282203
** to the next cycle.
18292204
*/
18302205
if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
18312206
char *zMsg = blob_terminate(&xfer.aToken[1]);
18322207
defossilize(zMsg);
1833
- if( (syncFlags & SYNC_PUSH) && zMsg && sqlite3_strglob("pull only *", zMsg)==0 ){
2208
+ if( (syncFlags & SYNC_PUSH) && zMsg
2209
+ && sqlite3_strglob("pull only *", zMsg)==0 ){
18342210
syncFlags &= ~SYNC_PUSH;
18352211
zMsg = 0;
18362212
}
18372213
if( zMsg && zMsg[0] ){
18382214
fossil_force_newline();
@@ -1845,10 +2221,22 @@
18452221
** The server can send pragmas to try to convey meta-information to
18462222
** the client. These are informational only. Unknown pragmas are
18472223
** silently ignored.
18482224
*/
18492225
if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
2226
+ /* If the server is unwill to accept new unversioned content (because
2227
+ ** this client lacks the necessary permissions) then it sends a
2228
+ ** "uv-pull-only" pragma so that the client will know not to waste
2229
+ ** bandwidth trying to upload unversioned content. If the server
2230
+ ** does accept new unversioned content, it sends "uv-push-ok".
2231
+ */
2232
+ if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){
2233
+ uvStatus = 1;
2234
+ }else if( blob_eq(&xfer.aToken[1], "uv-push-ok") ){
2235
+ uvStatus = 2;
2236
+ uvDoPush = 1;
2237
+ }
18502238
}else
18512239
18522240
/* error MESSAGE
18532241
**
18542242
** Report an error and abandon the sync session.
@@ -1943,13 +2331,17 @@
19432331
xfer.nDanglingFile = 0;
19442332
19452333
/* If we have one or more files queued to send, then go
19462334
** another round
19472335
*/
1948
- if( xfer.nFileSent+xfer.nDeltaSent>0 ){
2336
+ if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){
19492337
go = 1;
19502338
}
2339
+
2340
+ /* Continue looping as long as new uvfile cards are being received
2341
+ ** and uvgimme cards are being sent. */
2342
+ if( nUvGimmeSent>0 && nUvFileRcvd>0 ) go = 1;
19512343
19522344
/* If this is a clone, the go at least two rounds */
19532345
if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
19542346
19552347
/* Stop the cycle if the server sends a "clone_seqno 0" card and
19562348
--- src/xfer.c
+++ src/xfer.c
@@ -2,11 +2,11 @@
2 ** Copyright (c) 2007 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
@@ -282,10 +282,126 @@
282 Th_AppendToList(pzUuidList, pnUuidList, blob_str(&pXfer->aToken[1]),
283 blob_size(&pXfer->aToken[1]));
284 remote_has(rid);
285 blob_reset(&content);
286 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
288 /*
289 ** Try to send a file as a delta against its parent.
290 ** If successful, return the number of bytes in the delta.
291 ** If we cannot generate an appropriate delta, then send
@@ -524,10 +640,71 @@
524 blob_reset(&fullContent);
525 }
526 }
527 db_reset(&q1);
528 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
529
530 /*
531 ** Send a gimme message for every phantom.
532 **
533 ** Except: do not request shunned artifacts. And do not request
@@ -593,11 +770,13 @@
593 Stmt q;
594 int rc = -1;
595 char *zLogin = blob_terminate(pLogin);
596 defossilize(zLogin);
597
598 if( fossil_strcmp(zLogin, "nobody")==0 || fossil_strcmp(zLogin,"anonymous")==0 ){
 
 
599 return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */
600 }
601 if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0
602 && db_get_boolean("remote_user_ok",0) ){
603 return 0; /* Accept Basic Authorization */
@@ -833,10 +1012,40 @@
833 blob_size(&content), blob_str(&content));
834 blob_reset(&content);
835 }
836 }
837
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838 /*
839 ** Called when there is an attempt to transfer private content to and
840 ** from a server without authorization.
841 */
842 static void server_private_xfer_not_authorized(void){
@@ -1026,10 +1235,24 @@
1026 @ error %T(blob_str(&xfer.err))
1027 nErr++;
1028 break;
1029 }
1030 }else
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1031
1032 /* gimme UUID
1033 **
1034 ** Client is requesting a file. Send it.
1035 */
@@ -1043,10 +1266,21 @@
1043 if( rid ){
1044 send_file(&xfer, rid, &xfer.aToken[1], deltaFlag);
1045 }
1046 }
1047 }else
 
 
 
 
 
 
 
 
 
 
 
1048
1049 /* igot UUID ?ISPRIVATE?
1050 **
1051 ** Client announces that it has a particular file. If the ISPRIVATE
1052 ** argument exists and is non-zero, then the file is a private file.
@@ -1225,11 +1459,10 @@
1225 blob_reset(&content);
1226 blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
1227 }else
1228
1229
1230
1231 /* cookie TEXT
1232 **
1233 ** A cookie contains a arbitrary-length argument that is server-defined.
1234 ** The argument must be encoded so as not to contain any whitespace.
1235 ** The server can optionally send a cookie to the client. The client
@@ -1269,10 +1502,11 @@
1269 ** The client issue pragmas to try to influence the behavior of the
1270 ** server. These are requests only. Unknown pragmas are silently
1271 ** ignored.
1272 */
1273 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
 
1274 /* pragma send-private
1275 **
1276 ** If the user has the "x" privilege (which must be set explicitly -
1277 ** it is not automatic with "a" or "s") then this pragma causes
1278 ** private information to be pulled in addition to public records.
@@ -1283,17 +1517,36 @@
1283 server_private_xfer_not_authorized();
1284 }else{
1285 xfer.syncPrivate = 1;
1286 }
1287 }
 
1288 /* pragma send-catalog
1289 **
1290 ** Send igot cards for all known artifacts.
1291 */
1292 if( blob_eq(&xfer.aToken[1], "send-catalog") ){
1293 xfer.resync = 0x7fffffff;
1294 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1295 }else
1296
1297 /* Unknown message
1298 */
1299 {
@@ -1391,17 +1644,18 @@
1391
1392 #if INTERFACE
1393 /*
1394 ** Flag options for controlling client_sync()
1395 */
1396 #define SYNC_PUSH 0x0001
1397 #define SYNC_PULL 0x0002
1398 #define SYNC_CLONE 0x0004
1399 #define SYNC_PRIVATE 0x0008
1400 #define SYNC_VERBOSE 0x0010
1401 #define SYNC_RESYNC 0x0020
1402 #define SYNC_FROMPARENT 0x0080
 
1403 #endif
1404
1405 /*
1406 ** Floating-point absolute value
1407 */
@@ -1424,11 +1678,11 @@
1424 ){
1425 int go = 1; /* Loop until zero */
1426 int nCardSent = 0; /* Number of cards sent */
1427 int nCardRcvd = 0; /* Number of cards received */
1428 int nCycle = 0; /* Number of round trips to the server */
1429 int size; /* Size of a config value */
1430 int origConfigRcvMask; /* Original value of configRcvMask */
1431 int nFileRecv; /* Number of files received */
1432 int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
1433 const char *zCookie; /* Server cookie */
1434 i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
@@ -1445,13 +1699,19 @@
1445 int nRoundtrip= 0; /* Number of HTTP requests */
1446 int nArtifactSent = 0; /* Total artifacts sent */
1447 int nArtifactRcvd = 0; /* Total artifacts received */
1448 const char *zOpType = 0;/* Push, Pull, Sync, Clone */
1449 double rSkew = 0.0; /* Maximum time skew */
 
 
 
 
 
 
1450
1451 if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
1452 if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0
1453 && configRcvMask==0 && configSendMask==0 ) return 0;
1454 if( syncFlags & SYNC_FROMPARENT ){
1455 configRcvMask = 0;
1456 configSendMask = 0;
1457 syncFlags &= ~(SYNC_PUSH);
@@ -1485,10 +1745,24 @@
1485
1486 /* Send the send-private pragma if we are trying to sync private data */
1487 if( syncFlags & SYNC_PRIVATE ){
1488 blob_append(&send, "pragma send-private\n", -1);
1489 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1490
1491 /*
1492 ** Always begin with a clone, pull, or push message
1493 */
1494 if( syncFlags & SYNC_CLONE ){
@@ -1526,11 +1800,11 @@
1526 db_multi_exec(
1527 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1528 );
1529 manifest_crosslink_begin();
1530
1531 /* Send make the most recently received cookie. Let the server
1532 ** figure out if this is a cookie that it cares about.
1533 */
1534 zCookie = db_get("cookie", 0);
1535 if( zCookie ){
1536 blob_appendf(&send, "cookie %s\n", zCookie);
@@ -1570,10 +1844,23 @@
1570 configure_prepare_to_receive(overwrite);
1571 }
1572 origConfigRcvMask = configRcvMask;
1573 configRcvMask = 0;
1574 }
 
 
 
 
 
 
 
 
 
 
 
 
 
1575
1576 /* Send configuration parameters being pushed */
1577 if( configSendMask ){
1578 if( zOpType==0 ) zOpType = "Push";
1579 if( configSendMask & CONFIGSET_OLDFORMAT ){
@@ -1587,10 +1874,31 @@
1587 }else{
1588 nCardSent += configure_send_group(xfer.pOut, configSendMask, 0);
1589 }
1590 configSendMask = 0;
1591 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1592
1593 /* Append randomness to the end of the message. This makes all
1594 ** messages unique so that that the login-card nonce will always
1595 ** be unique.
1596 */
@@ -1647,10 +1955,12 @@
1647 if( syncFlags & SYNC_PUSH ){
1648 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1649 nCardSent++;
1650 }
1651 go = 0;
 
 
1652
1653 /* Process the reply that came back from the server */
1654 while( blob_line(&recv, &xfer.line) ){
1655 if( blob_buffer(&xfer.line)[0]=='#' ){
1656 const char *zLine = blob_buffer(&xfer.line);
@@ -1659,11 +1969,13 @@
1659 double rDiff;
1660 sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]);
1661 rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g",
1662 zTime, rArrivalTime);
1663 if( rDiff>9e98 || rDiff<-9e98 ) rDiff = 0.0;
1664 if( rDiff*24.0*3600.0 >= -(blob_size(&recv)/5000.0 + 20) ) rDiff = 0.0;
 
 
1665 if( fossil_fabs(rDiff)>fossil_fabs(rSkew) ) rSkew = rDiff;
1666 }
1667 nCardRcvd++;
1668 continue;
1669 }
@@ -1695,10 +2007,20 @@
1695 */
1696 if( blob_eq(&xfer.aToken[0],"cfile") ){
1697 xfer_accept_compressed_file(&xfer, 0, 0);
1698 nArtifactRcvd++;
1699 }else
 
 
 
 
 
 
 
 
 
 
1700
1701 /* gimme UUID
1702 **
1703 ** Server is requesting a file. If the file is a manifest, assume
1704 ** that the server will also want to know all of the content files
@@ -1742,10 +2064,63 @@
1742 if( rid ) newPhantom = 1;
1743 }
1744 remote_has(rid);
1745 }else
1746
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1747
1748 /* push SERVERCODE PRODUCTCODE
1749 **
1750 ** Should only happen in response to a clone. This message tells
1751 ** the client what product to use for the new database.
@@ -1828,11 +2203,12 @@
1828 ** to the next cycle.
1829 */
1830 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
1831 char *zMsg = blob_terminate(&xfer.aToken[1]);
1832 defossilize(zMsg);
1833 if( (syncFlags & SYNC_PUSH) && zMsg && sqlite3_strglob("pull only *", zMsg)==0 ){
 
1834 syncFlags &= ~SYNC_PUSH;
1835 zMsg = 0;
1836 }
1837 if( zMsg && zMsg[0] ){
1838 fossil_force_newline();
@@ -1845,10 +2221,22 @@
1845 ** The server can send pragmas to try to convey meta-information to
1846 ** the client. These are informational only. Unknown pragmas are
1847 ** silently ignored.
1848 */
1849 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
 
 
 
 
 
 
 
 
 
 
 
 
1850 }else
1851
1852 /* error MESSAGE
1853 **
1854 ** Report an error and abandon the sync session.
@@ -1943,13 +2331,17 @@
1943 xfer.nDanglingFile = 0;
1944
1945 /* If we have one or more files queued to send, then go
1946 ** another round
1947 */
1948 if( xfer.nFileSent+xfer.nDeltaSent>0 ){
1949 go = 1;
1950 }
 
 
 
 
1951
1952 /* If this is a clone, the go at least two rounds */
1953 if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
1954
1955 /* Stop the cycle if the server sends a "clone_seqno 0" card and
1956
--- src/xfer.c
+++ src/xfer.c
@@ -2,11 +2,11 @@
2 ** Copyright (c) 2007 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7 **
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
@@ -282,10 +282,126 @@
282 Th_AppendToList(pzUuidList, pnUuidList, blob_str(&pXfer->aToken[1]),
283 blob_size(&pXfer->aToken[1]));
284 remote_has(rid);
285 blob_reset(&content);
286 }
287
288 /*
289 ** The aToken[0..nToken-1] blob array is a parse of a "uvfile" line
290 ** message. This routine finishes parsing that message and adds the
291 ** unversioned file to the "unversioned" table.
292 **
293 ** The file line is in one of the following two forms:
294 **
295 ** uvfile NAME MTIME HASH SIZE FLAGS
296 ** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
297 **
298 ** If the 0x0001 bit of FLAGS is set, that means the file has been
299 ** deleted, SIZE is zero, the HASH is "-", and the "\n CONTENT" is omitted.
300 **
301 ** SIZE is the number of bytes of CONTENT. The CONTENT is uncompressed.
302 ** HASH is the SHA1 hash of CONTENT.
303 **
304 ** If the 0x0004 bit of FLAGS is set, that means the CONTENT is omitted.
305 ** The sender might have omitted the content because it is too big to
306 ** transmit, or because it is unchanged and this record exists purely
307 ** to update the MTIME.
308 */
309 static void xfer_accept_unversioned_file(Xfer *pXfer, int isWriter){
310 sqlite3_int64 mtime; /* The MTIME */
311 Blob *pHash; /* The HASH value */
312 int sz; /* The SIZE */
313 int flags; /* The FLAGS */
314 Blob content; /* The CONTENT */
315 Blob hash; /* Hash computed from CONTENT to compare with HASH */
316 Blob x; /* Compressed content */
317 Stmt q; /* SQL statements for comparison and insert */
318 int isDelete; /* HASH is "-" indicating this is a delete */
319 int nullContent; /* True of CONTENT is NULL */
320 int iStatus; /* Result from unversioned_status() */
321
322 pHash = &pXfer->aToken[3];
323 if( pXfer->nToken==5
324 || !blob_is_filename(&pXfer->aToken[1])
325 || !blob_is_int64(&pXfer->aToken[2], &mtime)
326 || (!blob_eq(pHash,"-") && !blob_is_uuid(pHash))
327 || !blob_is_int(&pXfer->aToken[4], &sz)
328 || !blob_is_int(&pXfer->aToken[5], &flags)
329 ){
330 blob_appendf(&pXfer->err, "malformed uvfile line");
331 return;
332 }
333 blob_init(&content, 0, 0);
334 blob_init(&hash, 0, 0);
335 blob_init(&x, 0, 0);
336 if( sz>0 && (flags & 0x0005)==0 ){
337 blob_extract(pXfer->pIn, sz, &content);
338 nullContent = 0;
339 sha1sum_blob(&content, &hash);
340 if( blob_compare(&hash, pHash)!=0 ){
341 blob_appendf(&pXfer->err, "in uvfile line, HASH does not match CONTENT");
342 goto end_accept_unversioned_file;
343 }
344 }else{
345 nullContent = 1;
346 }
347
348 /* The isWriter flag must be true in order to land the new file */
349 if( !isWriter ) goto end_accept_unversioned_file;
350
351 /* Check to see if current content really should be overwritten. Ideally,
352 ** a uvfile card should never have been sent unless the overwrite should
353 ** occur. But do not trust the sender. Double-check.
354 */
355 iStatus = unversioned_status(blob_str(&pXfer->aToken[1]), mtime,
356 blob_str(pHash));
357 if( iStatus>=3 ) goto end_accept_unversioned_file;
358
359 /* Store the content */
360 isDelete = blob_eq(pHash, "-");
361 if( isDelete ){
362 db_prepare(&q,
363 "UPDATE unversioned"
364 " SET rcvid=:rcvid, mtime=:mtime, hash=NULL,"
365 " sz=0, encoding=0, content=NULL"
366 " WHERE name=:name"
367 );
368 db_bind_int(&q, ":rcvid", g.rcvid);
369 }else if( iStatus==4 ){
370 db_prepare(&q, "UPDATE unversioned SET mtime=:mtime WHERE name=:name");
371 }else{
372 db_prepare(&q,
373 "REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,encoding,content)"
374 " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)"
375 );
376 db_bind_int(&q, ":rcvid", g.rcvid);
377 db_bind_text(&q, ":hash", blob_str(pHash));
378 db_bind_int(&q, ":sz", blob_size(&content));
379 if( !nullContent ){
380 blob_compress(&content, &x);
381 if( blob_size(&x) < 0.8*blob_size(&content) ){
382 db_bind_blob(&q, ":content", &x);
383 db_bind_int(&q, ":encoding", 1);
384 }else{
385 db_bind_blob(&q, ":content", &content);
386 db_bind_int(&q, ":encoding", 0);
387 }
388 }else{
389 db_bind_int(&q, ":encoding", 0);
390 }
391 }
392 db_bind_text(&q, ":name", blob_str(&pXfer->aToken[1]));
393 db_bind_int64(&q, ":mtime", mtime);
394 db_step(&q);
395 db_finalize(&q);
396 db_unset("uv-hash", 0);
397
398 end_accept_unversioned_file:
399 blob_reset(&x);
400 blob_reset(&content);
401 blob_reset(&hash);
402 }
403
404 /*
405 ** Try to send a file as a delta against its parent.
406 ** If successful, return the number of bytes in the delta.
407 ** If we cannot generate an appropriate delta, then send
@@ -524,10 +640,71 @@
640 blob_reset(&fullContent);
641 }
642 }
643 db_reset(&q1);
644 }
645
646 /*
647 ** Send the unversioned file identified by zName by generating the
648 ** appropriate "uvfile" card.
649 **
650 ** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
651 **
652 ** If the noContent flag is set, omit the CONTENT and set the 0x0004
653 ** flag in FLAGS.
654 */
655 static void send_unversioned_file(
656 Xfer *pXfer, /* Transfer context */
657 const char *zName, /* Name of unversioned file to be sent */
658 int noContent /* True to omit the content */
659 ){
660 Stmt q1;
661
662 if( blob_size(pXfer->pOut)>=pXfer->mxSend ) noContent = 1;
663 if( noContent ){
664 db_prepare(&q1,
665 "SELECT mtime, hash, encoding, sz FROM unversioned WHERE name=%Q",
666 zName
667 );
668 }else{
669 db_prepare(&q1,
670 "SELECT mtime, hash, encoding, sz, content FROM unversioned"
671 " WHERE name=%Q",
672 zName
673 );
674 }
675 if( db_step(&q1)==SQLITE_ROW ){
676 sqlite3_int64 mtime = db_column_int64(&q1, 0);
677 const char *zHash = db_column_text(&q1, 1);
678 if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
679 /* If we have already reached the send size limit, send a (short)
680 ** uvigot card rather than a uvfile card. This only happens on the
681 ** server side. The uvigot card will provoke the client to resend
682 ** another uvgimme on the next cycle. */
683 blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
684 zName, mtime, zHash, db_column_int(&q1,3));
685 }else{
686 blob_appendf(pXfer->pOut, "uvfile %s %lld", zName, mtime);
687 if( zHash==0 ){
688 blob_append(pXfer->pOut, " - 0 1\n", -1);
689 }else if( noContent ){
690 blob_appendf(pXfer->pOut, " %s %d 4\n", zHash, db_column_int(&q1,3));
691 }else{
692 Blob content;
693 blob_init(&content, 0, 0);
694 db_column_blob(&q1, 4, &content);
695 if( db_column_int(&q1, 2) ){
696 blob_uncompress(&content, &content);
697 }
698 blob_appendf(pXfer->pOut, " %s %d 0\n", zHash, blob_size(&content));
699 blob_append(pXfer->pOut, blob_buffer(&content), blob_size(&content));
700 blob_reset(&content);
701 }
702 }
703 }
704 db_finalize(&q1);
705 }
706
707 /*
708 ** Send a gimme message for every phantom.
709 **
710 ** Except: do not request shunned artifacts. And do not request
@@ -593,11 +770,13 @@
770 Stmt q;
771 int rc = -1;
772 char *zLogin = blob_terminate(pLogin);
773 defossilize(zLogin);
774
775 if( fossil_strcmp(zLogin, "nobody")==0
776 || fossil_strcmp(zLogin,"anonymous")==0
777 ){
778 return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */
779 }
780 if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0
781 && db_get_boolean("remote_user_ok",0) ){
782 return 0; /* Accept Basic Authorization */
@@ -833,10 +1012,40 @@
1012 blob_size(&content), blob_str(&content));
1013 blob_reset(&content);
1014 }
1015 }
1016
1017
1018 /*
1019 ** pXfer is a "pragma uv-hash HASH" card.
1020 **
1021 ** If HASH is different from the unversioned content hash on this server,
1022 ** then send a bunch of uvigot cards, one for each entry unversioned file
1023 ** on this server.
1024 */
1025 static void send_unversioned_catalog(Xfer *pXfer){
1026 unversioned_schema();
1027 if( !blob_eq(&pXfer->aToken[2], unversioned_content_hash(0)) ){
1028 int nUvIgot = 0;
1029 Stmt uvq;
1030 db_prepare(&uvq,
1031 "SELECT name, mtime, hash, sz FROM unversioned"
1032 );
1033 while( db_step(&uvq)==SQLITE_ROW ){
1034 const char *zName = db_column_text(&uvq,0);
1035 sqlite3_int64 mtime = db_column_int64(&uvq,1);
1036 const char *zHash = db_column_text(&uvq,2);
1037 int sz = db_column_int(&uvq,3);
1038 nUvIgot++;
1039 if( zHash==0 ){ sz = 0; zHash = "-"; }
1040 blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
1041 zName, mtime, zHash, sz);
1042 }
1043 db_finalize(&uvq);
1044 }
1045 }
1046
1047 /*
1048 ** Called when there is an attempt to transfer private content to and
1049 ** from a server without authorization.
1050 */
1051 static void server_private_xfer_not_authorized(void){
@@ -1026,10 +1235,24 @@
1235 @ error %T(blob_str(&xfer.err))
1236 nErr++;
1237 break;
1238 }
1239 }else
1240
1241 /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
1242 **
1243 ** Accept an unversioned file from the client.
1244 */
1245 if( blob_eq(&xfer.aToken[0], "uvfile") ){
1246 xfer_accept_unversioned_file(&xfer, g.perm.WrUnver);
1247 if( blob_size(&xfer.err) ){
1248 cgi_reset_content();
1249 @ error %T(blob_str(&xfer.err))
1250 nErr++;
1251 break;
1252 }
1253 }else
1254
1255 /* gimme UUID
1256 **
1257 ** Client is requesting a file. Send it.
1258 */
@@ -1043,10 +1266,21 @@
1266 if( rid ){
1267 send_file(&xfer, rid, &xfer.aToken[1], deltaFlag);
1268 }
1269 }
1270 }else
1271
1272 /* uvgimme NAME
1273 **
1274 ** Client is requesting an unversioned file. Send it.
1275 */
1276 if( blob_eq(&xfer.aToken[0], "uvgimme")
1277 && xfer.nToken==2
1278 && blob_is_filename(&xfer.aToken[1])
1279 ){
1280 send_unversioned_file(&xfer, blob_str(&xfer.aToken[1]), 0);
1281 }else
1282
1283 /* igot UUID ?ISPRIVATE?
1284 **
1285 ** Client announces that it has a particular file. If the ISPRIVATE
1286 ** argument exists and is non-zero, then the file is a private file.
@@ -1225,11 +1459,10 @@
1459 blob_reset(&content);
1460 blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
1461 }else
1462
1463
 
1464 /* cookie TEXT
1465 **
1466 ** A cookie contains a arbitrary-length argument that is server-defined.
1467 ** The argument must be encoded so as not to contain any whitespace.
1468 ** The server can optionally send a cookie to the client. The client
@@ -1269,10 +1502,11 @@
1502 ** The client issue pragmas to try to influence the behavior of the
1503 ** server. These are requests only. Unknown pragmas are silently
1504 ** ignored.
1505 */
1506 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
1507
1508 /* pragma send-private
1509 **
1510 ** If the user has the "x" privilege (which must be set explicitly -
1511 ** it is not automatic with "a" or "s") then this pragma causes
1512 ** private information to be pulled in addition to public records.
@@ -1283,17 +1517,36 @@
1517 server_private_xfer_not_authorized();
1518 }else{
1519 xfer.syncPrivate = 1;
1520 }
1521 }
1522
1523 /* pragma send-catalog
1524 **
1525 ** Send igot cards for all known artifacts.
1526 */
1527 if( blob_eq(&xfer.aToken[1], "send-catalog") ){
1528 xfer.resync = 0x7fffffff;
1529 }
1530
1531 /* pragma uv-hash HASH
1532 **
1533 ** The client wants to make sure that unversioned files are all synced.
1534 ** If the HASH does not match, send a complete catalog of
1535 ** "uvigot" cards.
1536 */
1537 if( blob_eq(&xfer.aToken[1], "uv-hash")
1538 && blob_is_uuid(&xfer.aToken[2])
1539 ){
1540 if( g.perm.Read && g.perm.WrUnver ){
1541 @ pragma uv-push-ok
1542 send_unversioned_catalog(&xfer);
1543 }else if( g.perm.Read ){
1544 @ pragma uv-pull-only
1545 send_unversioned_catalog(&xfer);
1546 }
1547 }
1548 }else
1549
1550 /* Unknown message
1551 */
1552 {
@@ -1391,17 +1644,18 @@
1644
1645 #if INTERFACE
1646 /*
1647 ** Flag options for controlling client_sync()
1648 */
1649 #define SYNC_PUSH 0x0001
1650 #define SYNC_PULL 0x0002
1651 #define SYNC_CLONE 0x0004
1652 #define SYNC_PRIVATE 0x0008
1653 #define SYNC_VERBOSE 0x0010
1654 #define SYNC_RESYNC 0x0020
1655 #define SYNC_UNVERSIONED 0x0040
1656 #define SYNC_FROMPARENT 0x0080
1657 #endif
1658
1659 /*
1660 ** Floating-point absolute value
1661 */
@@ -1424,11 +1678,11 @@
1678 ){
1679 int go = 1; /* Loop until zero */
1680 int nCardSent = 0; /* Number of cards sent */
1681 int nCardRcvd = 0; /* Number of cards received */
1682 int nCycle = 0; /* Number of round trips to the server */
1683 int size; /* Size of a config value or uvfile */
1684 int origConfigRcvMask; /* Original value of configRcvMask */
1685 int nFileRecv; /* Number of files received */
1686 int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
1687 const char *zCookie; /* Server cookie */
1688 i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
@@ -1445,13 +1699,19 @@
1699 int nRoundtrip= 0; /* Number of HTTP requests */
1700 int nArtifactSent = 0; /* Total artifacts sent */
1701 int nArtifactRcvd = 0; /* Total artifacts received */
1702 const char *zOpType = 0;/* Push, Pull, Sync, Clone */
1703 double rSkew = 0.0; /* Maximum time skew */
1704 int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
1705 int uvStatus = 0; /* 0: no I/O. 1: pull-only 2: push-and-pull */
1706 int uvDoPush = 0; /* Generate uvfile messages to send to server */
1707 int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
1708 int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
1709 sqlite3_int64 mtime; /* Modification time on a UV file */
1710
1711 if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
1712 if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
1713 && configRcvMask==0 && configSendMask==0 ) return 0;
1714 if( syncFlags & SYNC_FROMPARENT ){
1715 configRcvMask = 0;
1716 configSendMask = 0;
1717 syncFlags &= ~(SYNC_PUSH);
@@ -1485,10 +1745,24 @@
1745
1746 /* Send the send-private pragma if we are trying to sync private data */
1747 if( syncFlags & SYNC_PRIVATE ){
1748 blob_append(&send, "pragma send-private\n", -1);
1749 }
1750
1751 /* When syncing unversioned files, create a TEMP table in which to store
1752 ** the names of files that do not need to be sent from client to server.
1753 */
1754 if( syncFlags & SYNC_UNVERSIONED ){
1755 db_multi_exec(
1756 "CREATE TEMP TABLE uv_tosend("
1757 " name TEXT PRIMARY KEY,"
1758 " mtimeOnly BOOLEAN"
1759 ") WITHOUT ROWID;"
1760 "INSERT INTO uv_toSend(name,mtimeOnly)"
1761 " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;"
1762 );
1763 }
1764
1765 /*
1766 ** Always begin with a clone, pull, or push message
1767 */
1768 if( syncFlags & SYNC_CLONE ){
@@ -1526,11 +1800,11 @@
1800 db_multi_exec(
1801 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1802 );
1803 manifest_crosslink_begin();
1804
1805 /* Send back the most recently received cookie. Let the server
1806 ** figure out if this is a cookie that it cares about.
1807 */
1808 zCookie = db_get("cookie", 0);
1809 if( zCookie ){
1810 blob_appendf(&send, "cookie %s\n", zCookie);
@@ -1570,10 +1844,23 @@
1844 configure_prepare_to_receive(overwrite);
1845 }
1846 origConfigRcvMask = configRcvMask;
1847 configRcvMask = 0;
1848 }
1849
1850 /* Send a request to sync unversioned files. On a clone, delay sending
1851 ** this until the second cycle since the login card might fail on
1852 ** the first cycle.
1853 */
1854 if( (syncFlags & SYNC_UNVERSIONED)!=0
1855 && ((syncFlags & SYNC_CLONE)==0 || nCycle>0)
1856 && !uvHashSent
1857 ){
1858 blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0));
1859 nCardSent++;
1860 uvHashSent = 1;
1861 }
1862
1863 /* Send configuration parameters being pushed */
1864 if( configSendMask ){
1865 if( zOpType==0 ) zOpType = "Push";
1866 if( configSendMask & CONFIGSET_OLDFORMAT ){
@@ -1587,10 +1874,31 @@
1874 }else{
1875 nCardSent += configure_send_group(xfer.pOut, configSendMask, 0);
1876 }
1877 configSendMask = 0;
1878 }
1879
1880 /* Send unversioned files present here on the client but missing or
1881 ** obsolete on the server.
1882 */
1883 if( uvDoPush ){
1884 Stmt uvq;
1885 int rc = SQLITE_OK;
1886 assert( (syncFlags & SYNC_UNVERSIONED)!=0 );
1887 assert( uvStatus==2 );
1888 db_prepare(&uvq, "SELECT name, mtimeOnly FROM uv_tosend");
1889 while( (rc = db_step(&uvq))==SQLITE_ROW ){
1890 const char *zName = db_column_text(&uvq, 0);
1891 send_unversioned_file(&xfer, zName, db_column_int(&uvq,1));
1892 nCardSent++;
1893 nArtifactSent++;
1894 db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
1895 if( blob_size(xfer.pOut)>xfer.mxSend ) break;
1896 }
1897 db_finalize(&uvq);
1898 if( rc==SQLITE_DONE ) uvDoPush = 0;
1899 }
1900
1901 /* Append randomness to the end of the message. This makes all
1902 ** messages unique so that that the login-card nonce will always
1903 ** be unique.
1904 */
@@ -1647,10 +1955,12 @@
1955 if( syncFlags & SYNC_PUSH ){
1956 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1957 nCardSent++;
1958 }
1959 go = 0;
1960 nUvGimmeSent = 0;
1961 nUvFileRcvd = 0;
1962
1963 /* Process the reply that came back from the server */
1964 while( blob_line(&recv, &xfer.line) ){
1965 if( blob_buffer(&xfer.line)[0]=='#' ){
1966 const char *zLine = blob_buffer(&xfer.line);
@@ -1659,11 +1969,13 @@
1969 double rDiff;
1970 sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]);
1971 rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g",
1972 zTime, rArrivalTime);
1973 if( rDiff>9e98 || rDiff<-9e98 ) rDiff = 0.0;
1974 if( rDiff*24.0*3600.0 >= -(blob_size(&recv)/5000.0 + 20) ){
1975 rDiff = 0.0;
1976 }
1977 if( fossil_fabs(rDiff)>fossil_fabs(rSkew) ) rSkew = rDiff;
1978 }
1979 nCardRcvd++;
1980 continue;
1981 }
@@ -1695,10 +2007,20 @@
2007 */
2008 if( blob_eq(&xfer.aToken[0],"cfile") ){
2009 xfer_accept_compressed_file(&xfer, 0, 0);
2010 nArtifactRcvd++;
2011 }else
2012
2013 /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
2014 **
2015 ** Accept an unversioned file from the client.
2016 */
2017 if( blob_eq(&xfer.aToken[0], "uvfile") ){
2018 xfer_accept_unversioned_file(&xfer, 1);
2019 nArtifactRcvd++;
2020 nUvFileRcvd++;
2021 }else
2022
2023 /* gimme UUID
2024 **
2025 ** Server is requesting a file. If the file is a manifest, assume
2026 ** that the server will also want to know all of the content files
@@ -1742,10 +2064,63 @@
2064 if( rid ) newPhantom = 1;
2065 }
2066 remote_has(rid);
2067 }else
2068
2069 /* uvigot NAME MTIME HASH SIZE
2070 **
2071 ** Server announces that it has a particular unversioned file. The
2072 ** server will only send this card if the client had previously sent
2073 ** a "pragma uv-hash" card with a hash that does not match.
2074 **
2075 ** If the identified file needs to be transferred, then setup for the
2076 ** transfer. Generate a "uvgimme" card in the reply if the server
2077 ** version is newer than the client. Generate a "uvfile" card if
2078 ** the client version is newer than the server. If HASH is "-"
2079 ** (indicating that the file has been deleted) and MTIME is newer,
2080 ** then do the deletion.
2081 */
2082 if( xfer.nToken==5
2083 && blob_eq(&xfer.aToken[0], "uvigot")
2084 && blob_is_filename(&xfer.aToken[1])
2085 && blob_is_int64(&xfer.aToken[2], &mtime)
2086 && blob_is_int(&xfer.aToken[4], &size)
2087 && (blob_eq(&xfer.aToken[3],"-") || blob_is_uuid(&xfer.aToken[3]))
2088 ){
2089 const char *zName = blob_str(&xfer.aToken[1]);
2090 const char *zHash = blob_str(&xfer.aToken[3]);
2091 int iStatus;
2092 if( uvStatus==0 ) uvStatus = 2;
2093 iStatus = unversioned_status(zName, mtime, zHash);
2094 if( iStatus<=1 ){
2095 if( zHash[0]!='-' ){
2096 blob_appendf(xfer.pOut, "uvgimme %s\n", zName);
2097 nCardSent++;
2098 nUvGimmeSent++;
2099 }else if( iStatus==1 ){
2100 db_multi_exec(
2101 "UPDATE unversioned"
2102 " SET mtime=%lld, hash=NULL, sz=0, encoding=0, content=NULL"
2103 " WHERE name=%Q", mtime, zName
2104 );
2105 db_unset("uv-hash", 0);
2106 }
2107 }else if( iStatus==2 ){
2108 db_multi_exec(
2109 "UPDATE unversioned SET mtime=%lld WHERE name=%Q", mtime, zName
2110 );
2111 db_unset("uv-hash", 0);
2112 }
2113 if( iStatus<=3 ){
2114 db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
2115 }else if( iStatus==4 ){
2116 db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q",zName);
2117 }else if( iStatus==5 ){
2118 db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)",
2119 zName);
2120 }
2121 }else
2122
2123 /* push SERVERCODE PRODUCTCODE
2124 **
2125 ** Should only happen in response to a clone. This message tells
2126 ** the client what product to use for the new database.
@@ -1828,11 +2203,12 @@
2203 ** to the next cycle.
2204 */
2205 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
2206 char *zMsg = blob_terminate(&xfer.aToken[1]);
2207 defossilize(zMsg);
2208 if( (syncFlags & SYNC_PUSH) && zMsg
2209 && sqlite3_strglob("pull only *", zMsg)==0 ){
2210 syncFlags &= ~SYNC_PUSH;
2211 zMsg = 0;
2212 }
2213 if( zMsg && zMsg[0] ){
2214 fossil_force_newline();
@@ -1845,10 +2221,22 @@
2221 ** The server can send pragmas to try to convey meta-information to
2222 ** the client. These are informational only. Unknown pragmas are
2223 ** silently ignored.
2224 */
2225 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
2226 /* If the server is unwill to accept new unversioned content (because
2227 ** this client lacks the necessary permissions) then it sends a
2228 ** "uv-pull-only" pragma so that the client will know not to waste
2229 ** bandwidth trying to upload unversioned content. If the server
2230 ** does accept new unversioned content, it sends "uv-push-ok".
2231 */
2232 if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){
2233 uvStatus = 1;
2234 }else if( blob_eq(&xfer.aToken[1], "uv-push-ok") ){
2235 uvStatus = 2;
2236 uvDoPush = 1;
2237 }
2238 }else
2239
2240 /* error MESSAGE
2241 **
2242 ** Report an error and abandon the sync session.
@@ -1943,13 +2331,17 @@
2331 xfer.nDanglingFile = 0;
2332
2333 /* If we have one or more files queued to send, then go
2334 ** another round
2335 */
2336 if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){
2337 go = 1;
2338 }
2339
2340 /* Continue looping as long as new uvfile cards are being received
2341 ** and uvgimme cards are being sent. */
2342 if( nUvGimmeSent>0 && nUvFileRcvd>0 ) go = 1;
2343
2344 /* If this is a clone, the go at least two rounds */
2345 if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
2346
2347 /* Stop the cycle if the server sends a "clone_seqno 0" card and
2348
+10 -4
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
2828
2929
SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5
3030
3131
SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
3232
33
-SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
33
+SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
3434
35
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
35
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
3636
3737
3838
RC=$(DMDIR)\bin\rcc
3939
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
4040
@@ -49,11 +49,11 @@
4949
5050
$(OBJDIR)\fossil.res: $B\win\fossil.rc
5151
$(RC) $(RCFLAGS) -o$@ $**
5252
5353
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54
- +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fshell fusefs glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
54
+ +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fshell fusefs glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
5555
+echo fossil >> $@
5656
+echo fossil >> $@
5757
+echo $(LIBS) >> $@
5858
+echo. >> $@
5959
+echo fossil >> $@
@@ -752,10 +752,16 @@
752752
$(OBJDIR)\unicode$O : unicode_.c unicode.h
753753
$(TCC) -o$@ -c unicode_.c
754754
755755
unicode_.c : $(SRCDIR)\unicode.c
756756
+translate$E $** > $@
757
+
758
+$(OBJDIR)\unversioned$O : unversioned_.c unversioned.h
759
+ $(TCC) -o$@ -c unversioned_.c
760
+
761
+unversioned_.c : $(SRCDIR)\unversioned.c
762
+ +translate$E $** > $@
757763
758764
$(OBJDIR)\update$O : update_.c update.h
759765
$(TCC) -o$@ -c update_.c
760766
761767
update_.c : $(SRCDIR)\update.c
@@ -844,7 +850,7 @@
844850
845851
zip_.c : $(SRCDIR)\zip.c
846852
+translate$E $** > $@
847853
848854
headers: makeheaders$E page_index.h builtin_data.h VERSION.h
849
- +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
855
+ +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
850856
@copy /Y nul: headers
851857
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5
30
31 SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35 OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
38 RC=$(DMDIR)\bin\rcc
39 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
40
@@ -49,11 +49,11 @@
49
50 $(OBJDIR)\fossil.res: $B\win\fossil.rc
51 $(RC) $(RCFLAGS) -o$@ $**
52
53 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54 +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fshell fusefs glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
55 +echo fossil >> $@
56 +echo fossil >> $@
57 +echo $(LIBS) >> $@
58 +echo. >> $@
59 +echo fossil >> $@
@@ -752,10 +752,16 @@
752 $(OBJDIR)\unicode$O : unicode_.c unicode.h
753 $(TCC) -o$@ -c unicode_.c
754
755 unicode_.c : $(SRCDIR)\unicode.c
756 +translate$E $** > $@
 
 
 
 
 
 
757
758 $(OBJDIR)\update$O : update_.c update.h
759 $(TCC) -o$@ -c update_.c
760
761 update_.c : $(SRCDIR)\update.c
@@ -844,7 +850,7 @@
844
845 zip_.c : $(SRCDIR)\zip.c
846 +translate$E $** > $@
847
848 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
849 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
850 @copy /Y nul: headers
851
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5
30
31 SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35 OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
38 RC=$(DMDIR)\bin\rcc
39 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
40
@@ -49,11 +49,11 @@
49
50 $(OBJDIR)\fossil.res: $B\win\fossil.rc
51 $(RC) $(RCFLAGS) -o$@ $**
52
53 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54 +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fshell fusefs glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
55 +echo fossil >> $@
56 +echo fossil >> $@
57 +echo $(LIBS) >> $@
58 +echo. >> $@
59 +echo fossil >> $@
@@ -752,10 +752,16 @@
752 $(OBJDIR)\unicode$O : unicode_.c unicode.h
753 $(TCC) -o$@ -c unicode_.c
754
755 unicode_.c : $(SRCDIR)\unicode.c
756 +translate$E $** > $@
757
758 $(OBJDIR)\unversioned$O : unversioned_.c unversioned.h
759 $(TCC) -o$@ -c unversioned_.c
760
761 unversioned_.c : $(SRCDIR)\unversioned.c
762 +translate$E $** > $@
763
764 $(OBJDIR)\update$O : update_.c update.h
765 $(TCC) -o$@ -c update_.c
766
767 update_.c : $(SRCDIR)\update.c
@@ -844,7 +850,7 @@
850
851 zip_.c : $(SRCDIR)\zip.c
852 +translate$E $** > $@
853
854 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
855 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
856 @copy /Y nul: headers
857
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -526,10 +526,11 @@
526526
$(SRCDIR)/timeline.c \
527527
$(SRCDIR)/tkt.c \
528528
$(SRCDIR)/tktsetup.c \
529529
$(SRCDIR)/undo.c \
530530
$(SRCDIR)/unicode.c \
531
+ $(SRCDIR)/unversioned.c \
531532
$(SRCDIR)/update.c \
532533
$(SRCDIR)/url.c \
533534
$(SRCDIR)/user.c \
534535
$(SRCDIR)/utf8.c \
535536
$(SRCDIR)/util.c \
@@ -699,10 +700,11 @@
699700
$(OBJDIR)/timeline_.c \
700701
$(OBJDIR)/tkt_.c \
701702
$(OBJDIR)/tktsetup_.c \
702703
$(OBJDIR)/undo_.c \
703704
$(OBJDIR)/unicode_.c \
705
+ $(OBJDIR)/unversioned_.c \
704706
$(OBJDIR)/update_.c \
705707
$(OBJDIR)/url_.c \
706708
$(OBJDIR)/user_.c \
707709
$(OBJDIR)/utf8_.c \
708710
$(OBJDIR)/util_.c \
@@ -821,10 +823,11 @@
821823
$(OBJDIR)/timeline.o \
822824
$(OBJDIR)/tkt.o \
823825
$(OBJDIR)/tktsetup.o \
824826
$(OBJDIR)/undo.o \
825827
$(OBJDIR)/unicode.o \
828
+ $(OBJDIR)/unversioned.o \
826829
$(OBJDIR)/update.o \
827830
$(OBJDIR)/url.o \
828831
$(OBJDIR)/user.o \
829832
$(OBJDIR)/utf8.o \
830833
$(OBJDIR)/util.o \
@@ -1154,10 +1157,11 @@
11541157
$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
11551158
$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
11561159
$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
11571160
$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
11581161
$(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
1162
+ $(OBJDIR)/unversioned_.c:$(OBJDIR)/unversioned.h \
11591163
$(OBJDIR)/update_.c:$(OBJDIR)/update.h \
11601164
$(OBJDIR)/url_.c:$(OBJDIR)/url.h \
11611165
$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
11621166
$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
11631167
$(OBJDIR)/util_.c:$(OBJDIR)/util.h \
@@ -2017,10 +2021,18 @@
20172021
20182022
$(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
20192023
$(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
20202024
20212025
$(OBJDIR)/unicode.h: $(OBJDIR)/headers
2026
+
2027
+$(OBJDIR)/unversioned_.c: $(SRCDIR)/unversioned.c $(TRANSLATE)
2028
+ $(TRANSLATE) $(SRCDIR)/unversioned.c >$@
2029
+
2030
+$(OBJDIR)/unversioned.o: $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.h $(SRCDIR)/config.h
2031
+ $(XTCC) -o $(OBJDIR)/unversioned.o -c $(OBJDIR)/unversioned_.c
2032
+
2033
+$(OBJDIR)/unversioned.h: $(OBJDIR)/headers
20222034
20232035
$(OBJDIR)/update_.c: $(SRCDIR)/update.c $(TRANSLATE)
20242036
$(TRANSLATE) $(SRCDIR)/update.c >$@
20252037
20262038
$(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
20272039
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -526,10 +526,11 @@
526 $(SRCDIR)/timeline.c \
527 $(SRCDIR)/tkt.c \
528 $(SRCDIR)/tktsetup.c \
529 $(SRCDIR)/undo.c \
530 $(SRCDIR)/unicode.c \
 
531 $(SRCDIR)/update.c \
532 $(SRCDIR)/url.c \
533 $(SRCDIR)/user.c \
534 $(SRCDIR)/utf8.c \
535 $(SRCDIR)/util.c \
@@ -699,10 +700,11 @@
699 $(OBJDIR)/timeline_.c \
700 $(OBJDIR)/tkt_.c \
701 $(OBJDIR)/tktsetup_.c \
702 $(OBJDIR)/undo_.c \
703 $(OBJDIR)/unicode_.c \
 
704 $(OBJDIR)/update_.c \
705 $(OBJDIR)/url_.c \
706 $(OBJDIR)/user_.c \
707 $(OBJDIR)/utf8_.c \
708 $(OBJDIR)/util_.c \
@@ -821,10 +823,11 @@
821 $(OBJDIR)/timeline.o \
822 $(OBJDIR)/tkt.o \
823 $(OBJDIR)/tktsetup.o \
824 $(OBJDIR)/undo.o \
825 $(OBJDIR)/unicode.o \
 
826 $(OBJDIR)/update.o \
827 $(OBJDIR)/url.o \
828 $(OBJDIR)/user.o \
829 $(OBJDIR)/utf8.o \
830 $(OBJDIR)/util.o \
@@ -1154,10 +1157,11 @@
1154 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
1155 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
1156 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
1157 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
1158 $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
 
1159 $(OBJDIR)/update_.c:$(OBJDIR)/update.h \
1160 $(OBJDIR)/url_.c:$(OBJDIR)/url.h \
1161 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
1162 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
1163 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
@@ -2017,10 +2021,18 @@
2017
2018 $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
2019 $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
2020
2021 $(OBJDIR)/unicode.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
2022
2023 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(TRANSLATE)
2024 $(TRANSLATE) $(SRCDIR)/update.c >$@
2025
2026 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
2027
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -526,10 +526,11 @@
526 $(SRCDIR)/timeline.c \
527 $(SRCDIR)/tkt.c \
528 $(SRCDIR)/tktsetup.c \
529 $(SRCDIR)/undo.c \
530 $(SRCDIR)/unicode.c \
531 $(SRCDIR)/unversioned.c \
532 $(SRCDIR)/update.c \
533 $(SRCDIR)/url.c \
534 $(SRCDIR)/user.c \
535 $(SRCDIR)/utf8.c \
536 $(SRCDIR)/util.c \
@@ -699,10 +700,11 @@
700 $(OBJDIR)/timeline_.c \
701 $(OBJDIR)/tkt_.c \
702 $(OBJDIR)/tktsetup_.c \
703 $(OBJDIR)/undo_.c \
704 $(OBJDIR)/unicode_.c \
705 $(OBJDIR)/unversioned_.c \
706 $(OBJDIR)/update_.c \
707 $(OBJDIR)/url_.c \
708 $(OBJDIR)/user_.c \
709 $(OBJDIR)/utf8_.c \
710 $(OBJDIR)/util_.c \
@@ -821,10 +823,11 @@
823 $(OBJDIR)/timeline.o \
824 $(OBJDIR)/tkt.o \
825 $(OBJDIR)/tktsetup.o \
826 $(OBJDIR)/undo.o \
827 $(OBJDIR)/unicode.o \
828 $(OBJDIR)/unversioned.o \
829 $(OBJDIR)/update.o \
830 $(OBJDIR)/url.o \
831 $(OBJDIR)/user.o \
832 $(OBJDIR)/utf8.o \
833 $(OBJDIR)/util.o \
@@ -1154,10 +1157,11 @@
1157 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
1158 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
1159 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
1160 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
1161 $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
1162 $(OBJDIR)/unversioned_.c:$(OBJDIR)/unversioned.h \
1163 $(OBJDIR)/update_.c:$(OBJDIR)/update.h \
1164 $(OBJDIR)/url_.c:$(OBJDIR)/url.h \
1165 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
1166 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
1167 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
@@ -2017,10 +2021,18 @@
2021
2022 $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
2023 $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
2024
2025 $(OBJDIR)/unicode.h: $(OBJDIR)/headers
2026
2027 $(OBJDIR)/unversioned_.c: $(SRCDIR)/unversioned.c $(TRANSLATE)
2028 $(TRANSLATE) $(SRCDIR)/unversioned.c >$@
2029
2030 $(OBJDIR)/unversioned.o: $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.h $(SRCDIR)/config.h
2031 $(XTCC) -o $(OBJDIR)/unversioned.o -c $(OBJDIR)/unversioned_.c
2032
2033 $(OBJDIR)/unversioned.h: $(OBJDIR)/headers
2034
2035 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(TRANSLATE)
2036 $(TRANSLATE) $(SRCDIR)/update.c >$@
2037
2038 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
2039
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -445,10 +445,11 @@
445445
timeline_.c \
446446
tkt_.c \
447447
tktsetup_.c \
448448
undo_.c \
449449
unicode_.c \
450
+ unversioned_.c \
450451
update_.c \
451452
url_.c \
452453
user_.c \
453454
utf8_.c \
454455
util_.c \
@@ -622,10 +623,11 @@
622623
$(OX)\timeline$O \
623624
$(OX)\tkt$O \
624625
$(OX)\tktsetup$O \
625626
$(OX)\undo$O \
626627
$(OX)\unicode$O \
628
+ $(OX)\unversioned$O \
627629
$(OX)\update$O \
628630
$(OX)\url$O \
629631
$(OX)\user$O \
630632
$(OX)\utf8$O \
631633
$(OX)\util$O \
@@ -803,10 +805,11 @@
803805
echo $(OX)\timeline.obj >> $@
804806
echo $(OX)\tkt.obj >> $@
805807
echo $(OX)\tktsetup.obj >> $@
806808
echo $(OX)\undo.obj >> $@
807809
echo $(OX)\unicode.obj >> $@
810
+ echo $(OX)\unversioned.obj >> $@
808811
echo $(OX)\update.obj >> $@
809812
echo $(OX)\url.obj >> $@
810813
echo $(OX)\user.obj >> $@
811814
echo $(OX)\utf8.obj >> $@
812815
echo $(OX)\util.obj >> $@
@@ -1558,10 +1561,16 @@
15581561
$(OX)\unicode$O : unicode_.c unicode.h
15591562
$(TCC) /Fo$@ -c unicode_.c
15601563
15611564
unicode_.c : $(SRCDIR)\unicode.c
15621565
translate$E $** > $@
1566
+
1567
+$(OX)\unversioned$O : unversioned_.c unversioned.h
1568
+ $(TCC) /Fo$@ -c unversioned_.c
1569
+
1570
+unversioned_.c : $(SRCDIR)\unversioned.c
1571
+ translate$E $** > $@
15631572
15641573
$(OX)\update$O : update_.c update.h
15651574
$(TCC) /Fo$@ -c update_.c
15661575
15671576
update_.c : $(SRCDIR)\update.c
@@ -1758,10 +1767,11 @@
17581767
timeline_.c:timeline.h \
17591768
tkt_.c:tkt.h \
17601769
tktsetup_.c:tktsetup.h \
17611770
undo_.c:undo.h \
17621771
unicode_.c:unicode.h \
1772
+ unversioned_.c:unversioned.h \
17631773
update_.c:update.h \
17641774
url_.c:url.h \
17651775
user_.c:user.h \
17661776
utf8_.c:utf8.h \
17671777
util_.c:util.h \
17681778
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -445,10 +445,11 @@
445 timeline_.c \
446 tkt_.c \
447 tktsetup_.c \
448 undo_.c \
449 unicode_.c \
 
450 update_.c \
451 url_.c \
452 user_.c \
453 utf8_.c \
454 util_.c \
@@ -622,10 +623,11 @@
622 $(OX)\timeline$O \
623 $(OX)\tkt$O \
624 $(OX)\tktsetup$O \
625 $(OX)\undo$O \
626 $(OX)\unicode$O \
 
627 $(OX)\update$O \
628 $(OX)\url$O \
629 $(OX)\user$O \
630 $(OX)\utf8$O \
631 $(OX)\util$O \
@@ -803,10 +805,11 @@
803 echo $(OX)\timeline.obj >> $@
804 echo $(OX)\tkt.obj >> $@
805 echo $(OX)\tktsetup.obj >> $@
806 echo $(OX)\undo.obj >> $@
807 echo $(OX)\unicode.obj >> $@
 
808 echo $(OX)\update.obj >> $@
809 echo $(OX)\url.obj >> $@
810 echo $(OX)\user.obj >> $@
811 echo $(OX)\utf8.obj >> $@
812 echo $(OX)\util.obj >> $@
@@ -1558,10 +1561,16 @@
1558 $(OX)\unicode$O : unicode_.c unicode.h
1559 $(TCC) /Fo$@ -c unicode_.c
1560
1561 unicode_.c : $(SRCDIR)\unicode.c
1562 translate$E $** > $@
 
 
 
 
 
 
1563
1564 $(OX)\update$O : update_.c update.h
1565 $(TCC) /Fo$@ -c update_.c
1566
1567 update_.c : $(SRCDIR)\update.c
@@ -1758,10 +1767,11 @@
1758 timeline_.c:timeline.h \
1759 tkt_.c:tkt.h \
1760 tktsetup_.c:tktsetup.h \
1761 undo_.c:undo.h \
1762 unicode_.c:unicode.h \
 
1763 update_.c:update.h \
1764 url_.c:url.h \
1765 user_.c:user.h \
1766 utf8_.c:utf8.h \
1767 util_.c:util.h \
1768
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -445,10 +445,11 @@
445 timeline_.c \
446 tkt_.c \
447 tktsetup_.c \
448 undo_.c \
449 unicode_.c \
450 unversioned_.c \
451 update_.c \
452 url_.c \
453 user_.c \
454 utf8_.c \
455 util_.c \
@@ -622,10 +623,11 @@
623 $(OX)\timeline$O \
624 $(OX)\tkt$O \
625 $(OX)\tktsetup$O \
626 $(OX)\undo$O \
627 $(OX)\unicode$O \
628 $(OX)\unversioned$O \
629 $(OX)\update$O \
630 $(OX)\url$O \
631 $(OX)\user$O \
632 $(OX)\utf8$O \
633 $(OX)\util$O \
@@ -803,10 +805,11 @@
805 echo $(OX)\timeline.obj >> $@
806 echo $(OX)\tkt.obj >> $@
807 echo $(OX)\tktsetup.obj >> $@
808 echo $(OX)\undo.obj >> $@
809 echo $(OX)\unicode.obj >> $@
810 echo $(OX)\unversioned.obj >> $@
811 echo $(OX)\update.obj >> $@
812 echo $(OX)\url.obj >> $@
813 echo $(OX)\user.obj >> $@
814 echo $(OX)\utf8.obj >> $@
815 echo $(OX)\util.obj >> $@
@@ -1558,10 +1561,16 @@
1561 $(OX)\unicode$O : unicode_.c unicode.h
1562 $(TCC) /Fo$@ -c unicode_.c
1563
1564 unicode_.c : $(SRCDIR)\unicode.c
1565 translate$E $** > $@
1566
1567 $(OX)\unversioned$O : unversioned_.c unversioned.h
1568 $(TCC) /Fo$@ -c unversioned_.c
1569
1570 unversioned_.c : $(SRCDIR)\unversioned.c
1571 translate$E $** > $@
1572
1573 $(OX)\update$O : update_.c update.h
1574 $(TCC) /Fo$@ -c update_.c
1575
1576 update_.c : $(SRCDIR)\update.c
@@ -1758,10 +1767,11 @@
1767 timeline_.c:timeline.h \
1768 tkt_.c:tkt.h \
1769 tktsetup_.c:tktsetup.h \
1770 undo_.c:undo.h \
1771 unicode_.c:unicode.h \
1772 unversioned_.c:unversioned.h \
1773 update_.c:update.h \
1774 url_.c:url.h \
1775 user_.c:user.h \
1776 utf8_.c:utf8.h \
1777 util_.c:util.h \
1778
+175 -6
--- www/sync.wiki
+++ www/sync.wiki
@@ -167,18 +167,21 @@
167167
168168
<p>Privileges are cumulative. There can be multiple successful
169169
login cards. The session privileges are the bit-wise OR of the
170170
privileges of each individual login.</p>
171171
172
-<h3>3.3 File and Compressed File Cards</h3>
172
+<h3>3.3 File Cards</h3>
173173
174
-<p>Artifacts are transferred using either "file" cards, or "cfile" cards.
175
-(The name "file" card comes from the fact that most artifacts correspond to
176
-files, and "cfile" is just a compressed file.)
174
+<p>Artifacts are transferred using either "file" cards, or "cfile"
175
+or "uvfile" cards.
176
+The name "file" card comes from the fact that most artifacts correspond to
177
+files that are under version control.
178
+The "cfile" name is an abbreviation for "compressed file".
179
+The "uvfile" name is an abbreviation for "unversioned file".
177180
</p>
178181
179
-<h4>3.3.1 File Cards</h4>
182
+<h4>3.3.1 Ordinary File Cards</h4>
180183
181184
<p>For sync protocols, artifacts are transferred using "file"
182185
cards. File cards come in two different formats depending
183186
on whether the artifact is sent directly or as a delta from some
184187
other artifact.</p>
@@ -186,11 +189,11 @@
186189
<blockquote>
187190
<b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br>
188191
<b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
189192
</blockquote>
190193
191
-<p>File cards are different from all other cards in that they
194
+<p>File cards are different from most other cards in that they are
192195
followed by in-line "payload" data. The content of the artifact
193196
or the artifact delta consists of the first <i>size</i> bytes of the
194197
x-fossil content that immediately follow the newline that
195198
terminates the file card. Only file and cfile cards have this characteristic.
196199
</p>
@@ -257,10 +260,44 @@
257260
</blockquote>
258261
259262
<p>The private card has no arguments and must directly precede a
260263
file card that contains the private content.</p>
261264
265
+<h4>3.3.4 Unversioned File Cards</h4>
266
+
267
+<p>Unversioned content is sent in both directions (client to server and
268
+server to client) using "uvfile" cards in the following format:
269
+
270
+<blockquote>
271
+<b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i>
272
+</blockquote>
273
+
274
+<p>The <i>name</i> field is the name of the unversioned file. The
275
+<i>mtime</i> is the last modification time of the file in seconds
276
+since 1970. The <i>hash</i> field is the SHA1 hash of the content
277
+for the unversioned file, or "<b>-</b>" for deleted content.
278
+The <i>size</i> field is the (uncompressed) size of the content
279
+in bytes. The <i>flags</i> field is an integer which is interpreted
280
+as an array of bits. The 0x0004 bit of <i>flags</i> indicates that
281
+the <i>content</i> is to be omitted. The content might be omitted if
282
+it is too large to transmit, or if the send merely wants to update the
283
+modification time of the file without changing the files content.
284
+The <i>content</i> is the (uncompressed) content of the file.
285
+
286
+<p>The receiver should only accept the uvfile card if the hash and
287
+size match the content and if the mtime is newer than any existing
288
+instance of the same file held by the receiver. The sender will not
289
+normally transmit a uvfile card unless all these constraints are true,
290
+but the receiver should double-check.
291
+
292
+<p>A server should only accept uvfile cards if the login user has
293
+the "y" write-unversioned permission.
294
+
295
+<p>Servers send uvfile cards in response to uvgimme cards received from
296
+the client. Clients send uvfile cards when they determine that the server
297
+needs the content based on uvigot cards previously received from the server.
298
+
262299
<h3>3.4 Push and Pull Cards</h3>
263300
264301
<p>Among the first cards in a client-to-server message are
265302
the push and pull cards. The push card tells the server that
266303
the client is pushing content. The pull card tells the server
@@ -359,10 +396,42 @@
359396
360397
<p>If the second argument exists and is "1", then the artifact
361398
identified by the first argument is private on the sender and should
362399
be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
363400
401
+<h4>3.6.1 Unversioned Igot Cards</h4>
402
+
403
+<p>Zero or more "uvigot" cards are sent from client to server when
404
+synchronizing unversioned content. The format of a uvigot card is
405
+as follows:
406
+
407
+<blockquote>
408
+<b>uvigot</b> <i>name mtime hash size</i>
409
+</blockquote>
410
+
411
+<p>The <i>name</i> argument is the name of an unversioned file.
412
+The <i>mtime</i> is the last modification time of the unversioned file
413
+in seconds since 1970.
414
+The <i>hash</i> is the SHA1 hash of the unversioned file content, or
415
+"<b>-</b>" if the file has been deleted.
416
+The <i>size</i> is the uncompressed size of the file in bytes.
417
+
418
+<p>When the server sees a "pragma uv-hash" card for which the hash
419
+does not match, it sends uvigot cards for every unversioned file that it
420
+holds. The client will use this information to figure out which
421
+unversioned files need to be synchronized.
422
+The server might also send a uvigot card when it receives a uvgimme card
423
+but its reply message size is already oversized and hence unable to hold
424
+the usual uvfile reply.
425
+
426
+<p>When a client receives a "uvigot" card, it checks to see if the the
427
+file needs to be transfered from client to server or from server to client.
428
+If a client-to-server transmission is needed, the client schedules that
429
+transfer to occur on a subsequent HTTP request. If a server-to-client
430
+transfer is needed, then the client sends a "uvgimme" card back to the
431
+server to request the file content.
432
+
364433
<h3>3.7 Gimme Cards</h3>
365434
366435
<p>A gimme card is sent from either client to server or from server
367436
to client. The gimme card asks the receiver to send a particular
368437
artifact back to the sender. The format of a gimme card is this:</p>
@@ -374,10 +443,26 @@
374443
<p>The argument to the gimme card is the ID of the artifact that
375444
the sender wants. The receiver will typically respond to a
376445
gimme card by sending a file card in its reply or in the next
377446
message.</p>
378447
448
+<h4>3.7.1 Unversioned Gimme Cards</h4>
449
+
450
+<p>Sync synchronizing unversioned content, the client may send "uvgimme"
451
+cards to the server. A uvgimme card requests that the server send
452
+unversioned content to the client. The format of a uvgimme card is
453
+as follows:
454
+
455
+<blockquote>
456
+<b>uvgimme</b> <i>name</i>
457
+</blockquote>
458
+
459
+<p>The <i>name</i> is the name of the unversioned file found on the
460
+server that the client would like to have. When a server sees a
461
+uvgimme card, it normally responses with a uvfile card, though it might
462
+also send anoter uvigot card if the HTTP reply is already oversized.
463
+
379464
<h3>3.8 Cookie Cards</h3>
380465
381466
<p>A cookie card can be used by a server to record a small amount
382467
of state information on a client. The server sends a cookie to the
383468
client. The client sends the same cookie back to the server on
@@ -539,10 +624,49 @@
539624
<p>The send-catalog pragma instructs the server to transmit igot
540625
cards for every known artifact. This can help the client and server
541626
to get back in synchronization after a prior protocol error. The
542627
"--verily" option to the [/help?cmd=sync|fossil sync] command causes
543628
the send-catalog pragma to be transmitted.</p>
629
+
630
+<li><p><b>uv-hash</b> <i>HASH</i>
631
+<p>The uv-hash pragma is sent from client to server to provoke a
632
+synchronization of unversioned content. The <i>HASH</i> is a SHA1
633
+hash of the names, modification times, and individual hashes of all
634
+unversioned files on the client. If the unversioned content hash
635
+from the client does not match the unversioned content hash on the
636
+server, then the server will reply with either a "pragma uv-push-ok"
637
+or "pragma uv-pull-only" card followed by one "uvigot" card for
638
+each unversioned file currently held on the server. The collection
639
+of "uvigot" cards sent in response to a "uv-hash" pragma is called
640
+the "unversioned catalog". The client will used the unversioned
641
+catalog to figure out which files (if any) need to be synchronized
642
+between client and server and send appropriate "uvfile" or "uvgimme"
643
+cards on the next HTTP request.</p>
644
+
645
+<p>If a client sends a uv-hash pragma and does not receive back
646
+either a uv-pull-only or uv-push-ok pragma, that means that the
647
+content on the server exactly matches the content on the client and
648
+no further synchronization is required.
649
+
650
+<li><p><b>uv-pull-only</b></i>
651
+<p>A server sends the uv-pull-only pragma to the client in response
652
+to a uv-hash pragma with a mismatched content hash argument. This
653
+pragma indicates that there are differences in unversioned content
654
+between the client and server but that content can only be transfered
655
+from server to client. The server is unwilling to accept content from
656
+the client because the client login lacks the "write-unversioned"
657
+permission.</p>
658
+
659
+<li><p><b>uv-push-ok</b></i>
660
+<p>A server sends the uv-push-ok pragma to the client in response
661
+to a uv-hash pragma with a mismatched content hash argument. This
662
+pragma indicates that there are differences in unversioned content
663
+between the client and server and that content can only be transfered
664
+in either direction. The server is willing to accept content from
665
+the client because the client login has the "write-unversioned"
666
+permission.</p>
667
+
544668
</ol>
545669
546670
<h3>3.12 Comment Cards</h3>
547671
548672
<p>Any card that begins with "#" (ASCII 0x23) is a comment card and
@@ -713,10 +837,52 @@
713837
The first three steps of a pull are combined with the first five steps
714838
of a push. Steps (4) through (7) of a pull are combined with steps
715839
(5) through (8) of a push. And steps (8) through (10) of a pull
716840
are combined with step (9) of a push.</p>
717841
842
+<h3>5.4 Unversioned File Sync</h3>
843
+
844
+<p>"Unversioned files" are files held in the repository
845
+where only the most recent version of the file is kept rather than
846
+the entire change history. Unversioned files are intended to be
847
+used to store ephemeral content, such as compiled binaries of the
848
+most recent release.
849
+
850
+<p>Unversioned files are identified by name and timestamp (mtime).
851
+Only the most recent version of each file (the version with
852
+the largest mtime value) is retained.
853
+
854
+<p>Unversioned files are synchronized using the
855
+[/help?cmd=unversioned|fossil unversioned sync] command.
856
+
857
+<p>A schematic of an unversioned file synchronization is as follows:
858
+
859
+<ol>
860
+<li>The client sends a "pragma uv-hash" card to the server. The argument
861
+ to the uv-hash pragma is a hash of all filesnames, mtimes, and
862
+ content hashes for the unversioned files held by the client.
863
+ <hr>
864
+<li>If the unversion content hash from the client matches the unversioned
865
+ content hash on the server, then nothing needs to be done and the
866
+ server no-ops. But if the hashes are different, then the server
867
+ replies with either a uv-pull-only or a uv-push-ok pragma followed by
868
+ uvigot cards for all unversioned files held on the server.
869
+ <hr>
870
+<li>The client examines the uvigot cards received from the server and
871
+ determines which unversioned files need to be exchanged in order
872
+ to bring the client and server into synchronization. The client
873
+ then sends appropriate "uvgimme" or "uvfile" cards back to the
874
+ server.
875
+ <hr>
876
+<li>The server updates its unversioned file store with received "uvfile"
877
+ cards and answers "uvgimme" cards with "uvfile" cards in its reply.
878
+</ol>
879
+
880
+<p>The last two steps might be repeated multiple
881
+times if there is more unversioned content to be transferred than will
882
+fit comfortably in a single HTTP request.
883
+
718884
<h2>6.0 Summary</h2>
719885
720886
<p>Here are the key points of the synchronization protocol:</p>
721887
722888
<ol>
@@ -734,13 +900,16 @@
734900
<li> <b>clone_seqno</b> <i>sequence-number</i>
735901
<li> <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
736902
<li> <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
737903
<li> <b>cfile</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
738904
<li> <b>cfile</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
905
+ <li> <b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i>
739906
<li> <b>private</b>
740907
<li> <b>igot</b> <i>artifact-id</i> ?<i>flag</i>?
908
+ <li> <b>uvigot</b> <i>name mtime hash size</i>
741909
<li> <b>gimme</b> <i>artifact-id</i>
910
+ <li> <b>uvgimme</b> <i>name</i>
742911
<li> <b>cookie</b> <i>cookie-text</i>
743912
<li> <b>reqconfig</b> <i>parameter-name</i>
744913
<li> <b>config</b> <i>parameter-name size</i> <b>\n</b> <i>content</i>
745914
<li> <b>pragma</b> <i>name</i> <i>value...</i>
746915
<li> <b>error</b> <i>error-message</i>
747916
--- www/sync.wiki
+++ www/sync.wiki
@@ -167,18 +167,21 @@
167
168 <p>Privileges are cumulative. There can be multiple successful
169 login cards. The session privileges are the bit-wise OR of the
170 privileges of each individual login.</p>
171
172 <h3>3.3 File and Compressed File Cards</h3>
173
174 <p>Artifacts are transferred using either "file" cards, or "cfile" cards.
175 (The name "file" card comes from the fact that most artifacts correspond to
176 files, and "cfile" is just a compressed file.)
 
 
 
177 </p>
178
179 <h4>3.3.1 File Cards</h4>
180
181 <p>For sync protocols, artifacts are transferred using "file"
182 cards. File cards come in two different formats depending
183 on whether the artifact is sent directly or as a delta from some
184 other artifact.</p>
@@ -186,11 +189,11 @@
186 <blockquote>
187 <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br>
188 <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
189 </blockquote>
190
191 <p>File cards are different from all other cards in that they
192 followed by in-line "payload" data. The content of the artifact
193 or the artifact delta consists of the first <i>size</i> bytes of the
194 x-fossil content that immediately follow the newline that
195 terminates the file card. Only file and cfile cards have this characteristic.
196 </p>
@@ -257,10 +260,44 @@
257 </blockquote>
258
259 <p>The private card has no arguments and must directly precede a
260 file card that contains the private content.</p>
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262 <h3>3.4 Push and Pull Cards</h3>
263
264 <p>Among the first cards in a client-to-server message are
265 the push and pull cards. The push card tells the server that
266 the client is pushing content. The pull card tells the server
@@ -359,10 +396,42 @@
359
360 <p>If the second argument exists and is "1", then the artifact
361 identified by the first argument is private on the sender and should
362 be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364 <h3>3.7 Gimme Cards</h3>
365
366 <p>A gimme card is sent from either client to server or from server
367 to client. The gimme card asks the receiver to send a particular
368 artifact back to the sender. The format of a gimme card is this:</p>
@@ -374,10 +443,26 @@
374 <p>The argument to the gimme card is the ID of the artifact that
375 the sender wants. The receiver will typically respond to a
376 gimme card by sending a file card in its reply or in the next
377 message.</p>
378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379 <h3>3.8 Cookie Cards</h3>
380
381 <p>A cookie card can be used by a server to record a small amount
382 of state information on a client. The server sends a cookie to the
383 client. The client sends the same cookie back to the server on
@@ -539,10 +624,49 @@
539 <p>The send-catalog pragma instructs the server to transmit igot
540 cards for every known artifact. This can help the client and server
541 to get back in synchronization after a prior protocol error. The
542 "--verily" option to the [/help?cmd=sync|fossil sync] command causes
543 the send-catalog pragma to be transmitted.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544 </ol>
545
546 <h3>3.12 Comment Cards</h3>
547
548 <p>Any card that begins with "#" (ASCII 0x23) is a comment card and
@@ -713,10 +837,52 @@
713 The first three steps of a pull are combined with the first five steps
714 of a push. Steps (4) through (7) of a pull are combined with steps
715 (5) through (8) of a push. And steps (8) through (10) of a pull
716 are combined with step (9) of a push.</p>
717
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
718 <h2>6.0 Summary</h2>
719
720 <p>Here are the key points of the synchronization protocol:</p>
721
722 <ol>
@@ -734,13 +900,16 @@
734 <li> <b>clone_seqno</b> <i>sequence-number</i>
735 <li> <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
736 <li> <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
737 <li> <b>cfile</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
738 <li> <b>cfile</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
 
739 <li> <b>private</b>
740 <li> <b>igot</b> <i>artifact-id</i> ?<i>flag</i>?
 
741 <li> <b>gimme</b> <i>artifact-id</i>
 
742 <li> <b>cookie</b> <i>cookie-text</i>
743 <li> <b>reqconfig</b> <i>parameter-name</i>
744 <li> <b>config</b> <i>parameter-name size</i> <b>\n</b> <i>content</i>
745 <li> <b>pragma</b> <i>name</i> <i>value...</i>
746 <li> <b>error</b> <i>error-message</i>
747
--- www/sync.wiki
+++ www/sync.wiki
@@ -167,18 +167,21 @@
167
168 <p>Privileges are cumulative. There can be multiple successful
169 login cards. The session privileges are the bit-wise OR of the
170 privileges of each individual login.</p>
171
172 <h3>3.3 File Cards</h3>
173
174 <p>Artifacts are transferred using either "file" cards, or "cfile"
175 or "uvfile" cards.
176 The name "file" card comes from the fact that most artifacts correspond to
177 files that are under version control.
178 The "cfile" name is an abbreviation for "compressed file".
179 The "uvfile" name is an abbreviation for "unversioned file".
180 </p>
181
182 <h4>3.3.1 Ordinary File Cards</h4>
183
184 <p>For sync protocols, artifacts are transferred using "file"
185 cards. File cards come in two different formats depending
186 on whether the artifact is sent directly or as a delta from some
187 other artifact.</p>
@@ -186,11 +189,11 @@
189 <blockquote>
190 <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br>
191 <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
192 </blockquote>
193
194 <p>File cards are different from most other cards in that they are
195 followed by in-line "payload" data. The content of the artifact
196 or the artifact delta consists of the first <i>size</i> bytes of the
197 x-fossil content that immediately follow the newline that
198 terminates the file card. Only file and cfile cards have this characteristic.
199 </p>
@@ -257,10 +260,44 @@
260 </blockquote>
261
262 <p>The private card has no arguments and must directly precede a
263 file card that contains the private content.</p>
264
265 <h4>3.3.4 Unversioned File Cards</h4>
266
267 <p>Unversioned content is sent in both directions (client to server and
268 server to client) using "uvfile" cards in the following format:
269
270 <blockquote>
271 <b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i>
272 </blockquote>
273
274 <p>The <i>name</i> field is the name of the unversioned file. The
275 <i>mtime</i> is the last modification time of the file in seconds
276 since 1970. The <i>hash</i> field is the SHA1 hash of the content
277 for the unversioned file, or "<b>-</b>" for deleted content.
278 The <i>size</i> field is the (uncompressed) size of the content
279 in bytes. The <i>flags</i> field is an integer which is interpreted
280 as an array of bits. The 0x0004 bit of <i>flags</i> indicates that
281 the <i>content</i> is to be omitted. The content might be omitted if
282 it is too large to transmit, or if the send merely wants to update the
283 modification time of the file without changing the files content.
284 The <i>content</i> is the (uncompressed) content of the file.
285
286 <p>The receiver should only accept the uvfile card if the hash and
287 size match the content and if the mtime is newer than any existing
288 instance of the same file held by the receiver. The sender will not
289 normally transmit a uvfile card unless all these constraints are true,
290 but the receiver should double-check.
291
292 <p>A server should only accept uvfile cards if the login user has
293 the "y" write-unversioned permission.
294
295 <p>Servers send uvfile cards in response to uvgimme cards received from
296 the client. Clients send uvfile cards when they determine that the server
297 needs the content based on uvigot cards previously received from the server.
298
299 <h3>3.4 Push and Pull Cards</h3>
300
301 <p>Among the first cards in a client-to-server message are
302 the push and pull cards. The push card tells the server that
303 the client is pushing content. The pull card tells the server
@@ -359,10 +396,42 @@
396
397 <p>If the second argument exists and is "1", then the artifact
398 identified by the first argument is private on the sender and should
399 be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
400
401 <h4>3.6.1 Unversioned Igot Cards</h4>
402
403 <p>Zero or more "uvigot" cards are sent from client to server when
404 synchronizing unversioned content. The format of a uvigot card is
405 as follows:
406
407 <blockquote>
408 <b>uvigot</b> <i>name mtime hash size</i>
409 </blockquote>
410
411 <p>The <i>name</i> argument is the name of an unversioned file.
412 The <i>mtime</i> is the last modification time of the unversioned file
413 in seconds since 1970.
414 The <i>hash</i> is the SHA1 hash of the unversioned file content, or
415 "<b>-</b>" if the file has been deleted.
416 The <i>size</i> is the uncompressed size of the file in bytes.
417
418 <p>When the server sees a "pragma uv-hash" card for which the hash
419 does not match, it sends uvigot cards for every unversioned file that it
420 holds. The client will use this information to figure out which
421 unversioned files need to be synchronized.
422 The server might also send a uvigot card when it receives a uvgimme card
423 but its reply message size is already oversized and hence unable to hold
424 the usual uvfile reply.
425
426 <p>When a client receives a "uvigot" card, it checks to see if the the
427 file needs to be transfered from client to server or from server to client.
428 If a client-to-server transmission is needed, the client schedules that
429 transfer to occur on a subsequent HTTP request. If a server-to-client
430 transfer is needed, then the client sends a "uvgimme" card back to the
431 server to request the file content.
432
433 <h3>3.7 Gimme Cards</h3>
434
435 <p>A gimme card is sent from either client to server or from server
436 to client. The gimme card asks the receiver to send a particular
437 artifact back to the sender. The format of a gimme card is this:</p>
@@ -374,10 +443,26 @@
443 <p>The argument to the gimme card is the ID of the artifact that
444 the sender wants. The receiver will typically respond to a
445 gimme card by sending a file card in its reply or in the next
446 message.</p>
447
448 <h4>3.7.1 Unversioned Gimme Cards</h4>
449
450 <p>Sync synchronizing unversioned content, the client may send "uvgimme"
451 cards to the server. A uvgimme card requests that the server send
452 unversioned content to the client. The format of a uvgimme card is
453 as follows:
454
455 <blockquote>
456 <b>uvgimme</b> <i>name</i>
457 </blockquote>
458
459 <p>The <i>name</i> is the name of the unversioned file found on the
460 server that the client would like to have. When a server sees a
461 uvgimme card, it normally responses with a uvfile card, though it might
462 also send anoter uvigot card if the HTTP reply is already oversized.
463
464 <h3>3.8 Cookie Cards</h3>
465
466 <p>A cookie card can be used by a server to record a small amount
467 of state information on a client. The server sends a cookie to the
468 client. The client sends the same cookie back to the server on
@@ -539,10 +624,49 @@
624 <p>The send-catalog pragma instructs the server to transmit igot
625 cards for every known artifact. This can help the client and server
626 to get back in synchronization after a prior protocol error. The
627 "--verily" option to the [/help?cmd=sync|fossil sync] command causes
628 the send-catalog pragma to be transmitted.</p>
629
630 <li><p><b>uv-hash</b> <i>HASH</i>
631 <p>The uv-hash pragma is sent from client to server to provoke a
632 synchronization of unversioned content. The <i>HASH</i> is a SHA1
633 hash of the names, modification times, and individual hashes of all
634 unversioned files on the client. If the unversioned content hash
635 from the client does not match the unversioned content hash on the
636 server, then the server will reply with either a "pragma uv-push-ok"
637 or "pragma uv-pull-only" card followed by one "uvigot" card for
638 each unversioned file currently held on the server. The collection
639 of "uvigot" cards sent in response to a "uv-hash" pragma is called
640 the "unversioned catalog". The client will used the unversioned
641 catalog to figure out which files (if any) need to be synchronized
642 between client and server and send appropriate "uvfile" or "uvgimme"
643 cards on the next HTTP request.</p>
644
645 <p>If a client sends a uv-hash pragma and does not receive back
646 either a uv-pull-only or uv-push-ok pragma, that means that the
647 content on the server exactly matches the content on the client and
648 no further synchronization is required.
649
650 <li><p><b>uv-pull-only</b></i>
651 <p>A server sends the uv-pull-only pragma to the client in response
652 to a uv-hash pragma with a mismatched content hash argument. This
653 pragma indicates that there are differences in unversioned content
654 between the client and server but that content can only be transfered
655 from server to client. The server is unwilling to accept content from
656 the client because the client login lacks the "write-unversioned"
657 permission.</p>
658
659 <li><p><b>uv-push-ok</b></i>
660 <p>A server sends the uv-push-ok pragma to the client in response
661 to a uv-hash pragma with a mismatched content hash argument. This
662 pragma indicates that there are differences in unversioned content
663 between the client and server and that content can only be transfered
664 in either direction. The server is willing to accept content from
665 the client because the client login has the "write-unversioned"
666 permission.</p>
667
668 </ol>
669
670 <h3>3.12 Comment Cards</h3>
671
672 <p>Any card that begins with "#" (ASCII 0x23) is a comment card and
@@ -713,10 +837,52 @@
837 The first three steps of a pull are combined with the first five steps
838 of a push. Steps (4) through (7) of a pull are combined with steps
839 (5) through (8) of a push. And steps (8) through (10) of a pull
840 are combined with step (9) of a push.</p>
841
842 <h3>5.4 Unversioned File Sync</h3>
843
844 <p>"Unversioned files" are files held in the repository
845 where only the most recent version of the file is kept rather than
846 the entire change history. Unversioned files are intended to be
847 used to store ephemeral content, such as compiled binaries of the
848 most recent release.
849
850 <p>Unversioned files are identified by name and timestamp (mtime).
851 Only the most recent version of each file (the version with
852 the largest mtime value) is retained.
853
854 <p>Unversioned files are synchronized using the
855 [/help?cmd=unversioned|fossil unversioned sync] command.
856
857 <p>A schematic of an unversioned file synchronization is as follows:
858
859 <ol>
860 <li>The client sends a "pragma uv-hash" card to the server. The argument
861 to the uv-hash pragma is a hash of all filesnames, mtimes, and
862 content hashes for the unversioned files held by the client.
863 <hr>
864 <li>If the unversion content hash from the client matches the unversioned
865 content hash on the server, then nothing needs to be done and the
866 server no-ops. But if the hashes are different, then the server
867 replies with either a uv-pull-only or a uv-push-ok pragma followed by
868 uvigot cards for all unversioned files held on the server.
869 <hr>
870 <li>The client examines the uvigot cards received from the server and
871 determines which unversioned files need to be exchanged in order
872 to bring the client and server into synchronization. The client
873 then sends appropriate "uvgimme" or "uvfile" cards back to the
874 server.
875 <hr>
876 <li>The server updates its unversioned file store with received "uvfile"
877 cards and answers "uvgimme" cards with "uvfile" cards in its reply.
878 </ol>
879
880 <p>The last two steps might be repeated multiple
881 times if there is more unversioned content to be transferred than will
882 fit comfortably in a single HTTP request.
883
884 <h2>6.0 Summary</h2>
885
886 <p>Here are the key points of the synchronization protocol:</p>
887
888 <ol>
@@ -734,13 +900,16 @@
900 <li> <b>clone_seqno</b> <i>sequence-number</i>
901 <li> <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
902 <li> <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
903 <li> <b>cfile</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
904 <li> <b>cfile</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
905 <li> <b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i>
906 <li> <b>private</b>
907 <li> <b>igot</b> <i>artifact-id</i> ?<i>flag</i>?
908 <li> <b>uvigot</b> <i>name mtime hash size</i>
909 <li> <b>gimme</b> <i>artifact-id</i>
910 <li> <b>uvgimme</b> <i>name</i>
911 <li> <b>cookie</b> <i>cookie-text</i>
912 <li> <b>reqconfig</b> <i>parameter-name</i>
913 <li> <b>config</b> <i>parameter-name size</i> <b>\n</b> <i>content</i>
914 <li> <b>pragma</b> <i>name</i> <i>value...</i>
915 <li> <b>error</b> <i>error-message</i>
916

Keyboard Shortcuts

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