godoc: support for title and subtitle headers when serving .html docs
and use it to show version (date) of go spec

Fixes #68.

R=rsc
CC=golang-dev, r
https://golang.org/cl/848042
diff --git a/doc/go_spec.html b/doc/go_spec.html
index 003bbdc..84480f6 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,4 +1,5 @@
-<!-- The Go Programming Language Specification -->
+<!-- title The Go Programming Language Specification -->
+<!-- subtitle Version of March 25, 2010 -->
 
 <!--
 Todo
diff --git a/doc/style.css b/doc/style.css
index 597e70b..38cf68d 100644
--- a/doc/style.css
+++ b/doc/style.css
@@ -198,6 +198,11 @@
   background-color: #ffffa0;
 }
 
+span.subtitle {
+  font-weight: bold;
+  font-size: medium;
+}
+
 /* same style as for gettingStarted */
 #menu {
   margin-top: 1.5em;
diff --git a/lib/godoc/godoc.html b/lib/godoc/godoc.html
index 99cd55e..dd21799 100644
--- a/lib/godoc/godoc.html
+++ b/lib/godoc/godoc.html
@@ -133,11 +133,18 @@
     </div>
   {.end}
 
-  <h1 id="generatedHeader">{Title|html-esc}</h1>
+  {.section Title}
+    <h1 id="generatedHeader">{@|html-esc}</h1>
+  {.end}
+  {.section Subtitle}
+    <span class="subtitle">{@|html-esc}</span>
+  {.end}
 
+  <p>
   <!-- The Table of Contents is automatically inserted in this <div>.
        Do not delete this <div>. -->
   <div id="nav"></div>
+  </p>
 
   <!-- Content is HTML-escaped elsewhere -->
   {Content}
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index 62265cf..f302f8c 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -19,6 +19,7 @@
 	"log"
 	"os"
 	pathutil "path"
+	"regexp"
 	"runtime"
 	"strings"
 	"sync"
@@ -874,9 +875,10 @@
 // ----------------------------------------------------------------------------
 // Generic HTML wrapper
 
-func servePage(c *http.Conn, title, query string, content []byte) {
+func servePage(c *http.Conn, title, subtitle, query string, content []byte) {
 	type Data struct {
 		Title     string
+		Subtitle  string
 		PkgRoots  []string
 		Timestamp uint64 // int64 to be compatible with os.Dir.Mtime_ns
 		Query     string
@@ -888,6 +890,7 @@
 	_, ts := fsTree.get()
 	d := Data{
 		Title:     title,
+		Subtitle:  subtitle,
 		PkgRoots:  fsMap.PrefixList(),
 		Timestamp: uint64(ts) * 1e9, // timestamp in ns
 		Query:     query,
@@ -912,16 +915,16 @@
 // Files
 
 var (
-	tagBegin = []byte("<!--")
-	tagEnd   = []byte("-->")
+	titleRx        = regexp.MustCompile(`<!-- title ([^\-]*)-->`)
+	subtitleRx     = regexp.MustCompile(`<!-- subtitle ([^\-]*)-->`)
+	firstCommentRx = regexp.MustCompile(`<!--([^\-]*)-->`)
 )
 
-// commentText returns the text of the first HTML comment in src.
-func commentText(src []byte) (text string) {
-	i := bytes.Index(src, tagBegin)
-	j := bytes.Index(src, tagEnd)
-	if i >= 0 && j >= i+len(tagBegin) {
-		text = string(bytes.TrimSpace(src[i+len(tagBegin) : j]))
+
+func extractString(src []byte, rx *regexp.Regexp) (s string) {
+	m := rx.Execute(src)
+	if len(m) >= 4 {
+		s = strings.TrimSpace(string(src[m[2]:m[3]]))
 	}
 	return
 }
@@ -950,8 +953,15 @@
 		src = buf.Bytes()
 	}
 
-	title := commentText(src)
-	servePage(c, title, "", src)
+	// get title and subtitle, if any
+	title := extractString(src, titleRx)
+	if title == "" {
+		// no title found; try first comment for backward-compatibility
+		title = extractString(src, firstCommentRx)
+	}
+	subtitle := extractString(src, subtitleRx)
+
+	servePage(c, title, subtitle, "", src)
 }
 
 
@@ -983,7 +993,7 @@
 	info := &SourceInfo{buf.Bytes(), styler.mapping()}
 
 	contents := applyTemplate(sourceHTML, "sourceHTML", info)
-	servePage(c, "Source file "+relpath, "", contents)
+	servePage(c, "Source file "+relpath, "", "", contents)
 }
 
 
@@ -1056,7 +1066,7 @@
 	template.HTMLEscape(&buf, src)
 	fmt.Fprintln(&buf, "</pre>")
 
-	servePage(c, "Text file "+relpath, "", buf.Bytes())
+	servePage(c, "Text file "+relpath, "", "", buf.Bytes())
 }
 
 
@@ -1079,7 +1089,7 @@
 	}
 
 	contents := applyTemplate(dirlistHTML, "dirlistHTML", list)
-	servePage(c, "Directory "+relpath, "", contents)
+	servePage(c, "Directory "+relpath, "", "", contents)
 }
 
 
@@ -1326,7 +1336,7 @@
 	}
 
 	contents := applyTemplate(packageHTML, "packageHTML", info)
-	servePage(c, title, "", contents)
+	servePage(c, title, "", "", contents)
 }
 
 
@@ -1373,7 +1383,7 @@
 	}
 
 	contents := applyTemplate(searchHTML, "searchHTML", result)
-	servePage(c, title, query, contents)
+	servePage(c, title, "", query, contents)
 }
 
 
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 0ede0dc..7a9279a 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -66,7 +66,7 @@
 
 func serveError(c *http.Conn, r *http.Request, relpath string, err os.Error) {
 	contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path!
-	servePage(c, "File "+relpath, "", contents)
+	servePage(c, "File "+relpath, "", "", contents)
 }