Diff
Not logged in

Differences From:

File src/info.c part of check-in [5cf1206dfa] - Separate the new /doc method out into its own source file. by drh on 2008-05-15 20:18:32. Also file src/info.c part of check-in [6458f020fc] - Change the spelling of "descendant" to use the -ant suffix everywhere, including in the filename "descendant.c". The adjective form can be spelled either -ant or -ent, but the noun form requires -ant, or so says American Heritage. by drh on 2008-05-14 02:03:45. [view]

To:

File src/info.c part of check-in [7351b6346d] - Add the "/doc" method on the server. by drh on 2008-05-15 16:58:42. [view]

@@ -790,8 +790,195 @@
   @ %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