Fossil

Check-in [1c8768b0]
Login

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

Overview
Comment:Refactoring and cleanup of some of the hash name interfaces.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fossil-2.0
Files: files | file ages | folders
SHA1:1c8768b0deed45e9afc9de3e42a8ce1d220a03ff
User & Date: drh 2017-02-28 16:25:26
Context
2017-02-28
16:36
Change the --sha1sum options on "fossil commit" and "fossil status" to be --hash, since it is no longer restricted to a single hash algorithm. check-in: 1f61b2dc user: drh tags: fossil-2.0
16:25
Refactoring and cleanup of some of the hash name interfaces. check-in: 1c8768b0 user: drh tags: fossil-2.0
14:14
On-the-fly schema updates. No "fossil rebuild" needed when moving to Fossil 2.0. check-in: 94f4c0aa user: drh tags: fossil-2.0
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/blob.c.

646
647
648
649
650
651
652





653
654
655
656
657
658
659
660
661
662
    blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
  }
  pFrom->iCursor = i;
}

/*
** Return true if the blob contains a valid base16 identifier artifact hash.





*/
int blob_is_hname(Blob *pBlob){
  return hname_validate(blob_buffer(pBlob), blob_size(pBlob))!=HNAME_NONE;
}

/*
** Return true if the blob contains a valid filename
*/
int blob_is_filename(Blob *pBlob){
  return file_is_simple_pathname(blob_str(pBlob), 1);







>
>
>
>
>


|







646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
    blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
  }
  pFrom->iCursor = i;
}

/*
** Return true if the blob contains a valid base16 identifier artifact hash.
**
** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the
** hash is valid.  Both of these are non-zero and therefore "true".
** If the hash is not valid, then HNAME_ERROR is returned, which is zero or
** false.
*/
int blob_is_hname(Blob *pBlob){
  return hname_validate(blob_buffer(pBlob), blob_size(pBlob));
}

/*
** Return true if the blob contains a valid filename
*/
int blob_is_filename(Blob *pBlob){
  return file_is_simple_pathname(blob_str(pBlob), 1);

Changes to src/browse.c.

366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
  i = pParent ? pParent->nFullName+1 : 0;
  while( zPath[i] ){
    FileTreeNode *pNew;
    int iStart = i;
    int nByte;
    while( zPath[i] && zPath[i]!='/' ){ i++; }
    nByte = sizeof(*pNew) + i + 1;
    if( zUuid!=0 && zPath[i]==0 ) nByte += HNAME_LEN_MAX+1;
    pNew = fossil_malloc( nByte );
    memset(pNew, 0, sizeof(*pNew));
    pNew->zFullName = (char*)&pNew[1];
    memcpy(pNew->zFullName, zPath, i);
    pNew->zFullName[i] = 0;
    pNew->nFullName = i;
    if( zUuid!=0 && zPath[i]==0 ){







|







366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
  i = pParent ? pParent->nFullName+1 : 0;
  while( zPath[i] ){
    FileTreeNode *pNew;
    int iStart = i;
    int nByte;
    while( zPath[i] && zPath[i]!='/' ){ i++; }
    nByte = sizeof(*pNew) + i + 1;
    if( zUuid!=0 && zPath[i]==0 ) nByte += HNAME_MAX+1;
    pNew = fossil_malloc( nByte );
    memset(pNew, 0, sizeof(*pNew));
    pNew->zFullName = (char*)&pNew[1];
    memcpy(pNew->zFullName, zPath, i);
    pNew->zFullName[i] = 0;
    pNew->nFullName = i;
    if( zUuid!=0 && zPath[i]==0 ){

Changes to src/bundle.c.

445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
...
595
596
597
598
599
600
601
602
603

604
605
606
607
608
609
610
    int rid;
    blob_zero(&h1);
    db_column_blob(&q, 0, &h1);
    blob_zero(&c1);
    db_column_blob(&q, 1, &c1);
    blob_uncompress(&c1, &c1);
    blob_zero(&c2);
    if( db_column_type(&q,2)==SQLITE_TEXT && db_column_bytes(&q,2)==40 ){
      Blob basis;
      rid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q",
                   db_column_text(&q,2));
      content_get(rid, &basis);
      blob_delta_apply(&basis, &c1, &c2);
      blob_reset(&basis);
      blob_reset(&c1);
................................................................................
  /* If the bundle contains deltas with a basis that is external to the
  ** bundle and those external basis files are missing from the local
  ** repo, then the delta encodings cannot be decoded and the bundle cannot
  ** be extracted. */
  zMissingDeltas = db_text(0,
      "SELECT group_concat(substr(delta,1,10),' ')"
      "  FROM bblob"
      " WHERE typeof(delta)='text' AND length(delta)=40"
      "   AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.delta)");

  if( zMissingDeltas && zMissingDeltas[0] ){
    fossil_fatal("delta basis artifacts not found in repository: %s",
                 zMissingDeltas);
  }

  db_begin_transaction();
  db_multi_exec(







|







 







|
|
>







445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
...
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
    int rid;
    blob_zero(&h1);
    db_column_blob(&q, 0, &h1);
    blob_zero(&c1);
    db_column_blob(&q, 1, &c1);
    blob_uncompress(&c1, &c1);
    blob_zero(&c2);
    if( db_column_type(&q,2)==SQLITE_TEXT && db_column_bytes(&q,2)>=HNAME_MIN ){
      Blob basis;
      rid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q",
                   db_column_text(&q,2));
      content_get(rid, &basis);
      blob_delta_apply(&basis, &c1, &c2);
      blob_reset(&basis);
      blob_reset(&c1);
................................................................................
  /* If the bundle contains deltas with a basis that is external to the
  ** bundle and those external basis files are missing from the local
  ** repo, then the delta encodings cannot be decoded and the bundle cannot
  ** be extracted. */
  zMissingDeltas = db_text(0,
      "SELECT group_concat(substr(delta,1,10),' ')"
      "  FROM bblob"
      " WHERE typeof(delta)='text' AND length(delta)>=%d"
      "   AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.delta)",
      HNAME_MIN);
  if( zMissingDeltas && zMissingDeltas[0] ){
    fossil_fatal("delta basis artifacts not found in repository: %s",
                 zMissingDeltas);
  }

  db_begin_transaction();
  db_multi_exec(

Changes to src/db.c.

2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
**
** The CONCEALED table is meant to obscure email addresses.  Every valid
** email address will contain a "@" character and "@" is not valid within
** an SHA1 hash so there is no chance that a valid email address will go
** unconcealed.
*/
char *db_conceal(const char *zContent, int n){
  static char zHash[HNAME_LEN_MAX+1];
  Blob out;
  if( hname_validate(zContent, n)!=HNAME_NONE ){
    memcpy(zHash, zContent, n);
    zHash[n] = 0;
  }else{
    sha1sum_step_text(zContent, n);
    sha1sum_finish(&out);
    sqlite3_snprintf(sizeof(zHash), zHash, "%s", blob_str(&out));
    blob_reset(&out);







|

|







2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
**
** The CONCEALED table is meant to obscure email addresses.  Every valid
** email address will contain a "@" character and "@" is not valid within
** an SHA1 hash so there is no chance that a valid email address will go
** unconcealed.
*/
char *db_conceal(const char *zContent, int n){
  static char zHash[HNAME_MAX+1];
  Blob out;
  if( hname_validate(zContent, n) ){
    memcpy(zHash, zContent, n);
    zHash[n] = 0;
  }else{
    sha1sum_step_text(zContent, n);
    sha1sum_finish(&out);
    sqlite3_snprintf(sizeof(zHash), zHash, "%s", blob_str(&out));
    blob_reset(&out);

Changes to src/event.c.

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
  }
  zTag = mprintf("event-%s", zId);
  rid = db_int(0,
    "SELECT rid FROM tagxref"
    " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB '%q*')"
    " ORDER BY mtime DESC", zTag
  );
  if( rid && strlen(zId)<40 ){
    zId = db_text(0,
      "SELECT substr(tagname,7) FROM tag WHERE tagname GLOB '%q*'",
      zTag
    );
  }
  free(zTag);








|







382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
  }
  zTag = mprintf("event-%s", zId);
  rid = db_int(0,
    "SELECT rid FROM tagxref"
    " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB '%q*')"
    " ORDER BY mtime DESC", zTag
  );
  if( rid && strlen(zId)<HNAME_MIN ){
    zId = db_text(0,
      "SELECT substr(tagname,7) FROM tag WHERE tagname GLOB '%q*'",
      zTag
    );
  }
  free(zTag);

Changes to src/hname.c.

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include "hname.h"


#if INTERFACE
/*
** Code numbers for the allowed hash algorithms.
*/
#define HNAME_NONE   (-1)   /* Not a valid hash */
#define HNAME_SHA1   0      /* SHA1 */
#define HNAME_K224   1      /* SHA3-224 */
#define HNAME_K256   2      /* SHA3-256 */

/*
** Minimum and maximum lengths for a hash value when hex encoded.
*/
#define HNAME_LEN_MIN  40     /* Length for SHA1 */
#define HNAME_LEN_MAX  64     /* Length for SHA3-256 */

/*
** Hash lengths for the various algorithms
*/
#define HNAME_LEN_SHA1   40
#define HNAME_LEN_K224   56
#define HNAME_LEN_K256   64
................................................................................


/*
** Convert a hash algorithm code number into a string name for that algorithm.
*/
const char *hname_algname(int aid){
  if( aid==HNAME_K224 ) return "SHA3-224";
  if( aid==HNAME_K256 ) return "SHA3-256";
  return "SHA1";
}

/*
** Given a hash algorithm name, return its appropriate number.  Return -1
** if the name is unknown.
*/
int hname_algid(const char *zName){
  if( fossil_stricmp(zName,"sha1")==0 ) return HNAME_SHA1;
  if( fossil_stricmp(zName,"sha3-224")==0 ) return HNAME_K224;
  if( fossil_stricmp(zName,"sha3-256")==0 ) return HNAME_K256;
  return -1;
}

/*
** Return the integer hash algorithm code number (ex: HNAME_K224) for
** the hash string provided.  Or return HNAME_NONE if the input string
** is not a valid artifact hash string.
*/
int hname_validate(const char *zHash, int nHash){
  int id;
  switch( nHash ){
    case HNAME_LEN_SHA1:   id = HNAME_SHA1;  break;
    case HNAME_LEN_K224:   id = HNAME_K224;  break;
    case HNAME_LEN_K256:   id = HNAME_K256;  break;
    default:               return HNAME_NONE;
  }
  if( !validate16(zHash, nHash) ) return HNAME_NONE;
  return id;
}







|
|
|
|




|
|







 







<




|
|





|




|








|

|


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
..
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include "hname.h"


#if INTERFACE
/*
** Code numbers for the allowed hash algorithms.
*/
#define HNAME_ERROR  0      /* Not a valid hash */
#define HNAME_SHA1   1      /* SHA1 */
#define HNAME_K224   2      /* SHA3-224 */
#define HNAME_K256   3      /* SHA3-256 */

/*
** Minimum and maximum lengths for a hash value when hex encoded.
*/
#define HNAME_MIN  40     /* Length for SHA1 */
#define HNAME_MAX  64     /* Length for SHA3-256 */

/*
** Hash lengths for the various algorithms
*/
#define HNAME_LEN_SHA1   40
#define HNAME_LEN_K224   56
#define HNAME_LEN_K256   64
................................................................................


/*
** Convert a hash algorithm code number into a string name for that algorithm.
*/
const char *hname_algname(int aid){
  if( aid==HNAME_K224 ) return "SHA3-224";

  return "SHA1";
}

/*
** Given a hash algorithm name, return its appropriate number.  Return 
** HNAME_ERROR if the name is unknown.
*/
int hname_algid(const char *zName){
  if( fossil_stricmp(zName,"sha1")==0 ) return HNAME_SHA1;
  if( fossil_stricmp(zName,"sha3-224")==0 ) return HNAME_K224;
  if( fossil_stricmp(zName,"sha3-256")==0 ) return HNAME_K256;
  return HNAME_ERROR;
}

/*
** Return the integer hash algorithm code number (ex: HNAME_K224) for
** the hash string provided.  Or return HNAME_ERROR (0) if the input string
** is not a valid artifact hash string.
*/
int hname_validate(const char *zHash, int nHash){
  int id;
  switch( nHash ){
    case HNAME_LEN_SHA1:   id = HNAME_SHA1;  break;
    case HNAME_LEN_K224:   id = HNAME_K224;  break;
    case HNAME_LEN_K256:   id = HNAME_K256;  break;
    default:               return HNAME_ERROR;
  }
  if( !validate16(zHash, nHash) ) return HNAME_ERROR;
  return id;
}

Changes to src/manifest.c.

447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
...
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
...
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
...
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
...
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
...
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
...
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
....
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
        if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
        if( p->zAttachName!=0 ) goto manifest_syntax_error;
        defossilize(zName);
        if( !file_is_simple_pathname(zName, 0) ){
          SYNTAX("invalid filename on A-card");
        }
        defossilize(zTarget);
        if( hname_validate(zTarget,nTarget)==HNAME_NONE
           && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
          SYNTAX("invalid target on A-card");
        }
        if( zSrc && hname_validate(zSrc,nSrc)==HNAME_NONE ){
          SYNTAX("invalid source on A-card");
        }
        p->zAttachName = (char*)file_tail(zName);
        p->zAttachSrc = zSrc;
        p->zAttachTarget = zTarget;
        break;
      }
................................................................................
      **
      ** A B-line gives the artifact hash for the baseline of a delta-manifest.
      */
      case 'B': {
        if( p->zBaseline ) SYNTAX("more than one B-card");
        p->zBaseline = next_token(&x, &sz);
        if( p->zBaseline==0 ) SYNTAX("missing hash on B-card");
        if( hname_validate(p->zBaseline,sz)==HNAME_NONE ){
          SYNTAX("invalid hash on B-card");
        }
        break;
      }


      /*
................................................................................
      ** is when the specific event is said to occur.
      */
      case 'E': {
        if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
        p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
        if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
        p->zEventId = next_token(&x, &sz);
        if( hname_validate(p->zEventId, sz)==HNAME_NONE ){
          SYNTAX("malformed hash on E-card");
        }
        break;
      }

      /*
      **     F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
................................................................................
        if( zName==0 ) SYNTAX("missing filename on F-card");
        defossilize(zName);
        if( !file_is_simple_pathname(zName, 0) ){
          SYNTAX("F-card filename is not a simple path");
        }
        zUuid = next_token(&x, &sz);
        if( p->zBaseline==0 || zUuid!=0 ){
          if( hname_validate(zUuid,sz)==HNAME_NONE ){
            SYNTAX("F-card hash invalid");
          }
        }
        zPerm = next_token(&x,0);
        zPriorName = next_token(&x,0);
        if( zPriorName ){
          defossilize(zPriorName);
................................................................................
      **
      ** An M-line identifies another artifact by its hash.  M-lines
      ** occur in clusters only.
      */
      case 'M': {
        zUuid = next_token(&x, &sz);
        if( zUuid==0 ) SYNTAX("missing hash on M-card");
        if( hname_validate(zUuid,sz)==HNAME_NONE ){
          SYNTAX("Invalid hash on M-card");
        }
        if( p->nCChild>=p->nCChildAlloc ){
          p->nCChildAlloc = p->nCChildAlloc*2 + 10;
          p->azCChild = fossil_realloc(p->azCChild
                                 , p->nCChildAlloc*sizeof(p->azCChild[0]) );
        }
................................................................................
      ** this artifact.  The first parent is the primary parent.  All
      ** others are parents by merge. Note that the initial empty
      ** check-in historically has an empty P-card, so empty P-cards
      ** must be accepted.
      */
      case 'P': {
        while( (zUuid = next_token(&x, &sz))!=0 ){
          if( hname_validate(zUuid, sz)==HNAME_NONE ){
             SYNTAX("invalid hash on P-card");
          }
          if( p->nParent>=p->nParentAlloc ){
            p->nParentAlloc = p->nParentAlloc*2 + 5;
            p->azParent = fossil_realloc(p->azParent,
                               p->nParentAlloc*sizeof(char*));
          }
................................................................................
      ** this check-in ("+") or backed out of this check-in ("-").
      */
      case 'Q': {
        if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing hash on Q-card");
        if( zUuid[0]!='+' && zUuid[0]!='-' ){
          SYNTAX("Q-card does not begin with '+' or '-'");
        }
        if( hname_validate(&zUuid[1], sz-1)==HNAME_NONE ){
          SYNTAX("invalid hash on Q-card");
        }
        n = p->nCherrypick;
        p->nCherrypick++;
        p->aCherrypick = fossil_realloc(p->aCherrypick,
                                 p->nCherrypick*sizeof(p->aCherrypick[0]));
        p->aCherrypick[n].zCPTarget = zUuid;
        p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz);
        if( zUuid && hname_validate(zUuid,sz)==HNAME_NONE ){
          SYNTAX("invalid second hash on Q-card");
        }
        break;
      }

      /*
      **     R <md5sum>
................................................................................
        char *zName, *zValue;
        zName = next_token(&x, 0);
        if( zName==0 ) SYNTAX("missing name on T-card");
        zUuid = next_token(&x, &sz);
        if( zUuid==0 ) SYNTAX("missing artifact hash on T-card");
        zValue = next_token(&x, 0);
        if( zValue ) defossilize(zValue);
        if( hname_validate(zUuid, sz)!=HNAME_NONE ){
          /* A valid artifact hash */
          if( p->zEventId ) SYNTAX("non-self-referential T-card in event");
        }else if( sz==1 && zUuid[0]=='*' ){
          zUuid = 0;
          hasSelfRefTag = 1;
          if( p->zEventId && zName[0]!='+' ){
            SYNTAX("propagating T-card in event");
................................................................................
void manifest_reparent_checkin(int rid, const char *zValue){
  int nParent = 0;
  char *zCopy = 0;
  char **azParent = 0;
  Manifest *p = 0;
  int i, j;
  int n = (int)strlen(zValue);
  int mxParent = (n+1)/(HNAME_LEN_MIN+1);

  if( mxParent<1 ) return;
  zCopy = fossil_strdup(zValue);
  azParent = fossil_malloc( sizeof(azParent[0])*mxParent );
  for(nParent=0, i=0; zCopy[i]; i++){
    char *z = &zCopy[i];
    azParent[nParent++] = z;
    if( nParent>mxParent ) goto reparent_abort;
    for(j=HNAME_LEN_MIN; z[j]>' '; j++){}
    if( hname_validate(z, j)==HNAME_NONE ) goto reparent_abort;
    if( z[j]==0 ) break;
    z[j] = 0;
    i += j;
  }
  if( !db_exists("SELECT 1 FROM plink WHERE cid=%d AND pid=%d",
                 rid, uuid_to_rid(azParent[0],0))
  ){







|



|







 







|







 







|







 







|







 







|







 







|







 







|








|







 







|







 







|








|
|







447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
...
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
...
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
...
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
...
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
...
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
...
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
....
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
        if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
        if( p->zAttachName!=0 ) goto manifest_syntax_error;
        defossilize(zName);
        if( !file_is_simple_pathname(zName, 0) ){
          SYNTAX("invalid filename on A-card");
        }
        defossilize(zTarget);
        if( !hname_validate(zTarget,nTarget)
           && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
          SYNTAX("invalid target on A-card");
        }
        if( zSrc && !hname_validate(zSrc,nSrc) ){
          SYNTAX("invalid source on A-card");
        }
        p->zAttachName = (char*)file_tail(zName);
        p->zAttachSrc = zSrc;
        p->zAttachTarget = zTarget;
        break;
      }
................................................................................
      **
      ** A B-line gives the artifact hash for the baseline of a delta-manifest.
      */
      case 'B': {
        if( p->zBaseline ) SYNTAX("more than one B-card");
        p->zBaseline = next_token(&x, &sz);
        if( p->zBaseline==0 ) SYNTAX("missing hash on B-card");
        if( !hname_validate(p->zBaseline,sz) ){
          SYNTAX("invalid hash on B-card");
        }
        break;
      }


      /*
................................................................................
      ** is when the specific event is said to occur.
      */
      case 'E': {
        if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
        p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
        if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
        p->zEventId = next_token(&x, &sz);
        if( !hname_validate(p->zEventId, sz) ){
          SYNTAX("malformed hash on E-card");
        }
        break;
      }

      /*
      **     F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
................................................................................
        if( zName==0 ) SYNTAX("missing filename on F-card");
        defossilize(zName);
        if( !file_is_simple_pathname(zName, 0) ){
          SYNTAX("F-card filename is not a simple path");
        }
        zUuid = next_token(&x, &sz);
        if( p->zBaseline==0 || zUuid!=0 ){
          if( !hname_validate(zUuid,sz) ){
            SYNTAX("F-card hash invalid");
          }
        }
        zPerm = next_token(&x,0);
        zPriorName = next_token(&x,0);
        if( zPriorName ){
          defossilize(zPriorName);
................................................................................
      **
      ** An M-line identifies another artifact by its hash.  M-lines
      ** occur in clusters only.
      */
      case 'M': {
        zUuid = next_token(&x, &sz);
        if( zUuid==0 ) SYNTAX("missing hash on M-card");
        if( !hname_validate(zUuid,sz) ){
          SYNTAX("Invalid hash on M-card");
        }
        if( p->nCChild>=p->nCChildAlloc ){
          p->nCChildAlloc = p->nCChildAlloc*2 + 10;
          p->azCChild = fossil_realloc(p->azCChild
                                 , p->nCChildAlloc*sizeof(p->azCChild[0]) );
        }
................................................................................
      ** this artifact.  The first parent is the primary parent.  All
      ** others are parents by merge. Note that the initial empty
      ** check-in historically has an empty P-card, so empty P-cards
      ** must be accepted.
      */
      case 'P': {
        while( (zUuid = next_token(&x, &sz))!=0 ){
          if( !hname_validate(zUuid, sz) ){
             SYNTAX("invalid hash on P-card");
          }
          if( p->nParent>=p->nParentAlloc ){
            p->nParentAlloc = p->nParentAlloc*2 + 5;
            p->azParent = fossil_realloc(p->azParent,
                               p->nParentAlloc*sizeof(char*));
          }
................................................................................
      ** this check-in ("+") or backed out of this check-in ("-").
      */
      case 'Q': {
        if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing hash on Q-card");
        if( zUuid[0]!='+' && zUuid[0]!='-' ){
          SYNTAX("Q-card does not begin with '+' or '-'");
        }
        if( !hname_validate(&zUuid[1], sz-1) ){
          SYNTAX("invalid hash on Q-card");
        }
        n = p->nCherrypick;
        p->nCherrypick++;
        p->aCherrypick = fossil_realloc(p->aCherrypick,
                                 p->nCherrypick*sizeof(p->aCherrypick[0]));
        p->aCherrypick[n].zCPTarget = zUuid;
        p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz);
        if( zUuid && !hname_validate(zUuid,sz) ){
          SYNTAX("invalid second hash on Q-card");
        }
        break;
      }

      /*
      **     R <md5sum>
................................................................................
        char *zName, *zValue;
        zName = next_token(&x, 0);
        if( zName==0 ) SYNTAX("missing name on T-card");
        zUuid = next_token(&x, &sz);
        if( zUuid==0 ) SYNTAX("missing artifact hash on T-card");
        zValue = next_token(&x, 0);
        if( zValue ) defossilize(zValue);
        if( hname_validate(zUuid, sz) ){
          /* A valid artifact hash */
          if( p->zEventId ) SYNTAX("non-self-referential T-card in event");
        }else if( sz==1 && zUuid[0]=='*' ){
          zUuid = 0;
          hasSelfRefTag = 1;
          if( p->zEventId && zName[0]!='+' ){
            SYNTAX("propagating T-card in event");
................................................................................
void manifest_reparent_checkin(int rid, const char *zValue){
  int nParent = 0;
  char *zCopy = 0;
  char **azParent = 0;
  Manifest *p = 0;
  int i, j;
  int n = (int)strlen(zValue);
  int mxParent = (n+1)/(HNAME_MIN+1);

  if( mxParent<1 ) return;
  zCopy = fossil_strdup(zValue);
  azParent = fossil_malloc( sizeof(azParent[0])*mxParent );
  for(nParent=0, i=0; zCopy[i]; i++){
    char *z = &zCopy[i];
    azParent[nParent++] = z;
    if( nParent>mxParent ) goto reparent_abort;
    for(j=HNAME_MIN; z[j]>' '; j++){}
    if( !hname_validate(z, j) ) goto reparent_abort;
    if( z[j]==0 ) break;
    z[j] = 0;
    i += j;
  }
  if( !db_exists("SELECT 1 FROM plink WHERE cid=%d AND pid=%d",
                 rid, uuid_to_rid(azParent[0],0))
  ){

Changes to src/name.c.

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
....
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
      "   AND event.type GLOB '%q'",
      zTagBase, zDate, zType
    );
    return rid;
  }

  /* artifact hash or prefix */
  if( nTag>=4 && nTag<=HNAME_LEN_MAX && validate16(zTag, nTag) ){
    Stmt q;
    char zUuid[HNAME_LEN_MAX+1];
    memcpy(zUuid, zTag, nTag+1);
    canonical16(zUuid, nTag);
    rid = 0;
    if( zType[0]=='*' ){
      db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUuid);
    }else{
      db_prepare(&q,
................................................................................
** collisions of a given UUID based on its length on UUIDs no shorter
** than 4 characters in length.
*/
int name_collisions(const char *zName){
  int c = 0;         /* count of collisions for zName */
  int nLen;          /* length of zName */
  nLen = strlen(zName);
  if( nLen>=4 && nLen<=HNAME_LEN_MAX && validate16(zName, nLen) ){
    c = db_int(0,
      "SELECT"
      " (SELECT count(*) FROM ticket"
      "   WHERE tkt_uuid GLOB '%q*') +"
      " (SELECT count(*) FROM tag"
      "   WHERE tagname GLOB 'event-%q*') +"
      " (SELECT count(*) FROM blob"
................................................................................
}

/*
** COMMAND: whatis*
**
** Usage: %fossil whatis NAME
**
** Resolve the symbol NAME into its canonical 40-character SHA1-hash
** artifact name and provide a description of what role that artifact
** plays.
**
** Options:
**
**    --type TYPE          Only find artifacts of TYPE (one of: 'ci', 't',
**                         'w', 'g', or 'e').
................................................................................
** Generate a report on the number of collisions in SHA1 hashes
** generated by the SQL given in the argument.
*/
static void collision_report(const char *zSql){
  int i, j, kk;
  int nHash = 0;
  Stmt q;
  char zPrev[HNAME_LEN_MAX+1];
  struct {
    int cnt;
    char *azHit[MAX_COLLIDE];
    char z[HNAME_LEN_MAX+1];
  } aCollide[HNAME_LEN_MAX+1];
  memset(aCollide, 0, sizeof(aCollide));
  memset(zPrev, 0, sizeof(zPrev));
  db_prepare(&q,"%s",zSql/*safe-for-%s*/);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q,0);
    int n = db_column_bytes(&q,0);
    int i;
    nHash++;
    for(i=0; zPrev[i] && zPrev[i]==zUuid[i]; i++){}
    if( i>0 && i<=HNAME_LEN_MAX ){
      if( i>=4 && aCollide[i].cnt<MAX_COLLIDE ){
        aCollide[i].azHit[aCollide[i].cnt] = mprintf("%.*s", i, zPrev);
      }
      aCollide[i].cnt++;
      if( aCollide[i].z[0]==0 ) memcpy(aCollide[i].z, zPrev, n+1);
    }
    memcpy(zPrev, zUuid, n+1);
  }
  db_finalize(&q);
  @ <table border=1><thead>
  @ <tr><th>Length<th>Instances<th>First Instance</tr>
  @ </thead><tbody>
  for(i=1; i<=HNAME_LEN_MAX; i++){
    if( aCollide[i].cnt==0 ) continue;
    @ <tr><td>%d(i)<td>%d(aCollide[i].cnt)<td>%h(aCollide[i].z)</tr>
  }
  @ </tbody></table>
  @ <p>Total number of hashes: %d(nHash)</p>
  kk = 0;
  for(i=HNAME_LEN_MAX; i>=4; i--){
    if( aCollide[i].cnt==0 ) continue;
    if( aCollide[i].cnt>200 ) break;
    kk += aCollide[i].cnt;
    if( aCollide[i].cnt<25 ){
      @ <p>Collisions of length %d(i):
    }else{
      @ <p>First 25 collisions of length %d(i):







|

|







 







|







 







|







 







|



|
|









|












|






|







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
....
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
      "   AND event.type GLOB '%q'",
      zTagBase, zDate, zType
    );
    return rid;
  }

  /* artifact hash or prefix */
  if( nTag>=4 && nTag<=HNAME_MAX && validate16(zTag, nTag) ){
    Stmt q;
    char zUuid[HNAME_MAX+1];
    memcpy(zUuid, zTag, nTag+1);
    canonical16(zUuid, nTag);
    rid = 0;
    if( zType[0]=='*' ){
      db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUuid);
    }else{
      db_prepare(&q,
................................................................................
** collisions of a given UUID based on its length on UUIDs no shorter
** than 4 characters in length.
*/
int name_collisions(const char *zName){
  int c = 0;         /* count of collisions for zName */
  int nLen;          /* length of zName */
  nLen = strlen(zName);
  if( nLen>=4 && nLen<=HNAME_MAX && validate16(zName, nLen) ){
    c = db_int(0,
      "SELECT"
      " (SELECT count(*) FROM ticket"
      "   WHERE tkt_uuid GLOB '%q*') +"
      " (SELECT count(*) FROM tag"
      "   WHERE tagname GLOB 'event-%q*') +"
      " (SELECT count(*) FROM blob"
................................................................................
}

/*
** COMMAND: whatis*
**
** Usage: %fossil whatis NAME
**
** Resolve the symbol NAME into its canonical artifact hash
** artifact name and provide a description of what role that artifact
** plays.
**
** Options:
**
**    --type TYPE          Only find artifacts of TYPE (one of: 'ci', 't',
**                         'w', 'g', or 'e').
................................................................................
** Generate a report on the number of collisions in SHA1 hashes
** generated by the SQL given in the argument.
*/
static void collision_report(const char *zSql){
  int i, j, kk;
  int nHash = 0;
  Stmt q;
  char zPrev[HNAME_MAX+1];
  struct {
    int cnt;
    char *azHit[MAX_COLLIDE];
    char z[HNAME_MAX+1];
  } aCollide[HNAME_MAX+1];
  memset(aCollide, 0, sizeof(aCollide));
  memset(zPrev, 0, sizeof(zPrev));
  db_prepare(&q,"%s",zSql/*safe-for-%s*/);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q,0);
    int n = db_column_bytes(&q,0);
    int i;
    nHash++;
    for(i=0; zPrev[i] && zPrev[i]==zUuid[i]; i++){}
    if( i>0 && i<=HNAME_MAX ){
      if( i>=4 && aCollide[i].cnt<MAX_COLLIDE ){
        aCollide[i].azHit[aCollide[i].cnt] = mprintf("%.*s", i, zPrev);
      }
      aCollide[i].cnt++;
      if( aCollide[i].z[0]==0 ) memcpy(aCollide[i].z, zPrev, n+1);
    }
    memcpy(zPrev, zUuid, n+1);
  }
  db_finalize(&q);
  @ <table border=1><thead>
  @ <tr><th>Length<th>Instances<th>First Instance</tr>
  @ </thead><tbody>
  for(i=1; i<=HNAME_MAX; i++){
    if( aCollide[i].cnt==0 ) continue;
    @ <tr><td>%d(i)<td>%d(aCollide[i].cnt)<td>%h(aCollide[i].z)</tr>
  }
  @ </tbody></table>
  @ <p>Total number of hashes: %d(nHash)</p>
  kk = 0;
  for(i=HNAME_MAX; i>=4; i--){
    if( aCollide[i].cnt==0 ) continue;
    if( aCollide[i].cnt>200 ) break;
    kk += aCollide[i].cnt;
    if( aCollide[i].cnt<25 ){
      @ <p>Collisions of length %d(i):
    }else{
      @ <p>First 25 collisions of length %d(i):

Changes to src/rebuild.c.

1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
**
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
**
**
** This command exports all artifacts of a given repository and
** writes all artifacts to the file system. The DESTINATION directory
** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
** AABBBBBBBBB.. is the 40 character artifact ID, AA the first 2 characters.
** If -L|--prefixlength is given, the length (default 2) of the directory
** prefix can be set to 0,1,..,9 characters.
**
** Options:
**   -R|--repository REPOSITORY  deconstruct given REPOSITORY
**   -L|--prefixlength N         set the length of the names of the DESTINATION
**                               subdirectories to N







|







1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
**
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
**
**
** This command exports all artifacts of a given repository and
** writes all artifacts to the file system. The DESTINATION directory
** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters.
** If -L|--prefixlength is given, the length (default 2) of the directory
** prefix can be set to 0,1,..,9 characters.
**
** Options:
**   -R|--repository REPOSITORY  deconstruct given REPOSITORY
**   -L|--prefixlength N         set the length of the names of the DESTINATION
**                               subdirectories to N

Changes to src/shun.c.

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
      }
      i++;
    }
    zCanonical[j+1] = zCanonical[j] = 0;
    p = zCanonical;
    while( *p ){
      int nUuid = strlen(p);
      if( hname_validate(p, nUuid)==HNAME_NONE ){
        @ <p class="generalError">Error: Bad artifact IDs.</p>
        fossil_free(zCanonical);
        zCanonical = 0;
        break;
      }else{
        canonical16(p, nUuid);
        p += nUuid+1;
................................................................................
  }
  @ <p>A shunned artifact will not be pushed nor accepted in a pull and the
  @ artifact content will be purged from the repository the next time the
  @ repository is rebuilt.  A list of shunned artifacts can be seen at the
  @ bottom of this page.</p>
  @
  @ <a name="addshun"></a>
  @ <p>To shun artifacts, enter their artifact IDs (the 40-character SHA1
  @ hash of the artifacts) in the
  @ following box and press the "Shun" button.  This will cause the artifacts
  @ to be removed from the repository and will prevent the artifacts from being
  @ readded to the repository by subsequent sync operation.</p>
  @
  @ <p>Note that you must enter the full 40-character artifact IDs, not
  @ an abbreviation or a symbolic tag.</p>
  @
  @ <p>Warning:  Shunning should only be used to remove inappropriate content
  @ from the repository.  Inappropriate content includes such things as
  @ spam added to Wiki, files that violate copyright or patent agreements,
  @ or artifacts that by design or accident interfere with the processing
  @ of the repository.  Do not shun artifacts merely to remove them from
  @ sight - set the "hidden" tag on such artifacts instead.</p>







|







 







|
|




|
|







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
      }
      i++;
    }
    zCanonical[j+1] = zCanonical[j] = 0;
    p = zCanonical;
    while( *p ){
      int nUuid = strlen(p);
      if( !hname_validate(p, nUuid) ){
        @ <p class="generalError">Error: Bad artifact IDs.</p>
        fossil_free(zCanonical);
        zCanonical = 0;
        break;
      }else{
        canonical16(p, nUuid);
        p += nUuid+1;
................................................................................
  }
  @ <p>A shunned artifact will not be pushed nor accepted in a pull and the
  @ artifact content will be purged from the repository the next time the
  @ repository is rebuilt.  A list of shunned artifacts can be seen at the
  @ bottom of this page.</p>
  @
  @ <a name="addshun"></a>
  @ <p>To shun artifacts, enter their artifact hashes (the 40- or
  @ 64-character lowercase hexadecimal hash of the artifact content) in the
  @ following box and press the "Shun" button.  This will cause the artifacts
  @ to be removed from the repository and will prevent the artifacts from being
  @ readded to the repository by subsequent sync operation.</p>
  @
  @ <p>Note that you must enter the full 40- or 64-character artifact hashes, 
  @ not an abbreviation or a symbolic tag.</p>
  @
  @ <p>Warning:  Shunning should only be used to remove inappropriate content
  @ from the repository.  Inappropriate content includes such things as
  @ spam added to Wiki, files that violate copyright or patent agreements,
  @ or artifacts that by design or accident interfere with the processing
  @ of the repository.  Do not shun artifacts merely to remove them from
  @ sight - set the "hidden" tag on such artifacts instead.</p>

Changes to src/verify.c.

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
static void verify_rid(int rid){
  Blob uuid, hash, content;
  if( content_size(rid, 0)<0 ){
    return;  /* No way to verify phantoms */
  }
  blob_zero(&uuid);
  db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( hname_validate(blob_buffer(&uuid), blob_size(&uuid))!=HNAME_NONE ){
    fossil_fatal("not a valid rid: %d", rid);
  }
  if( content_get(rid, &content) ){
    sha1sum_blob(&content, &hash);
    blob_reset(&content);
    if( blob_compare(&uuid, &hash) ){
      fossil_fatal("hash of rid %d (%b) does not match its uuid (%b)",







|







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
static void verify_rid(int rid){
  Blob uuid, hash, content;
  if( content_size(rid, 0)<0 ){
    return;  /* No way to verify phantoms */
  }
  blob_zero(&uuid);
  db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( !hname_validate(blob_buffer(&uuid), blob_size(&uuid)) ){
    fossil_fatal("not a valid rid: %d", rid);
  }
  if( content_get(rid, &content) ){
    sha1sum_blob(&content, &hash);
    blob_reset(&content);
    if( blob_compare(&uuid, &hash) ){
      fossil_fatal("hash of rid %d (%b) does not match its uuid (%b)",

Changes to src/vfile.c.

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
..
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
*/
#include "config.h"
#include "vfile.h"
#include <assert.h>
#include <sys/types.h>

/*
** The input is guaranteed to be a 40-character well-formed UUID.
** Find its rid.
*/
int fast_uuid_to_rid(const char *zUuid){
  static Stmt q;
  int rid;
  db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid");
  db_bind_text(&q, ":uuid", zUuid);
  if( db_step(&q)==SQLITE_ROW ){
................................................................................
**
** If the UUID is not found and phantomize is 1 or 2, then attempt to
** create a phantom record.  A private phantom is created for 2 and
** a public phantom is created for 1.
*/
int uuid_to_rid(const char *zUuid, int phantomize){
  int rid, sz;
  char z[HNAME_LEN_MAX+1];

  sz = strlen(zUuid);
  if( hname_validate(zUuid, sz)!=HNAME_NONE ){
    return 0;
  }
  memcpy(z, zUuid, sz+1);
  canonical16(z, sz);
  rid = fast_uuid_to_rid(z);
  if( rid==0 && phantomize ){
    rid = content_new(zUuid, phantomize-1);
  }







|
|







 







|


|
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
..
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
*/
#include "config.h"
#include "vfile.h"
#include <assert.h>
#include <sys/types.h>

/*
** The input is guaranteed to be a 40- or 64-character well-formed 
** artifact hash.  Find its rid.
*/
int fast_uuid_to_rid(const char *zUuid){
  static Stmt q;
  int rid;
  db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid");
  db_bind_text(&q, ":uuid", zUuid);
  if( db_step(&q)==SQLITE_ROW ){
................................................................................
**
** If the UUID is not found and phantomize is 1 or 2, then attempt to
** create a phantom record.  A private phantom is created for 2 and
** a public phantom is created for 1.
*/
int uuid_to_rid(const char *zUuid, int phantomize){
  int rid, sz;
  char z[HNAME_MAX+1];

  sz = strlen(zUuid);
  if( !hname_validate(zUuid, sz) ){
    return 0;  /* Not a valid hash */
  }
  memcpy(z, zUuid, sz+1);
  canonical16(z, sz);
  rid = fast_uuid_to_rid(z);
  if( rid==0 && phantomize ){
    rid = content_new(zUuid, phantomize-1);
  }

Changes to src/wiki.c.

1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
** timestamp. Returns 0 if there is no such item and -1 if the details
** are ambiguous and could refer to multiple items.
*/
int wiki_technote_to_rid(const char *zETime) {
  int rid=0;                    /* Artifact ID of the tech note */
  int nETime = strlen(zETime);
  Stmt q;
  if( nETime>=4 && hname_validate(zETime, nETime)!=HNAME_NONE ){
    char zUuid[HNAME_LEN_MAX+1];
    memcpy(zUuid, zETime, nETime+1);
    canonical16(zUuid, nETime);
    db_prepare(&q,
      "SELECT e.objid"
      "  FROM event e, tag t"
      " WHERE e.type='e' AND e.tagid IS NOT NULL AND t.tagid=e.tagid"
      "   AND t.tagname GLOB 'event-%q*'",







|
|







1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
** timestamp. Returns 0 if there is no such item and -1 if the details
** are ambiguous and could refer to multiple items.
*/
int wiki_technote_to_rid(const char *zETime) {
  int rid=0;                    /* Artifact ID of the tech note */
  int nETime = strlen(zETime);
  Stmt q;
  if( nETime>=4 && hname_validate(zETime, nETime) ){
    char zUuid[HNAME_MAX+1];
    memcpy(zUuid, zETime, nETime+1);
    canonical16(zUuid, nETime);
    db_prepare(&q,
      "SELECT e.objid"
      "  FROM event e, tag t"
      " WHERE e.type='e' AND e.tagid IS NOT NULL AND t.tagid=e.tagid"
      "   AND t.tagname GLOB 'event-%q*'",

Changes to src/wikiformat.c.

1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079

/*
** If the input string corresponds to an existing baseline,
** return true.
*/
static int is_valid_uuid(const char *z){
  int n = strlen(z);
  if( n<4 || n>HNAME_LEN_MAX ) return 0;
  if( !validate16(z, n) ) return 0;
  return 1;
}

/*
** Return TRUE if a UUID corresponds to an artifact in this
** repository.







|







1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079

/*
** If the input string corresponds to an existing baseline,
** return true.
*/
static int is_valid_uuid(const char *z){
  int n = strlen(z);
  if( n<4 || n>HNAME_MAX ) return 0;
  if( !validate16(z, n) ) return 0;
  return 1;
}

/*
** Return TRUE if a UUID corresponds to an artifact in this
** repository.