Check-in [7351b6346d]
Not logged in
Overview

SHA1 Hash:7351b6346d153f984d38ece87b52c222346ed3be
Date: 2008-05-15 16:58:42
User: drh
Comment:Add the "/doc" method on the server.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/db.c from [0644da6326] to [3bee800775].

@@ -111,10 +111,11 @@
     sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
   }
   nBegin++;
 }
 void db_end_transaction(int rollbackFlag){
+  if( nBegin<=0 ) return;
   if( rollbackFlag ) doRollback = 1;
   nBegin--;
   if( nBegin==0 ){
     int i;
     for(i=0; doRollback==0 && i<nCommitHook; i++){

Modified src/info.c from [60d6be822b] to [d6911c08c2].

@@ -789,10 +789,197 @@
   content_get(rid, &content);
   @ %h(blob_str(&content))
   @ </pre></blockquote>
   blob_reset(&content);
   style_footer();
+}
+
+/*
+** Guess the mime-type of a document based on its name.
+*/
+const char *mimetype_from_name(const char *zName){
+  const char *z;
+  int i;
+  char zSuffix[20];
+  static const struct {
+    const char *zSuffix;
+    const char *zMimetype;
+  } aMime[] = {
+    { "html",     "text/html"                 },
+    { "htm",      "text/html"                 },
+    { "wiki",     "application/x-fossil-wiki" },
+    { "txt",      "text/plain"                },
+    { "jpg",      "image/jpeg"                },
+    { "jpeg",     "image/jpeg"                },
+    { "gif",      "image/gif"                 },
+    { "png",      "image/png"                 },
+    { "css",      "text/css"                  },
+  };
+
+  z = zName;
+  for(i=0; zName[i]; i++){
+    if( zName[i]=='.' ) z = &zName[i+1];
+  }
+  i = strlen(z);
+  if( i<sizeof(zSuffix)-1 ){
+    strcpy(zSuffix, z);
+    for(i=0; zSuffix[i]; i++) zSuffix[i] = tolower(zSuffix[i]);
+    for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){
+      if( strcmp(zSuffix, aMime[i].zSuffix)==0 ){
+        return aMime[i].zMimetype;
+      }
+    }
+  }
+  return "application/x-fossil-artifact";
+}
+
+/*
+** WEBPAGE: doc
+** URL: /doc?name=BASELINE/PATH
+**
+** BASELINE can be either a baseline uuid prefix or magic words "tip"
+** to me the most recently checked in baseline or "ckout" to mean the
+** content of the local checkout, if any.  PATH is the relative pathname
+** of some file.  This method returns the file content.
+**
+** If PATH matches the patterns *.wiki or *.txt then formatting content
+** is added before returning the file.  For all other names, the content
+** is returned straight without any interpretation or processing.
+*/
+void doc_page(void){
+  const char *zName;                /* Argument to the /doc page */
+  const char *zMime;                /* Document MIME type */
+  int vid = 0;                      /* Artifact of baseline */
+  int rid = 0;                      /* Artifact of file */
+  int i;                            /* Loop counter */
+  Blob filebody;                    /* Content of the documentation file */
+  char zBaseline[UUID_SIZE+1];      /* Baseline UUID */
+
+  login_check_credentials();
+  if( !g.okRead ){ login_needed(); return; }
+  zName = PD("name", "tip/index.wiki");
+  for(i=0; zName[i] && zName[i]!='/'; i++){}
+  if( zName[i]==0 || i>UUID_SIZE ){
+    goto doc_not_found;
+  }
+  memcpy(zBaseline, zName, i);
+  zBaseline[i] = 0;
+  zName += i;
+  while( zName[0]=='/' ){ zName++; }
+  if( !file_is_simple_pathname(zName) ){
+    goto doc_not_found;
+  }
+  if( strcmp(zBaseline,"ckout")==0 ){
+    /* Read from the local checkout */
+    char *zFullpath;
+    db_must_be_within_tree();
+    zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
+    if( !file_isfile(zFullpath) ){
+      goto doc_not_found;
+    }
+    if( blob_read_from_file(&filebody, zFullpath)<0 ){
+      goto doc_not_found;
+    }
+  }else{
+    db_begin_transaction();
+    if( strcmp(zBaseline,"tip")==0 ){
+      vid = db_int(0, "SELECT objid FROM event WHERE type='ci'"
+                      " ORDER BY mtime DESC LIMIT 1");
+    }else{
+      vid = name_to_rid(zBaseline);
+    }
+
+    /* Create the baseline cache if it does not already exist */
+    db_multi_exec(
+      "CREATE TABLE IF NOT EXISTS vcache(\n"
+      "  vid INTEGER,         -- baseline ID\n"
+      "  fname TEXT,          -- filename\n"
+      "  rid INTEGER,         -- artifact ID\n"
+      "  UNIQUE(vid,fname,rid)\n"
+      ")"
+    );
+
+    /* Check to see if the documentation file artifact ID is contained
+    ** in the baseline cache */
+    rid = db_int(0, "SELECT rid FROM vcache"
+                    " WHERE vid=%d AND fname=%Q", vid, zName);
+    if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
+      goto doc_not_found;
+    }
+
+    if( rid==0 ){
+      Stmt s;
+      Blob baseline;
+      Manifest m;
+
+      /* Add the vid baseline to the cache */
+      if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){
+        db_multi_exec("DELETE FROM vcache");
+      }
+      if( content_get(vid, &baseline)==0 ){
+        goto doc_not_found;
+      }
+      if( manifest_parse(&m, &baseline)==0 || m.type!=CFTYPE_MANIFEST ){
+        goto doc_not_found;
+      }
+      db_prepare(&s,
+        "INSERT INTO vcache(vid,fname,rid)"
+        " SELECT %d, :fname, rid FROM blob"
+        "  WHERE uuid=:uuid",
+        vid
+      );
+      for(i=0; i<m.nFile; i++){
+        db_bind_text(&s, ":fname", m.aFile[i].zName);
+        db_bind_text(&s, ":uuid", m.aFile[i].zUuid);
+        db_step(&s);
+        db_reset(&s);
+      }
+      db_finalize(&s);
+      manifest_clear(&m);
+
+      /* Try again to find the file */
+      rid = db_int(0, "SELECT rid FROM vcache"
+                      " WHERE vid=%d AND fname=%Q", vid, zName);
+    }
+    if( rid==0 ){
+      goto doc_not_found;
+    }
+
+    /* Get the file content */
+    if( content_get(rid, &filebody)==0 ){
+      goto doc_not_found;
+    }
+    db_end_transaction(0);
+  }
+
+  /* The file is now contained in the filebody blob.  Deliver the
+  ** file to the user
+  */
+  zMime = mimetype_from_name(zName);
+  if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
+    style_header("Documentation");
+    wiki_convert(&filebody, 0, 0);
+    style_footer();
+  }else if( strcmp(zMime, "text/plain")==0 ){
+    style_header("Documentation");
+    @ <blockquote><pre>
+    @ %h(blob_str(&filebody))
+    @ </pre></blockquote>
+    style_footer();
+  }else{
+    cgi_set_content_type(zMime);
+    cgi_set_content(&filebody);
+  }
+  return;
+
+doc_not_found:
+  /* Jump here when unable to locate the document */
+  db_end_transaction(0);
+  style_header("Document Not Found");
+  @ <p>No such document: %h(PD("name","tip/index.wiki"))</p>
+  style_footer();
+  return;
 }
 
 /*
 ** WEBPAGE: info
 ** URL: info/UUID

Modified src/wikiformat.c from [30a93b7778] to [2f33e8ac29].

@@ -151,12 +151,12 @@
 #define MARKUP_H3               18
 #define MARKUP_H4               19
 #define MARKUP_H5               20
 #define MARKUP_H6               21
 #define MARKUP_HR               22
-#define MARKUP_IMG              23
-#define MARKUP_I                24
+#define MARKUP_I                23
+#define MARKUP_IMG              24
 #define MARKUP_KBD              25
 #define MARKUP_LI               26
 #define MARKUP_NOBR             27
 #define MARKUP_NOWIKI           28
 #define MARKUP_OL               29
@@ -233,14 +233,14 @@
  { "h4",            MARKUP_H4,           MUTYPE_BLOCK,         ATTR_ALIGN  },
  { "h5",            MARKUP_H5,           MUTYPE_BLOCK,         ATTR_ALIGN  },
  { "h6",            MARKUP_H6,           MUTYPE_BLOCK,         ATTR_ALIGN  },
  { "hr",            MARKUP_HR,           MUTYPE_SINGLE,
                     ATTR_ALIGN|ATTR_COLOR|ATTR_SIZE|ATTR_WIDTH  },
+ { "i",             MARKUP_I,            MUTYPE_FONT,          0  },
  { "img",           MARKUP_IMG,          MUTYPE_SINGLE,
                     ATTR_ALIGN|ATTR_ALT|ATTR_BORDER|ATTR_HEIGHT|
                     ATTR_HSPACE|ATTR_SRC|ATTR_VSPACE|ATTR_WIDTH  },
- { "i",             MARKUP_I,            MUTYPE_FONT,          0  },
  { "kbd",           MARKUP_KBD,          MUTYPE_FONT,          0  },
  { "li",            MARKUP_LI,           MUTYPE_LI,
                     ATTR_TYPE|ATTR_VALUE  },
  { "nobr",          MARKUP_NOBR,         MUTYPE_FONT,          0  },
  { "nowiki",        MARKUP_NOWIKI,       MUTYPE_SPECIAL,       0  },