| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | // 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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // godoc: Go Documentation Server | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | // Web server tree: | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | //	http://godoc/	main landing page | 
					
						
							|  |  |  | //	http://godoc/doc/	serve from $GOROOT/doc - spec, mem, tutorial, etc. | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | //	http://godoc/src/	serve files from $GOROOT/src; .go gets pretty-printed | 
					
						
							|  |  |  | //	http://godoc/cmd/	serve documentation about commands (TODO) | 
					
						
							|  |  |  | //	http://godoc/pkg/	serve documentation about packages | 
					
						
							|  |  |  | //		(idea is if you say import "compress/zlib", you go to | 
					
						
							|  |  |  | //		http://godoc/pkg/compress/zlib) | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Command-line interface: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //	godoc packagepath [name ...] | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //	godoc compress/zlib | 
					
						
							|  |  |  | //		- prints doc for package proto | 
					
						
							|  |  |  | //	godoc compress/zlib Cipher NewCMAC | 
					
						
							|  |  |  | //		- prints doc for Cipher and NewCMAC in package crypto/block | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bufio"; | 
					
						
							| 
									
										
										
										
											2009-04-16 20:52:37 -07:00
										 |  |  | 	"container/vector"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"flag"; | 
					
						
							|  |  |  | 	"fmt"; | 
					
						
							| 
									
										
										
										
											2009-04-16 20:52:37 -07:00
										 |  |  | 	"go/ast"; | 
					
						
							|  |  |  | 	"go/parser"; | 
					
						
							|  |  |  | 	"go/token"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"http"; | 
					
						
							|  |  |  | 	"io"; | 
					
						
							|  |  |  | 	"log"; | 
					
						
							|  |  |  | 	"net"; | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	"once"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"os"; | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	pathutil "path"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"sort"; | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	"strings"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"tabwriter"; | 
					
						
							|  |  |  | 	"template"; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	"time"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	"astprinter"; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	"comment"; | 
					
						
							|  |  |  | 	"docprinter";	// TODO: "doc" | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | // TODO | 
					
						
							|  |  |  | // - uniform use of path, filename, dirname, pakname, etc. | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | // - fix weirdness with double-/'s in paths | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | // - split http service into its own source file | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // TODO: tell flag package about usage string | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | const usageString = | 
					
						
							|  |  |  | 	"usage: godoc package [name ...]\n" | 
					
						
							|  |  |  | 	"	godoc -http=:6060\n" | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | var ( | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	goroot string; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	verbose = flag.Bool("v", false, "verbose mode"); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// server control | 
					
						
							|  |  |  | 	httpaddr = flag.String("http", "", "HTTP service address (e.g., ':6060')"); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// layout control | 
					
						
							|  |  |  | 	tabwidth = flag.Int("tabwidth", 4, "tab width"); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	usetabs = flag.Bool("tabs", false, "align with tabs instead of spaces"); | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	html = flag.Bool("html", false, "print HTML in command-line mode"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pkgroot = flag.String("pkgroot", "src/lib", "root package source directory (if unrooted, relative to goroot)"); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	Pkg = "/pkg/"	// name for auto-generated package documentation tree | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | func init() { | 
					
						
							|  |  |  | 	var err *os.Error; | 
					
						
							|  |  |  | 	goroot, err = os.Getenv("GOROOT"); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		goroot = "/home/r/go-build/go"; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	flag.StringVar(&goroot, "goroot", goroot, "Go root directory"); | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Support | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | func isGoFile(dir *os.Dir) bool { | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	return dir.IsRegular() && strings.HasSuffix(dir.Name, ".go"); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | func isDir(name string) bool { | 
					
						
							|  |  |  | 	d, err := os.Stat(name); | 
					
						
							|  |  |  | 	return err == nil && d.IsDirectory(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | func makeTabwriter(writer io.Write) *tabwriter.Writer { | 
					
						
							|  |  |  | 	padchar := byte(' '); | 
					
						
							|  |  |  | 	if *usetabs { | 
					
						
							|  |  |  | 		padchar = '\t'; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return tabwriter.NewWriter(writer, *tabwidth, 1, padchar, tabwriter.FilterHTML); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // TODO(rsc): this belongs in a library somewhere, maybe os | 
					
						
							|  |  |  | func ReadFile(name string) ([]byte, *os.Error) { | 
					
						
							|  |  |  | 	f, err := os.Open(name, os.O_RDONLY, 0); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer f.Close(); | 
					
						
							|  |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	if n, err := io.Copy(f, &b); err != nil { | 
					
						
							|  |  |  | 		return nil, err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return b.Data(), nil; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | // Parsing | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | type rawError struct { | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	pos token.Position; | 
					
						
							|  |  |  | 	msg string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | type rawErrorVector struct { | 
					
						
							|  |  |  | 	vector.Vector; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (v *rawErrorVector) At(i int) rawError { return v.Vector.At(i).(rawError) } | 
					
						
							|  |  |  | func (v *rawErrorVector) Less(i, j int) bool { return v.At(i).pos.Offset < v.At(j).pos.Offset; } | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (v *rawErrorVector) Error(pos token.Position, msg string) { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	// only collect errors that are on a new line | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	// in the hope to avoid most follow-up errors | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	lastLine := 0; | 
					
						
							|  |  |  | 	if n := v.Len(); n > 0 { | 
					
						
							|  |  |  | 		lastLine = v.At(n - 1).pos.Line; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if lastLine != pos.Line { | 
					
						
							|  |  |  | 		v.Push(rawError{pos, msg}); | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // A single error in the parsed file. | 
					
						
							|  |  |  | type parseError struct { | 
					
						
							|  |  |  | 	src []byte;	// source before error | 
					
						
							|  |  |  | 	line int;	// line number of error | 
					
						
							|  |  |  | 	msg string;	// error message | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // All the errors in the parsed file, plus surrounding source code. | 
					
						
							|  |  |  | // Each error has a slice giving the source text preceding it | 
					
						
							|  |  |  | // (starting where the last error occurred).  The final element in list[] | 
					
						
							|  |  |  | // has msg = "", to give the remainder of the source code. | 
					
						
							|  |  |  | // This data structure is handed to the templates parseerror.txt and parseerror.html. | 
					
						
							|  |  |  | type parseErrors struct { | 
					
						
							|  |  |  | 	filename string;	// path to file | 
					
						
							|  |  |  | 	list []parseError;	// the errors | 
					
						
							|  |  |  | 	src []byte;	// the file's entire source code | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | // Parses a file (path) and returns the corresponding AST and | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | // a sorted list (by file position) of errors, if any. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func parse(filename string, mode uint) (*ast.Program, *parseErrors) { | 
					
						
							|  |  |  | 	src, err := ReadFile(filename); | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		log.Stderrf("ReadFile %s: %v", filename, err); | 
					
						
							|  |  |  | 		errs := []parseError{parseError{nil, 0, err.String()}}; | 
					
						
							|  |  |  | 		return nil, &parseErrors{filename, errs, nil}; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	var raw rawErrorVector; | 
					
						
							|  |  |  | 	prog, ok := parser.Parse(src, &raw, mode); | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		// sort and convert error list | 
					
						
							|  |  |  | 		sort.Sort(&raw); | 
					
						
							|  |  |  | 		errs := make([]parseError, raw.Len() + 1);	// +1 for final fragment of source | 
					
						
							|  |  |  | 		offs := 0; | 
					
						
							|  |  |  | 		for i := 0; i < raw.Len(); i++ { | 
					
						
							|  |  |  | 			r := raw.At(i); | 
					
						
							|  |  |  | 			// Should always be true, but check for robustness. | 
					
						
							|  |  |  | 			if 0 <= r.pos.Offset && r.pos.Offset <= len(src) { | 
					
						
							|  |  |  | 				errs[i].src = src[offs : r.pos.Offset]; | 
					
						
							|  |  |  | 				offs = r.pos.Offset; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			errs[i].line = r.pos.Line; | 
					
						
							|  |  |  | 			errs[i].msg = r.msg; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		errs[raw.Len()].src = src[offs : len(src)]; | 
					
						
							|  |  |  | 		return nil, &parseErrors{filename, errs, src}; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return prog, nil; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Templates | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Return text for decl. | 
					
						
							|  |  |  | func DeclText(d ast.Decl) []byte { | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	var b io.ByteBuffer; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	var p astPrinter.Printer; | 
					
						
							|  |  |  | 	p.Init(&b, nil, nil, false); | 
					
						
							|  |  |  | 	d.Visit(&p); | 
					
						
							|  |  |  | 	return b.Data(); | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Return text for expr. | 
					
						
							|  |  |  | func ExprText(d ast.Expr) []byte { | 
					
						
							|  |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	var p astPrinter.Printer; | 
					
						
							|  |  |  | 	p.Init(&b, nil, nil, false); | 
					
						
							|  |  |  | 	d.Visit(&p); | 
					
						
							|  |  |  | 	return b.Data(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Convert x, whatever it is, to text form. | 
					
						
							|  |  |  | func toText(x interface{}) []byte { | 
					
						
							|  |  |  | 	type String interface { String() string } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch v := x.(type) { | 
					
						
							|  |  |  | 	case []byte: | 
					
						
							|  |  |  | 		return v; | 
					
						
							|  |  |  | 	case string: | 
					
						
							|  |  |  | 		return io.StringBytes(v); | 
					
						
							|  |  |  | 	case String: | 
					
						
							|  |  |  | 		return io.StringBytes(v.String()); | 
					
						
							|  |  |  | 	case ast.Decl: | 
					
						
							|  |  |  | 		return DeclText(v); | 
					
						
							|  |  |  | 	case ast.Expr: | 
					
						
							|  |  |  | 		return ExprText(v); | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	fmt.Fprint(&b, x); | 
					
						
							|  |  |  | 	return b.Data(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Template formatter for "html" format. | 
					
						
							|  |  |  | func htmlFmt(w io.Write, x interface{}, format string) { | 
					
						
							|  |  |  | 	// Can do better than text in some cases. | 
					
						
							|  |  |  | 	switch v := x.(type) { | 
					
						
							|  |  |  | 	case ast.Decl: | 
					
						
							|  |  |  | 		var p astPrinter.Printer; | 
					
						
							|  |  |  | 		tw := makeTabwriter(w); | 
					
						
							|  |  |  | 		p.Init(tw, nil, nil, true); | 
					
						
							|  |  |  | 		v.Visit(&p); | 
					
						
							|  |  |  | 		tw.Flush(); | 
					
						
							|  |  |  | 	case ast.Expr: | 
					
						
							|  |  |  | 		var p astPrinter.Printer; | 
					
						
							|  |  |  | 		tw := makeTabwriter(w); | 
					
						
							|  |  |  | 		p.Init(tw, nil, nil, true); | 
					
						
							|  |  |  | 		v.Visit(&p); | 
					
						
							|  |  |  | 		tw.Flush(); | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		template.HtmlEscape(w, toText(x)); | 
					
						
							| 
									
										
										
										
											2009-04-14 00:06:49 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Template formatter for "html-comment" format. | 
					
						
							|  |  |  | func htmlCommentFmt(w io.Write, x interface{}, format string) { | 
					
						
							|  |  |  | 	comment.ToHtml(w, toText(x)); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Template formatter for "" (default) format. | 
					
						
							|  |  |  | func textFmt(w io.Write, x interface{}, format string) { | 
					
						
							|  |  |  | 	w.Write(toText(x)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Template formatter for "dir/" format. | 
					
						
							|  |  |  | // Writes out "/" if the os.Dir argument is a directory. | 
					
						
							|  |  |  | var slash = io.StringBytes("/"); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func dirSlashFmt(w io.Write, x interface{}, format string) { | 
					
						
							|  |  |  | 	d := x.(os.Dir);	// TODO(rsc): want *os.Dir | 
					
						
							|  |  |  | 	if d.IsDirectory() { | 
					
						
							|  |  |  | 		w.Write(slash); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | var fmap = template.FormatterMap{ | 
					
						
							|  |  |  | 	"": textFmt, | 
					
						
							|  |  |  | 	"html": htmlFmt, | 
					
						
							|  |  |  | 	"html-comment": htmlCommentFmt, | 
					
						
							|  |  |  | 	"dir/": dirSlashFmt, | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // TODO: const templateDir = "lib/godoc" | 
					
						
							|  |  |  | const templateDir = "usr/gri/pretty" | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func ReadTemplate(name string) *template.Template { | 
					
						
							|  |  |  | 	data, err := ReadFile(templateDir + "/" + name); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Exitf("ReadFile %s: %v", name, err); | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	t, err1, line := template.Parse(string(data), fmap); | 
					
						
							|  |  |  | 	if err1 != nil { | 
					
						
							|  |  |  | 		log.Exitf("%s:%d: %v", name, line, err); | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	return t; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | var godocHtml *template.Template | 
					
						
							|  |  |  | var packageHtml *template.Template | 
					
						
							|  |  |  | var packageText *template.Template | 
					
						
							|  |  |  | var packagelistHtml *template.Template; | 
					
						
							|  |  |  | var packagelistText *template.Template; | 
					
						
							|  |  |  | var parseerrorHtml *template.Template; | 
					
						
							|  |  |  | var parseerrorText *template.Template; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func ReadTemplates() { | 
					
						
							|  |  |  | 	// have to delay until after flags processing, | 
					
						
							|  |  |  | 	// so that main has chdir'ed to goroot. | 
					
						
							|  |  |  | 	godocHtml = ReadTemplate("godoc.html"); | 
					
						
							|  |  |  | 	packageHtml = ReadTemplate("package.html"); | 
					
						
							|  |  |  | 	packageText = ReadTemplate("package.txt"); | 
					
						
							|  |  |  | 	packagelistHtml = ReadTemplate("packagelist.html"); | 
					
						
							|  |  |  | 	packagelistText = ReadTemplate("packagelist.txt"); | 
					
						
							|  |  |  | 	parseerrorHtml = ReadTemplate("parseerror.html"); | 
					
						
							|  |  |  | 	parseerrorText = ReadTemplate("parseerror.txt"); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ---------------------------------------------------------------------------- | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // Generic HTML wrapper | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func servePage(c *http.Conn, title, content interface{}) { | 
					
						
							|  |  |  | 	type Data struct { | 
					
						
							|  |  |  | 		title interface{}; | 
					
						
							|  |  |  | 		header interface{}; | 
					
						
							|  |  |  | 		timestamp string; | 
					
						
							|  |  |  | 		content interface{}; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	var d Data; | 
					
						
							|  |  |  | 	d.title = title; | 
					
						
							|  |  |  | 	d.header = title; | 
					
						
							|  |  |  | 	d.timestamp = time.UTC().String(); | 
					
						
							|  |  |  | 	d.content = content; | 
					
						
							|  |  |  | 	godocHtml.Execute(&d, c); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func serveText(c *http.Conn, text []byte) { | 
					
						
							|  |  |  | 	c.SetHeader("content-type", "text/plain; charset=utf-8"); | 
					
						
							|  |  |  | 	c.Write(text); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func serveError(c *http.Conn, err, arg string) { | 
					
						
							|  |  |  | 	servePage(c, "Error", fmt.Sprintf("%v (%s)\n", err, arg)); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Files | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func serveParseErrors(c *http.Conn, errors *parseErrors) { | 
					
						
							|  |  |  | 	// format errors | 
					
						
							|  |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	parseerrorHtml.Execute(errors, &b); | 
					
						
							|  |  |  | 	servePage(c, errors.filename + " - Parse Errors", b.Data()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func serveGoSource(c *http.Conn, name string) { | 
					
						
							|  |  |  | 	prog, errors := parse(name, parser.ParseComments); | 
					
						
							|  |  |  | 	if errors != nil { | 
					
						
							|  |  |  | 		serveParseErrors(c, errors); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	fmt.Fprintln(&b, "<pre>"); | 
					
						
							|  |  |  | 	var p astPrinter.Printer; | 
					
						
							|  |  |  | 	writer := makeTabwriter(&b);  // for nicely formatted output | 
					
						
							|  |  |  | 	p.Init(writer, nil, nil, true); | 
					
						
							|  |  |  | 	p.DoProgram(prog); | 
					
						
							|  |  |  | 	writer.Flush();  // ignore errors | 
					
						
							|  |  |  | 	fmt.Fprintln(&b, "</pre>"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	servePage(c, name + " - Go source", b.Data()); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | var fileServer = http.FileServer(".", ""); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func serveFile(c *http.Conn, req *http.Request) { | 
					
						
							|  |  |  | 	// pick off special cases and hand the rest to the standard file server | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case req.Url.Path == "/": | 
					
						
							|  |  |  | 		// serve landing page. | 
					
						
							|  |  |  | 		// TODO: hide page from ordinary file serving. | 
					
						
							|  |  |  | 		// writing doc/index.html will take care of that. | 
					
						
							|  |  |  | 		http.ServeFile(c, req, "doc/root.html"); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	case req.Url.Path == "/doc/root.html": | 
					
						
							|  |  |  | 		// hide landing page from its real name | 
					
						
							|  |  |  | 		http.NotFound(c, req); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case pathutil.Ext(req.Url.Path) == ".go": | 
					
						
							|  |  |  | 		serveGoSource(c, req.Url.Path[1:len(req.Url.Path)]); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		fileServer.ServeHTTP(c, req); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Packages | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type pakDesc struct { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	dirname string;  // relative to goroot | 
					
						
							|  |  |  | 	pakname string;  // relative to directory | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	importpath string;	// import "___" | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	filenames map[string] bool;  // set of file (names) belonging to this package | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type pakArray []*pakDesc | 
					
						
							|  |  |  | func (p pakArray) Len() int            { return len(p); } | 
					
						
							|  |  |  | func (p pakArray) Less(i, j int) bool  { return p[i].pakname < p[j].pakname; } | 
					
						
							|  |  |  | func (p pakArray) Swap(i, j int)       { p[i], p[j] = p[j], p[i]; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func addFile(pmap map[string]*pakDesc, dirname, filename, importprefix string) { | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	if strings.HasSuffix(filename, "_test.go") { | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 		// ignore package tests | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	// determine package name | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	path := dirname + "/" + filename; | 
					
						
							|  |  |  | 	prog, errors := parse(path, parser.PackageClauseOnly); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	if prog == nil { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if prog.Name.Value == "main" { | 
					
						
							|  |  |  | 		// ignore main packages for now | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var importpath string; | 
					
						
							|  |  |  | 	dir, name := pathutil.Split(importprefix); | 
					
						
							|  |  |  | 	if name == prog.Name.Value {	// package math in directory "math" | 
					
						
							|  |  |  | 		importpath = importprefix; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		importpath = pathutil.Clean(importprefix + "/" + prog.Name.Value); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// find package descriptor | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	pakdesc, found := pmap[importpath]; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	if !found { | 
					
						
							|  |  |  | 		// add a new descriptor | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		pakdesc = &pakDesc{dirname, prog.Name.Value, importpath, make(map[string]bool)}; | 
					
						
							|  |  |  | 		pmap[importpath] = pakdesc; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	//fmt.Printf("pak = %s, file = %s\n", pakname, filename); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add file to package desc | 
					
						
							|  |  |  | 	if tmp, found := pakdesc.filenames[filename]; found { | 
					
						
							|  |  |  | 		panic("internal error: same file added more then once: " + filename); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pakdesc.filenames[filename] = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func addDirectory(pmap map[string]*pakDesc, dirname, importprefix string, subdirs *[]os.Dir) { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	path := dirname; | 
					
						
							|  |  |  | 	fd, err1 := os.Open(path, os.O_RDONLY, 0); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	if err1 != nil { | 
					
						
							| 
									
										
										
										
											2009-04-14 00:06:49 -07:00
										 |  |  | 		log.Stderrf("open %s: %v", path, err1); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list, err2 := fd.Readdir(-1); | 
					
						
							|  |  |  | 	if err2 != nil { | 
					
						
							| 
									
										
										
										
											2009-04-14 00:06:49 -07:00
										 |  |  | 		log.Stderrf("readdir %s: %v", path, err2); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	nsub := 0; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	for i, entry := range list { | 
					
						
							|  |  |  | 		switch { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		case isGoFile(&entry): | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			addFile(pmap, dirname, entry.Name, importprefix); | 
					
						
							|  |  |  | 		case entry.IsDirectory(): | 
					
						
							|  |  |  | 			nsub++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if subdirs != nil && nsub > 0 { | 
					
						
							|  |  |  | 		*subdirs = make([]os.Dir, nsub); | 
					
						
							|  |  |  | 		nsub = 0; | 
					
						
							|  |  |  | 		for i, entry := range list { | 
					
						
							|  |  |  | 			if entry.IsDirectory() { | 
					
						
							|  |  |  | 				subdirs[nsub] = entry; | 
					
						
							|  |  |  | 				nsub++; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | func mapValues(pmap map[string]*pakDesc) pakArray { | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	// build sorted package list | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	plist := make(pakArray, len(pmap)); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	i := 0; | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	for tmp, pakdesc := range pmap { | 
					
						
							|  |  |  | 		plist[i] = pakdesc; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	sort.Sort(plist); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	return plist; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func (p *pakDesc) Doc() (*doc.PackageDoc, *parseErrors) { | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	// compute documentation | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	var r doc.DocReader; | 
					
						
							|  |  |  | 	i := 0; | 
					
						
							|  |  |  | 	for filename := range p.filenames { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		path := p.dirname + "/" + filename; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		prog, err := parse(path, parser.ParseComments); | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err; | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if i == 0 { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			// first file - initialize doc | 
					
						
							|  |  |  | 			r.Init(prog.Name.Value, p.importpath); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		i++; | 
					
						
							|  |  |  | 		r.AddProgram(prog); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return r.Doc(), nil; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func servePackage(c *http.Conn, p *pakDesc) { | 
					
						
							|  |  |  | 	doc, errors := p.Doc(); | 
					
						
							|  |  |  | 	if errors != nil { | 
					
						
							|  |  |  | 		serveParseErrors(c, errors); | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	var b io.ByteBuffer; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	if false {	// TODO req.Params["format"] == "text" | 
					
						
							|  |  |  | 		err := packageText.Execute(doc, &b); | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			log.Stderrf("packageText.Execute: %s", err); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		serveText(c, b.Data()); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err := packageHtml.Execute(doc, &b); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Stderrf("packageHtml.Execute: %s", err); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	servePage(c, doc.ImportPath + " - Go package documentation", b.Data()); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | type pakInfo struct { | 
					
						
							|  |  |  | 	Path string; | 
					
						
							|  |  |  | 	Package *pakDesc; | 
					
						
							|  |  |  | 	Packages pakArray; | 
					
						
							|  |  |  | 	Subdirs []os.Dir;	// TODO(rsc): []*os.Dir | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func servePackageList(c *http.Conn, info *pakInfo) { | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	var b io.ByteBuffer; | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	err := packagelistHtml.Execute(info, &b); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Stderrf("packagelistHtml.Execute: %s", err); | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	servePage(c, info.Path + " - Go packages", b.Data()); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | // Return package or packages named by name. | 
					
						
							|  |  |  | // Name is either an import string or a directory, | 
					
						
							|  |  |  | // like you'd see in $GOROOT/pkg/ once the 6g | 
					
						
							|  |  |  | // tools can handle a hierarchy there. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Examples: | 
					
						
							|  |  |  | //	"math"	- single package made up of directory | 
					
						
							|  |  |  | //	"container"	- directory listing | 
					
						
							|  |  |  | //	"container/vector"	- single package in container directory | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func findPackages(name string) *pakInfo { | 
					
						
							|  |  |  | 	info := new(pakInfo); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	// Build list of packages. | 
					
						
							|  |  |  | 	// If the path names a directory, scan that directory | 
					
						
							|  |  |  | 	// for a package with the name matching the directory name. | 
					
						
							|  |  |  | 	// Otherwise assume it is a package name inside | 
					
						
							|  |  |  | 	// a directory, so scan the parent. | 
					
						
							|  |  |  | 	pmap := make(map[string]*pakDesc); | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	cname := pathutil.Clean(name); | 
					
						
							|  |  |  | 	if cname == "" { | 
					
						
							|  |  |  | 		cname = "." | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dir := pathutil.Join(*pkgroot, cname); | 
					
						
							|  |  |  | 	url := pathutil.Join(Pkg, cname); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	if isDir(dir) { | 
					
						
							|  |  |  | 		parent, pak := pathutil.Split(dir); | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		addDirectory(pmap, dir, cname, &info.Subdirs); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		paks := mapValues(pmap); | 
					
						
							|  |  |  | 		if len(paks) == 1 { | 
					
						
							|  |  |  | 			p := paks[0]; | 
					
						
							|  |  |  | 			if p.dirname == dir && p.pakname == pak { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 				info.Package = p; | 
					
						
							|  |  |  | 				info.Path = cname; | 
					
						
							|  |  |  | 				return info; | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		info.Packages = paks; | 
					
						
							|  |  |  | 		if cname == "." { | 
					
						
							|  |  |  | 			info.Path = ""; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			info.Path = cname + "/"; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return info; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	// Otherwise, have parentdir/pak.  Look for package pak in dir. | 
					
						
							|  |  |  | 	parentdir, pak := pathutil.Split(dir); | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	parentname, nam := pathutil.Split(cname); | 
					
						
							|  |  |  | 	if parentname == "" { | 
					
						
							|  |  |  | 		parentname = "." | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	addDirectory(pmap, parentdir, parentname, nil); | 
					
						
							|  |  |  | 	if p, ok := pmap[cname]; ok { | 
					
						
							|  |  |  | 		info.Package = p; | 
					
						
							|  |  |  | 		info.Path = cname; | 
					
						
							|  |  |  | 		return info; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	info.Path = name;	// original, uncleaned name | 
					
						
							|  |  |  | 	return info; | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func servePkg(c *http.Conn, r *http.Request) { | 
					
						
							|  |  |  | 	path := r.Url.Path; | 
					
						
							|  |  |  | 	path = path[len(Pkg) : len(path)]; | 
					
						
							|  |  |  | 	info := findPackages(path); | 
					
						
							|  |  |  | 	if r.Url.Path != Pkg + info.Path { | 
					
						
							|  |  |  | 		http.Redirect(c, info.Path); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	if info.Package != nil { | 
					
						
							|  |  |  | 		servePackage(c, info.Package); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		servePackageList(c, info); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Server | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | func LoggingHandler(h http.Handler) http.Handler { | 
					
						
							|  |  |  | 	return http.HandlerFunc(func(c *http.Conn, req *http.Request) { | 
					
						
							|  |  |  | 		log.Stderrf("%s\t%s", req.Host, req.Url.Path); | 
					
						
							|  |  |  | 		h.ServeHTTP(c, req); | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | func usage() { | 
					
						
							|  |  |  | 	fmt.Fprintf(os.Stderr, usageString); | 
					
						
							|  |  |  | 	sys.Exit(1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | func main() { | 
					
						
							|  |  |  | 	flag.Parse(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	// Check usage first; get usage message out early. | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case *httpaddr != "": | 
					
						
							|  |  |  | 		if flag.NArg() != 0 { | 
					
						
							|  |  |  | 			usage(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		if flag.NArg() == 0 { | 
					
						
							|  |  |  | 			usage(); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	if err := os.Chdir(goroot); err != nil { | 
					
						
							|  |  |  | 		log.Exitf("chdir %s: %v", goroot, err); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	ReadTemplates(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	if *httpaddr != "" { | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		var handler http.Handler = http.DefaultServeMux; | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		if *verbose { | 
					
						
							| 
									
										
										
										
											2009-04-14 00:06:49 -07:00
										 |  |  | 			log.Stderrf("Go Documentation Server\n"); | 
					
						
							|  |  |  | 			log.Stderrf("address = %s\n", *httpaddr); | 
					
						
							|  |  |  | 			log.Stderrf("goroot = %s\n", goroot); | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 			handler = LoggingHandler(handler); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		http.Handle(Pkg, http.HandlerFunc(servePkg)); | 
					
						
							|  |  |  | 		http.Handle("/", http.HandlerFunc(serveFile)); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 		if err := http.ListenAndServe(*httpaddr, handler); err != nil { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 			log.Exitf("ListenAndServe %s: %v", *httpaddr, err) | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-15 18:53:43 -07:00
										 |  |  | 	if *html { | 
					
						
							|  |  |  | 		packageText = packageHtml; | 
					
						
							|  |  |  | 		packagelistText = packagelistHtml; | 
					
						
							|  |  |  | 		parseerrorText = parseerrorHtml; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	info := findPackages(flag.Arg(0)); | 
					
						
							|  |  |  | 	if info.Package == nil { | 
					
						
							|  |  |  | 		err := packagelistText.Execute(info, os.Stderr); | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			log.Stderrf("packagelistText.Execute: %s", err); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sys.Exit(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	doc, errors := info.Package.Doc(); | 
					
						
							|  |  |  | 	if errors != nil { | 
					
						
							|  |  |  | 		err := parseerrorText.Execute(errors, os.Stderr); | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			log.Stderrf("parseerrorText.Execute: %s", err); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sys.Exit(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if flag.NArg() > 1 { | 
					
						
							|  |  |  | 		args := flag.Args(); | 
					
						
							|  |  |  | 		doc.Filter(args[1:len(args)]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	packageText.Execute(doc, os.Stdout); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } |