]> Cypherpunks repositories - gostls13.git/commitdiff
- better support for text files: show them nicely formatted
authorRobert Griesemer <gri@golang.org>
Sun, 8 Nov 2009 08:40:43 +0000 (00:40 -0800)
committerRobert Griesemer <gri@golang.org>
Sun, 8 Nov 2009 08:40:43 +0000 (00:40 -0800)
  instead of serving them raw
- path-related cleanups

R=rsc
http://go/go-review/1026021

src/cmd/godoc/godoc.go

index 48abcd53c0aca992763f9ecd533819372bd60de9..16a4982cf461849adc0279c08c25e79a916517b0 100644 (file)
@@ -23,6 +23,7 @@ import (
        "sync";
        "template";
        "time";
+       "utf8";
 )
 
 
@@ -626,9 +627,8 @@ func commentText(src []byte) (text string) {
 }
 
 
-func serveHtmlDoc(c *http.Conn, r *http.Request, filename string) {
+func serveHtmlDoc(c *http.Conn, r *http.Request, path string) {
        // get HTML body contents
-       path := pathutil.Join(goroot, filename);
        src, err := io.ReadFile(path);
        if err != nil {
                log.Stderrf("%v", err);
@@ -658,8 +658,7 @@ func serveParseErrors(c *http.Conn, errors *parseErrors) {
 }
 
 
-func serveGoSource(c *http.Conn, filename string, styler printer.Styler) {
-       path := pathutil.Join(goroot, filename);
+func serveGoSource(c *http.Conn, r *http.Request, path string, styler printer.Styler) {
        prog, errors := parse(path, parser.ParseComments);
        if errors != nil {
                serveParseErrors(c, errors);
@@ -671,7 +670,7 @@ func serveGoSource(c *http.Conn, filename string, styler printer.Styler) {
        writeNode(&buf, prog, true, styler);
        fmt.Fprintln(&buf, "</pre>");
 
-       servePage(c, "Source file " + filename, "", buf.Bytes());
+       servePage(c, "Source file " + r.Url.Path, "", buf.Bytes());
 }
 
 
@@ -684,12 +683,72 @@ func redirect(c *http.Conn, r *http.Request) (redirected bool) {
 }
 
 
-func serveDirectory(c *http.Conn, r *http.Request) {
+// TODO(gri): Should have a mapping from extension to handler, eventually.
+
+// textExt[x] is true if the extension x indicates a text file, and false otherwise.
+var textExt = map[string]bool{
+       ".css": false,  // must be served raw
+       ".js": false,   // must be served raw
+}
+
+
+func isTextFile(path string) bool {
+       // if the extension is known, use it for decision making
+       if isText, found := textExt[pathutil.Ext(path)]; found {
+               return isText;
+       }
+
+       // the extension is not known; read an initial chunk of
+       // file and check if it looks like correct UTF-8; if it
+       // does, it's probably a text file
+       f, err := os.Open(path, os.O_RDONLY, 0);
+       if err != nil {
+               return false;
+       }
+
+       var buf [1024]byte;
+       n, err := f.Read(&buf);
+       if err != nil {
+               return false;
+       }
+
+       s := string(buf[0:n]);
+       n -= utf8.UTFMax;       // make sure there's enough bytes for a complete unicode char
+       for i, c := range s {
+               if i > n {
+                       break;
+               }
+               if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' {
+                       // decoding error or control character - not a text file
+                       return false;
+               }
+       }
+
+       // likely a text file
+       return true;
+}
+
+
+func serveTextFile(c *http.Conn, r *http.Request, path string) {
+       src, err := io.ReadFile(path);
+       if err != nil {
+               log.Stderrf("serveTextFile: %s", err);
+       }
+
+       var buf bytes.Buffer;
+       fmt.Fprintln(&buf, "<pre>");
+       template.HtmlEscape(&buf, src);
+       fmt.Fprintln(&buf, "</pre>");
+
+       servePage(c, "Text file " + path, "", buf.Bytes());
+}
+
+
+func serveDirectory(c *http.Conn, r *http.Request, path string) {
        if redirect(c, r) {
                return;
        }
 
-       path := pathutil.Join(".", r.Url.Path);
        list, err := io.ReadDir(path);
        if err != nil {
                http.NotFound(c, r);
@@ -708,37 +767,45 @@ func serveDirectory(c *http.Conn, r *http.Request) {
 var fileServer = http.FileServer(".", "")
 
 func serveFile(c *http.Conn, r *http.Request) {
-       path := r.Url.Path;
+       path := pathutil.Join(".", r.Url.Path);
 
        // pick off special cases and hand the rest to the standard file server
        switch ext := pathutil.Ext(path); {
-       case path == "/":
+       case r.Url.Path == "/":
                serveHtmlDoc(c, r, "doc/root.html");
+               return;
 
        case r.Url.Path == "/doc/root.html":
                // hide landing page from its real name
                http.NotFound(c, r);
+               return;
 
        case ext == ".html":
                serveHtmlDoc(c, r, path);
+               return;
 
        case ext == ".go":
-               serveGoSource(c, path, &Styler{highlight: r.FormValue("h")});
+               serveGoSource(c, r, path, &Styler{highlight: r.FormValue("h")});
+               return;
+       }
 
-       default:
-               dir, err := os.Lstat(pathutil.Join(".", path));
-               if err != nil {
-                       http.NotFound(c, r);
-                       return;
-               }
+       dir, err := os.Lstat(path);
+       if err != nil {
+               http.NotFound(c, r);
+               return;
+       }
 
-               if dir != nil && dir.IsDirectory() {
-                       serveDirectory(c, r);
-                       return;
-               }
+       if dir != nil && dir.IsDirectory() {
+               serveDirectory(c, r, path);
+               return;
+       }
 
-               fileServer.ServeHTTP(c, r);
+       if isTextFile(path) {
+               serveTextFile(c, r, path);
+               return;
        }
+
+       fileServer.ServeHTTP(c, r);
 }