bye-bye to old makehtml.
go can do the job just fine.

R=rsc
DELTA=290  (209 added, 13 deleted, 68 changed)
OCL=35699
CL=35699
diff --git a/doc/htmlgen.go b/doc/htmlgen.go
new file mode 100644
index 0000000..8d44fc0
--- /dev/null
+++ b/doc/htmlgen.go
@@ -0,0 +1,192 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Process plain text into HTML.
+//	- h2's are made from lines followed by a line "----\n"
+//	- tab-indented blocks become <pre> blocks
+//	- blank lines become <p> marks
+//	- "quoted strings" become <code>quoted strings</code>
+
+package main
+
+import (
+	"bufio";
+	"bytes";
+	"log";
+	"os";
+	"strings";
+)
+
+var (
+	lines = make([][]byte, 0, 10000);	// assume big enough
+	linebuf = make([]byte, 10000);		// assume big enough
+
+	empty = strings.Bytes("");
+	newline = strings.Bytes("\n");
+	tab = strings.Bytes("\t");
+	quote = strings.Bytes(`"`);
+
+	sectionMarker = strings.Bytes("----\n");
+	preStart = strings.Bytes("<pre>");
+	preEnd = strings.Bytes("</pre>\n");
+	pp = strings.Bytes("<p>\n");
+);
+
+func main() {
+	read();
+	headings();
+	paragraphs();
+	coalesce(preStart, foldPre);
+	coalesce(tab, foldTabs);
+	quotes();
+	write();
+}
+
+func read() {
+	b := bufio.NewReader(os.Stdin);
+	for {
+		line, err := b.ReadBytes('\n');
+		if err == os.EOF {
+			break;
+		}
+		if err != nil {
+			log.Exit(err)
+		}
+		n := len(lines);
+		lines = lines[0:n+1];
+		lines[n] = line;
+	}
+}
+
+func write() {
+	b := bufio.NewWriter(os.Stdout);
+	for _, line := range lines {
+		b.Write(expandTabs(line));
+	}
+	b.Flush();
+}
+
+// each time prefix is found on a line, call fold and replace
+// line with return value from fold.
+func coalesce(prefix []byte, fold func(i int) (n int, line []byte)) {
+	j := 0;	// output line number; goes up by one each loop
+	for i := 0; i < len(lines); {
+		if bytes.HasPrefix(lines[i], prefix) {
+			nlines, block := fold(i);
+			lines[j] = block;
+			i += nlines;
+		} else {
+			lines[j] = lines[i];
+			i++;
+		}
+		j++;
+	}
+	lines = lines[0:j];
+}
+
+// return the <pre> block as a single slice
+func foldPre(i int) (n int, line []byte) {
+	buf := new(bytes.Buffer);
+	for i < len(lines) {
+		buf.Write(lines[i]);
+		n++;
+		if bytes.Equal(lines[i], preEnd) {
+			break
+		}
+		i++;
+	}
+	return n, buf.Bytes();
+}
+
+// return the tab-indented block as a single <pre>-bounded slice
+func foldTabs(i int) (n int, line []byte) {
+	buf := new(bytes.Buffer);
+	buf.WriteString("<pre>\n");
+	for i < len(lines) {
+		if !bytes.HasPrefix(lines[i], tab) {
+			break;
+		}
+		buf.Write(lines[i]);
+		n++;
+		i++;
+	}
+	buf.WriteString("</pre>\n");
+	return n, buf.Bytes();
+}
+
+func headings() {
+	b := bufio.NewWriter(os.Stdout);
+	for i, l := range lines {
+		if i > 0 && bytes.Equal(l, sectionMarker) {
+			lines[i-1] = strings.Bytes("<h2>" + string(trim(lines[i-1])) + "</h2>\n");
+			lines[i] = empty;
+		}
+	}
+	b.Flush();
+}
+
+func paragraphs() {
+	for i, l := range lines {
+		if bytes.Equal(l, newline) {
+			lines[i] = pp;
+		}
+	}
+}
+
+func quotes() {
+	for i, l := range lines {
+		lines[i] = codeQuotes(l);
+	}
+}
+
+func codeQuotes(l []byte) []byte {
+	if bytes.HasPrefix(l, preStart) {
+		return l
+	}
+	n := bytes.Index(l, quote);
+	if n < 0 {
+		return l
+	}
+	buf := new(bytes.Buffer);
+	inQuote := false;
+	for _, c := range l {
+		if c == '"' {
+			if inQuote {
+				buf.WriteString("</code>")
+			} else {
+				buf.WriteString("<code>")
+			}
+			inQuote = !inQuote
+		} else {
+			buf.WriteByte(c)
+		}
+	}
+	return buf.Bytes();
+}
+
+// drop trailing newline
+func trim(l []byte) []byte {
+	n := len(l);
+	if n > 0 && l[n-1] == '\n' {
+		return l[0:n-1]
+	}
+	return l
+}
+
+// expand tabs to 4 spaces. don't worry about columns.
+func expandTabs(l []byte) []byte {
+	j := 0;	// position in linebuf.
+	for _, c := range l {
+		if c == '\t' {
+			for k := 0; k < 4; k++ {
+				linebuf[j] = ' ';
+				j++;
+			}
+		} else {
+			linebuf[j] = c;
+			j++;
+		}
+	}
+	return linebuf[0:j];
+}