Diff
Not logged in

Differences From:

File src/manifest.c part of check-in [e63a9fd9d0] - Fixed many uninitialized variable warnings and some potential bug found via -Wall -Werror on gcc. by jnc on 2007-09-25 21:21:35. Also file src/manifest.c part of check-in [92291035fe] - Merged the compiler warning fixes into mainstream by jnc on 2007-09-25 21:28:30. [view]

To:

File src/manifest.c part of check-in [2ab2db0bd3] - The control file parser now reads ticket changes and wiki pages. by drh on 2007-10-05 13:03:22. [view]

@@ -23,9 +23,9 @@
 **
 ** This file contains code used to cross link control files and
 ** manifests.  The file is named "manifest.c" because it was
 ** original only used to parse manifests.  Then later clusters
-** and control files were added.
+** and control files and wiki pages and tickets were added.
 */
 #include "config.h"
 #include "manifest.h"
 #include <assert.h>
@@ -36,20 +36,33 @@
 */
 #define CFTYPE_MANIFEST   1
 #define CFTYPE_CLUSTER    2
 #define CFTYPE_CONTROL    3
+#define CFTYPE_WIKI       4
+#define CFTYPE_TICKET     5
+
+/*
+** Mode parameter values
+*/
+#define CFMODE_READ       1
+#define CFMODE_APPEND     2
+#define CFMODE_WRITE      3
 
 /*
 ** A parsed manifest or cluster.
 */
 struct Manifest {
   Blob content;         /* The original content blob */
   int type;             /* Type of file */
+  int mode;             /* Access mode */
   char *zComment;       /* Decoded comment */
   char zUuid[UUID_SIZE+1];  /* Self UUID */
   double rDate;         /* Time in the "D" line */
   char *zUser;          /* Name of the user */
   char *zRepoCksum;     /* MD5 checksum of the baseline content */
+  char *zWiki;          /* Text of the wiki page */
+  char *zWikiTitle;     /* Name of the wiki page */
+  char *zTicketUuid;    /* UUID for a ticket */
   int nFile;            /* Number of F lines */
   int nFileAlloc;       /* Slots allocated in aFile[] */
   struct {
     char *zName;           /* Name of a file */
@@ -67,8 +80,21 @@
     char *zName;           /* Name of the tag */
     char *zUuid;           /* UUID that the tag is applied to */
     char *zValue;          /* Value if the tag is really a property */
   } *aTag;
+  int nField;           /* Number of J lines */
+  int nFieldAlloc;      /* Slots allocated in aField[] */
+  struct {
+    char *zName;           /* Key or field name */
+    char *zValue;          /* Value of the field */
+  } *aField;
+  int nAttach;          /* Number of A lines */
+  int nAttachAlloc;     /* Slots allocated in aAttach[] */
+  struct {
+    char *zUuid;           /* UUID of the attachment */
+    char *zName;           /* Name of the attachment */
+    char *zDesc;           /* Description of the attachment */
+  } *aAttach;
 };
 #endif
 
 
@@ -79,27 +105,44 @@
   blob_reset(&p->content);
   free(p->aFile);
   free(p->azParent);
   free(p->azCChild);
+  free(p->aTag);
+  free(p->aField);
+  free(p->aAttach);
   memset(p, 0, sizeof(*p));
 }
 
 /*
-** Parse a manifest blob into a Manifest object.  The Manifest
-** object takes over the input blob and will free it when the
+** Parse a blob into a Manifest object.  The Manifest object
+** takes over the input blob and will free it when the
 ** Manifest object is freed.  Zeros are inserted into the blob
 ** as string terminators so that blob should not be used again.
 **
-** Return TRUE if the content really is a manifest.  Return FALSE
-** if there are syntax errors.
+** Return TRUE if the content really is a control file of some
+** kind.  Return FALSE if there are syntax errors.
+**
+** This routine is strict about the format of a control file.
+** The format must match exactly or else it is rejected.  This
+** rule minimizes the risk that a content file will be mistaken
+** for a control file simply because they look the same.
 **
 ** The pContent is reset.  If TRUE is returned, then pContent will
 ** be reset when the Manifest object is cleared.  If FALSE is
 ** returned then the Manifest object is cleared automatically
 ** and pContent is reset before the return.
+**
+** The entire file can be PGP clear-signed.  The signature is ignored.
+** The file consists of zero or more cards, one card per line.
+** (Except: the content of the W card can extend of multiple lines.)
+** Each card is divided into tokens by a single space character.
+** The first token is a single upper-case letter which is the card type.
+** The card type determines the other parameters to the card.
+** Cards must occur in lexicographical order.
 */
 int manifest_parse(Manifest *p, Blob *pContent){
   int seenHeader = 0;
+  int seenZ = 0;
   int i, lineNo=0;
   Blob line, token, a1, a2, a3;
   Blob selfuuid;
   char cPrevType = 0;
@@ -137,8 +180,46 @@
     seenHeader = 1;
     if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
     switch( z[0] ){
       /*
+      **     A <uuid> <filename> <description>
+      **
+      ** Identifies an attachment to either a wiki page or a ticket.
+      ** <uuid> is the artifact that is the attachment.
+      */
+      case 'A': {
+        char *zName, *zUuid, *zDesc;
+        md5sum_step_text(blob_buffer(&line), blob_size(&line));
+        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a3)==0 ) goto manifest_syntax_error;
+        zUuid = blob_terminate(&a1);
+        zName = blob_terminate(&a2);
+        zDesc = blob_terminate(&a3);
+        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
+        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
+        defossilize(zName);
+        if( !file_is_simple_pathname(zName) ){
+          goto manifest_syntax_error;
+        }
+        defossilize(zDesc);
+        if( p->nAttach>=p->nAttachAlloc ){
+          p->nAttachAlloc = p->nAttachAlloc*2 + 10;
+          p->aAttach = realloc(p->aAttach,
+                               p->nAttachAlloc*sizeof(p->aAttach[0]) );
+          if( p->aAttach==0 ) fossil_panic("out of memory");
+        }
+        i = p->nAttach++;
+        p->aAttach[i].zUuid = zUuid;
+        p->aAttach[i].zName = zName;
+        p->aAttach[i].zDesc = zDesc;
+        if( i>0 && strcmp(p->aAttach[i-1].zUuid, zUuid)>=0 ){
+          goto manifest_syntax_error;
+        }
+        break;
+      }
+
+      /*
       **     C <comment>
       **
       ** Comment text is fossil-encoded.  There may be no more than
       ** one C line.  C lines are required for manifests and are
@@ -168,8 +249,31 @@
         if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
         if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
         zDate = blob_terminate(&a1);
         p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
+        break;
+      }
+
+      /*
+      **     E <mode>
+      **
+      ** Access mode.  <mode> can be one of "read", "append",
+      ** or "write".
+      */
+      case 'E': {
+        md5sum_step_text(blob_buffer(&line), blob_size(&line));
+        if( p->mode!=0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
+        if( blob_eq(&a1, "write") ){
+          p->mode = CFMODE_WRITE;
+        }else if( blob_eq(&a1, "append") ){
+          p->mode = CFMODE_APPEND;
+        }else if( blob_eq(&a1, "read") ){
+          p->mode = CFMODE_READ;
+        }else{
+          goto manifest_syntax_error;
+        }
         break;
       }
 
       /*
@@ -207,8 +311,72 @@
         break;
       }
 
       /*
+      **     J <name> <value>
+      **
+      ** Specifies a name value pair for ticket.
+      */
+      case 'J': {
+        char *zName, *zValue;
+        md5sum_step_text(blob_buffer(&line), blob_size(&line));
+        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
+        zName = blob_terminate(&a1);
+        zValue = blob_terminate(&a2);
+        defossilize(zValue);
+        if( p->nField>=p->nFieldAlloc ){
+          p->nFieldAlloc = p->nFieldAlloc*2 + 10;
+          p->aField = realloc(p->aField,
+                               p->nFieldAlloc*sizeof(p->aField[0]) );
+          if( p->aField==0 ) fossil_panic("out of memory");
+        }
+        i = p->nField++;
+        p->aField[i].zName = zName;
+        p->aField[i].zValue = zValue;
+        if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){
+          goto manifest_syntax_error;
+        }
+        break;
+      }
+
+
+      /*
+      **    K <uuid>
+      **
+      ** A K-line gives the UUID for the ticket which this control file
+      ** is amending.
+      */
+      case 'K': {
+        char *zUuid;
+        md5sum_step_text(blob_buffer(&line), blob_size(&line));
+        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
+        zUuid = blob_terminate(&a1);
+        if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
+        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
+        if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
+        p->zTicketUuid = zUuid;
+        break;
+      }
+
+      /*
+      **     L <wikitite>
+      **
+      ** The wiki page title is fossil-encoded.  There may be no more than
+      ** one L line.
+      */
+      case 'L': {
+        md5sum_step_text(blob_buffer(&line), blob_size(&line));
+        if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
+        p->zWikiTitle = blob_terminate(&a1);
+        defossilize(p->zWikiTitle);
+        break;
+      }
+
+      /*
       **    M <uuid>
       **
       ** An M-line identifies another artifact by its UUID.  M-lines
       ** occur in clusters only.
@@ -230,8 +398,50 @@
         p->azCChild[i] = zUuid;
         if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){
           goto manifest_syntax_error;
         }
+        break;
+      }
+
+      /*
+      **     P <uuid> ...
+      **
+      ** Specify one or more other artifacts where are the parents of
+      ** this artifact.  The first parent is the primary parent.  All
+      ** others are parents by merge.
+      */
+      case 'P': {
+        md5sum_step_text(blob_buffer(&line), blob_size(&line));
+        while( blob_token(&line, &a1) ){
+          char *zUuid;
+          if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
+          zUuid = blob_terminate(&a1);
+          if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
+          if( p->nParent>=p->nParentAlloc ){
+            p->nParentAlloc = p->nParentAlloc*2 + 5;
+            p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));
+            if( p->azParent==0 ) fossil_panic("out of memory");
+          }
+          i = p->nParent++;
+          p->azParent[i] = zUuid;
+        }
+        break;
+      }
+
+      /*
+      **     R <md5sum>
+      **
+      ** Specify the MD5 checksum of the entire baseline in a
+      ** manifest.
+      */
+      case 'R': {
+        md5sum_step_text(blob_buffer(&line), blob_size(&line));
+        if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
+        if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
+        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
+        p->zRepoCksum = blob_terminate(&a1);
+        if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
         break;
       }
 
       /*
@@ -312,55 +522,44 @@
         break;
       }
 
       /*
-      **     R <md5sum>
+      **     W <size>
       **
-      ** Specify the MD5 checksum of the entire baseline in a
-      ** manifest.
+      ** The next <size> bytes of the file contain the text of the wiki
+      ** page.  There is always an extra \n before the start of the next
+      ** record.
       */
-      case 'R': {
-        md5sum_step_text(blob_buffer(&line), blob_size(&line));
-        if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
+      case 'W': {
+        int size;
+        Blob wiki;
+        md5sum_step_text(blob_buffer(&line), blob_size(&line));
         if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
         if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
-        if( blob_size(&a1)!=32 ) goto manifest_syntax_error;
-        p->zRepoCksum = blob_terminate(&a1);
-        if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
+        if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error;
+        if( size<0 ) goto manifest_syntax_error;
+        if( p->zWiki!=0 ) goto manifest_syntax_error;
+        blob_zero(&wiki);
+        if( blob_extract(pContent, size+1, &wiki)!=size+1 ){
+          goto manifest_syntax_error;
+        }
+        p->zWiki = blob_buffer(&wiki);
+        if( p->zWiki[size]!='\n' ) goto manifest_syntax_error;
+        p->zWiki[size] = 0;
         break;
       }
 
-      /*
-      **     P <uuid> ...
-      **
-      ** Specify one or more other artifacts where are the parents of
-      ** this artifact.  The first parent is the primary parent.  All
-      ** others are parents by merge.
-      */
-      case 'P': {
-        md5sum_step_text(blob_buffer(&line), blob_size(&line));
-        while( blob_token(&line, &a1) ){
-          char *zUuid;
-          if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error;
-          zUuid = blob_terminate(&a1);
-          if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
-          if( p->nParent>=p->nParentAlloc ){
-            p->nParentAlloc = p->nParentAlloc*2 + 5;
-            p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*));
-            if( p->azParent==0 ) fossil_panic("out of memory");
-          }
-          i = p->nParent++;
-          p->azParent[i] = zUuid;
-        }
-        break;
-      }
 
       /*
       **     Z <md5sum>
       **
       ** MD5 checksum on this control file.  The checksum is over all
       ** lines (other than PGP-signature lines) prior to the current
       ** line.  This must be the last record.
+      **
+      ** This card is required for all control file types except for
+      ** Manifest.  It is not required for manifest only for historical
+      ** compatibility reasons.
       */
       case 'Z': {
         int rc;
         Blob hash;
@@ -371,8 +570,9 @@
         md5sum_finish(&hash);
         rc = blob_compare(&hash, &a1);
         blob_reset(&hash);
         if( rc!=0 ) goto manifest_syntax_error;
+        seenZ = 1;
         break;
       }
       default: {
         goto manifest_syntax_error;
@@ -383,8 +583,14 @@
 
   if( p->nFile>0 ){
     if( p->nCChild>0 ) goto manifest_syntax_error;
     if( p->rDate==0.0 ) goto manifest_syntax_error;
+    if( p->nField>0 ) goto manifest_syntax_error;
+    if( p->zTicketUuid ) goto manifest_syntax_error;
+    if( p->nAttach>0 ) goto manifest_syntax_error;
+    if( p->zWiki ) goto manifest_syntax_error;
+    if( p->zWikiTitle ) goto manifest_syntax_error;
+    if( p->zTicketUuid ) goto manifest_syntax_error;
     p->type = CFTYPE_MANIFEST;
   }else if( p->nCChild>0 ){
     if( p->rDate>0.0 ) goto manifest_syntax_error;
     if( p->zComment!=0 ) goto manifest_syntax_error;
@@ -391,13 +597,46 @@
     if( p->zUser!=0 ) goto manifest_syntax_error;
     if( p->nTag>0 ) goto manifest_syntax_error;
     if( p->nParent>0 ) goto manifest_syntax_error;
     if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
+    if( p->nField>0 ) goto manifest_syntax_error;
+    if( p->zTicketUuid ) goto manifest_syntax_error;
+    if( p->nAttach>0 ) goto manifest_syntax_error;
+    if( p->zWiki ) goto manifest_syntax_error;
+    if( p->zWikiTitle ) goto manifest_syntax_error;
+    if( !seenZ ) goto manifest_syntax_error;
     p->type = CFTYPE_CLUSTER;
+  }else if( p->nField>0 ){
+    if( p->rDate==0.0 ) goto manifest_syntax_error;
+    if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
+    if( p->zWiki ) goto manifest_syntax_error;
+    if( p->zWikiTitle ) goto manifest_syntax_error;
+    if( p->nCChild>0 ) goto manifest_syntax_error;
+    if( p->nTag>0 ) goto manifest_syntax_error;
+    if( p->zTicketUuid==0 ) goto manifest_syntax_error;
+    if( p->zUser==0 ) goto manifest_syntax_error;
+    if( !seenZ ) goto manifest_syntax_error;
+    p->type = CFTYPE_TICKET;
+  }else if( p->zWiki!=0 ){
+    if( p->rDate==0.0 ) goto manifest_syntax_error;
+    if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
+    if( p->nCChild>0 ) goto manifest_syntax_error;
+    if( p->nTag>0 ) goto manifest_syntax_error;
+    if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
+    if( p->zUser==0 ) goto manifest_syntax_error;
+    if( p->zWikiTitle==0 ) goto manifest_syntax_error;
+    if( !seenZ ) goto manifest_syntax_error;
+    p->type = CFTYPE_WIKI;
   }else if( p->nTag>0 ){
     if( p->rDate<=0.0 ) goto manifest_syntax_error;
     if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
     if( p->nParent>0 ) goto manifest_syntax_error;
+    if( p->nAttach>0 ) goto manifest_syntax_error;
+    if( p->nField>0 ) goto manifest_syntax_error;
+    if( p->zWiki ) goto manifest_syntax_error;
+    if( p->zWikiTitle ) goto manifest_syntax_error;
+    if( p->zTicketUuid ) goto manifest_syntax_error;
+    if( !seenZ ) goto manifest_syntax_error;
     p->type = CFTYPE_CONTROL;
   }else{
     goto manifest_syntax_error;
   }
@@ -582,10 +821,22 @@
                  rid, m.rDate, tid);
     }
     if( parentid ){
       tag_propagate_all(parentid);
+    }
+  }
+  if( m.type==CFTYPE_WIKI ){
+    char *zTag = mprintf("wiki-%s", m.zWikiTitle);
+    int tagid = tag_findid(zTag, 1);
+    int prior;
+    tag_insert(zTag, 1, 0, rid, m.rDate, rid);
+    free(zTag);
+    prior = db_int(0, "SELECT rid FROM tagxref WHERE tagid=%d"
+                      " ORDER BY mtime DESC LIMIT 1 OFFSET 1", tagid);
+    if( prior ){
+      content_deltify(prior, rid, 0);
     }
   }
   db_end_transaction(0);
   manifest_clear(&m);
   return 1;
 }