| 
									
										
										
										
											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: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //	http://godoc/	main landing page (TODO) | 
					
						
							|  |  |  | //	http://godoc/doc/	serve from $GOROOT/doc - spec, mem, tutorial, etc. (TODO) | 
					
						
							|  |  |  | //	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 ( | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	"ast"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"bufio"; | 
					
						
							|  |  |  | 	"flag"; | 
					
						
							|  |  |  | 	"fmt"; | 
					
						
							|  |  |  | 	"http"; | 
					
						
							|  |  |  | 	"io"; | 
					
						
							|  |  |  | 	"log"; | 
					
						
							|  |  |  | 	"net"; | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	"once"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"os"; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	"parser"; | 
					
						
							| 
									
										
										
										
											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"; | 
					
						
							|  |  |  | 	"token"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"vector"; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	"astprinter"; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	"docprinter"; | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const usageString = | 
					
						
							|  |  |  | 	"usage: godoc package [name ...]\n" | 
					
						
							|  |  |  | 	"	godoc -http=:6060\n" | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	docPrefix = "/doc/"; | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	filePrefix = "/file/"; | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -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-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-03 16:19:22 -07:00
										 |  |  | func isHTMLFile(dir *os.Dir) bool { | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	return dir.IsRegular() && strings.HasSuffix(dir.Name, ".html"); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -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(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func isFile(name string) bool { | 
					
						
							|  |  |  | 	d, err := os.Stat(name); | 
					
						
							|  |  |  | 	return err == nil && d.IsRegular(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | func printLink(c io.Write, dir, name string) { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	fmt.Fprintf(c, "<a href=\"%s\">%s</a><br />\n", pathutil.Clean(filePrefix + dir + "/" + name), name); | 
					
						
							| 
									
										
										
										
											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-02 18:25:18 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | // Parsing | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | type parseError struct { | 
					
						
							|  |  |  | 	pos token.Position; | 
					
						
							|  |  |  | 	msg string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type errorList []parseError | 
					
						
							|  |  |  | func (list errorList) Len() int { return len(list); } | 
					
						
							|  |  |  | func (list errorList) Less(i, j int) bool { return list[i].pos.Offset < list[j].pos.Offset; } | 
					
						
							|  |  |  | func (list errorList) Swap(i, j int) { list[i], list[j] = list[j], list[i]; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type errorHandler struct { | 
					
						
							|  |  |  | 	lastLine int; | 
					
						
							|  |  |  | 	errors *vector.Vector; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (h *errorHandler) 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 | 
					
						
							|  |  |  | 	if pos.Line != h.lastLine { | 
					
						
							|  |  |  | 		h.lastLine = pos.Line; | 
					
						
							|  |  |  | 		if h.errors == nil { | 
					
						
							|  |  |  | 			// lazy initialize - most of the time there are no errors | 
					
						
							|  |  |  | 			h.errors = vector.New(0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		h.errors.Push(parseError{pos, msg}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-13 13:28:53 -07:00
										 |  |  | func parse(path string, mode uint) (*ast.Program, errorList) { | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	src, err := os.Open(path, os.O_RDONLY, 0); | 
					
						
							|  |  |  | 	defer src.Close(); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2009-04-14 00:06:49 -07:00
										 |  |  | 		log.Stderrf("open %s: %v", path, err); | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 		var noPos token.Position; | 
					
						
							|  |  |  | 		return nil, errorList{parseError{noPos, err.String()}}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var handler errorHandler; | 
					
						
							|  |  |  | 	prog, ok := parser.Parse(src, &handler, mode); | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		// convert error list and sort it | 
					
						
							|  |  |  | 		errors := make(errorList, handler.errors.Len()); | 
					
						
							|  |  |  | 		for i := 0; i < handler.errors.Len(); i++ { | 
					
						
							|  |  |  | 			errors[i] = handler.errors.At(i).(parseError); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sort.Sort(errors); | 
					
						
							|  |  |  | 		return nil, errors; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return prog, nil; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Templates | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // html template | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | var godoc_html string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func readTemplate() { | 
					
						
							|  |  |  | 	name := "usr/gri/pretty/godoc.html"; | 
					
						
							|  |  |  | 	f, err := os.Open(name, os.O_RDONLY, 0); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Exitf("open %s: %v", name, err); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	if n, err := io.Copy(f, &b); err != nil { | 
					
						
							|  |  |  | 		log.Exitf("copy %s: %v", name, err); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	f.Close(); | 
					
						
							|  |  |  | 	godoc_html = string(b.Data()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func servePage(c *http.Conn, title, content interface{}) { | 
					
						
							|  |  |  | 	once.Do(readTemplate); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	c.SetHeader("content-type", "text/html; charset=utf-8"); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	type Data struct { | 
					
						
							|  |  |  | 		title string; | 
					
						
							|  |  |  | 		header string; | 
					
						
							|  |  |  | 		timestamp string; | 
					
						
							|  |  |  | 		content string; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	// TODO(rsc): Once template system can handle []byte, | 
					
						
							|  |  |  | 	// remove this conversion. | 
					
						
							|  |  |  | 	if x, ok := title.([]byte); ok { | 
					
						
							|  |  |  | 		title = string(x); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if x, ok := content.([]byte); ok { | 
					
						
							|  |  |  | 		content = string(x); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var d Data; | 
					
						
							|  |  |  | 	d.title = title.(string); | 
					
						
							|  |  |  | 	d.header = title.(string); | 
					
						
							|  |  |  | 	d.timestamp = time.UTC().String(); | 
					
						
							|  |  |  | 	d.content = content.(string); | 
					
						
							| 
									
										
										
										
											2009-04-14 00:06:49 -07:00
										 |  |  | 	templ, err, line := template.Parse(godoc_html, nil); | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Stderrf("template error %s:%d: %s\n", title, line, err); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		templ.Execute(&d, c); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func serveError(c *http.Conn, err, arg string) { | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	servePage(c, "Error", fmt.Sprintf("%v (%s)\n", err, arg)); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Directories | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | type dirArray []os.Dir | 
					
						
							|  |  |  | func (p dirArray) Len() int            { return len(p); } | 
					
						
							|  |  |  | func (p dirArray) Less(i, j int) bool  { return p[i].Name < p[j].Name; } | 
					
						
							|  |  |  | func (p dirArray) Swap(i, j int)       { p[i], p[j] = p[j], p[i]; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | func serveDir(c *http.Conn, dirname string) { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	fd, err1 := os.Open(dirname, os.O_RDONLY, 0); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	if err1 != nil { | 
					
						
							|  |  |  | 		c.WriteHeader(http.StatusNotFound); | 
					
						
							|  |  |  | 		fmt.Fprintf(c, "Error: %v (%s)\n", err1, dirname); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list, err2 := fd.Readdir(-1); | 
					
						
							|  |  |  | 	if err2 != nil { | 
					
						
							|  |  |  | 		c.WriteHeader(http.StatusNotFound); | 
					
						
							|  |  |  | 		fmt.Fprintf(c, "Error: %v (%s)\n", err2, dirname); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sort.Sort(dirArray(list)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	path := dirname + "/"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Print contents in 3 sections: directories, go files, everything else | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	fmt.Fprintln(&b, "<h2>Directories</h2>"); | 
					
						
							|  |  |  | 	for i, entry := range list { | 
					
						
							|  |  |  | 		if entry.IsDirectory() { | 
					
						
							|  |  |  | 			printLink(&b, path, entry.Name); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	fmt.Fprintln(&b, "<h2>Go files</h2>"); | 
					
						
							|  |  |  | 	for i, entry := range list { | 
					
						
							|  |  |  | 		if isGoFile(&entry) { | 
					
						
							|  |  |  | 			printLink(&b, path, entry.Name); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	fmt.Fprintln(&b, "<h2>Other files</h2>"); | 
					
						
							|  |  |  | 	for i, entry := range list { | 
					
						
							|  |  |  | 		if !entry.IsDirectory() && !isGoFile(&entry) { | 
					
						
							|  |  |  | 			fmt.Fprintf(&b, "%s<br />\n", entry.Name); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	servePage(c, dirname + " - Contents", b.Data()); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Files | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | func serveParseErrors(c *http.Conn, filename string, errors errorList) { | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	// open file | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	path := filename; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	fd, err1 := os.Open(path, os.O_RDONLY, 0); | 
					
						
							|  |  |  | 	defer fd.Close(); | 
					
						
							|  |  |  | 	if err1 != nil { | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		serveError(c, err1.String(), path); | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	// read source | 
					
						
							|  |  |  | 	var buf io.ByteBuffer; | 
					
						
							|  |  |  | 	n, err2 := io.Copy(fd, &buf); | 
					
						
							|  |  |  | 	if err2 != nil { | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		serveError(c, err2.String(), path); | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2009-04-02 18:25:18 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	src := buf.Data(); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	// generate body | 
					
						
							|  |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	// section title | 
					
						
							|  |  |  | 	fmt.Fprintf(&b, "<h1>Parse errors in %s</h1>\n", filename); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	// handle read errors | 
					
						
							|  |  |  | 	if err1 != nil || err2 != nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(&b, "could not read file %s\n", filename); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	// write source with error messages interspersed | 
					
						
							|  |  |  | 	fmt.Fprintln(&b, "<pre>"); | 
					
						
							|  |  |  | 	offs := 0; | 
					
						
							|  |  |  | 	for i, e := range errors { | 
					
						
							|  |  |  | 		if 0 <= e.pos.Offset && e.pos.Offset <= len(src) { | 
					
						
							|  |  |  | 			// TODO handle Write errors | 
					
						
							|  |  |  | 			b.Write(src[offs : e.pos.Offset]); | 
					
						
							|  |  |  | 			// TODO this should be done using a .css file | 
					
						
							|  |  |  | 			fmt.Fprintf(&b, "<b><font color=red>%s >>></font></b>", e.msg); | 
					
						
							|  |  |  | 			offs = e.pos.Offset; | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2009-04-14 00:06:49 -07:00
										 |  |  | 			log.Stderrf("error position %d out of bounds (len = %d)", e.pos.Offset, len(src)); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// TODO handle Write errors | 
					
						
							|  |  |  | 	b.Write(src[offs : len(src)]); | 
					
						
							|  |  |  | 	fmt.Fprintln(&b, "</pre>"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	servePage(c, filename, b.Data()); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | func serveGoSource(c *http.Conn, dirname string, filename string) { | 
					
						
							|  |  |  | 	path := dirname + "/" + filename; | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	prog, errors := parse(path, parser.ParseComments); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	if len(errors) > 0 { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		serveParseErrors(c, filename, 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>"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	servePage(c, path + " - Go source", b.Data()); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | func serveHTMLFile(c *http.Conn, filename string) { | 
					
						
							|  |  |  | 	src, err1 := os.Open(filename, os.O_RDONLY, 0); | 
					
						
							|  |  |  | 	defer src.Close(); | 
					
						
							|  |  |  | 	if err1 != nil { | 
					
						
							|  |  |  | 		serveError(c, err1.String(), filename); | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	if written, err2 := io.Copy(src, c); err2 != nil { | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		serveError(c, err2.String(), filename); | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func serveFile(c *http.Conn, path string) { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	dir, err := os.Stat(path); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		serveError(c, err.String(), path); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case dir.IsDirectory(): | 
					
						
							|  |  |  | 		serveDir(c, path); | 
					
						
							|  |  |  | 	case isGoFile(dir): | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		serveGoSource(c, ".", path); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	case isHTMLFile(dir): | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		serveHTMLFile(c, path); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		serveError(c, "Not a directory or .go file", path); | 
					
						
							| 
									
										
										
										
											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-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-03 16:19:22 -07:00
										 |  |  | func addFile(pmap map[string]*pakDesc, dirname string, filename 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-13 13:28:53 -07:00
										 |  |  | 	pakname := pathutil.Clean(dirname + "/" + prog.Name.Value); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// find package descriptor | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	pakdesc, found := pmap[pakname]; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	if !found { | 
					
						
							|  |  |  | 		// add a new descriptor | 
					
						
							|  |  |  | 		pakdesc = &pakDesc{dirname, prog.Name.Value, make(map[string]bool)}; | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		pmap[pakname] = 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-03 16:19:22 -07:00
										 |  |  | func addDirectory(pmap map[string]*pakDesc, dirname string) { | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, entry := range list { | 
					
						
							|  |  |  | 		switch { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		case isGoFile(&entry): | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 			//fmt.Printf("found %s/%s\n", dirname, entry.Name); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 			addFile(pmap, dirname, entry.Name); | 
					
						
							| 
									
										
										
										
											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-03 16:19:22 -07:00
										 |  |  | func servePackage(c *http.Conn, p *pakDesc) { | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	// make a filename list | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	filenames := make([]string, len(p.filenames)); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	i := 0; | 
					
						
							|  |  |  | 	for filename, tmp := range p.filenames { | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		filenames[i] = filename; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	// compute documentation | 
					
						
							|  |  |  | 	var doc docPrinter.PackageDoc; | 
					
						
							|  |  |  | 	for i, filename := range filenames { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		path := p.dirname + "/" + filename; | 
					
						
							|  |  |  | 		prog, errors := parse(path, parser.ParseComments); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 		if len(errors) > 0 { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 			serveParseErrors(c, filename, errors); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if i == 0 { | 
					
						
							|  |  |  | 			// first package - initialize docPrinter | 
					
						
							|  |  |  | 			doc.Init(prog.Name.Value); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		doc.AddProgram(prog); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	writer := makeTabwriter(&b);  // for nicely formatted output | 
					
						
							|  |  |  | 	doc.Print(writer); | 
					
						
							|  |  |  | 	writer.Flush();	// ignore errors | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	servePage(c, doc.PackageName() + " - Go package documentation", b.Data()); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | func servePackageList(c *http.Conn, list pakArray) { | 
					
						
							| 
									
										
										
										
											2009-04-13 15:25:50 -07:00
										 |  |  | 	var b io.ByteBuffer; | 
					
						
							|  |  |  | 	for i := 0; i < len(list); i++ { | 
					
						
							|  |  |  | 		p := list[i]; | 
					
						
							|  |  |  | 		link := pathutil.Clean(p.dirname + "/" + p.pakname); | 
					
						
							|  |  |  | 		fmt.Fprintf(&b, "<a href=\"%s\">%s</a> <font color=grey>(%s)</font><br />\n", | 
					
						
							|  |  |  | 			p.pakname, p.pakname, link); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	servePage(c, "Packages", b.Data()); | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// TODO: show subdirectories | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | func findPackages(name string) (*pakDesc, pakArray) { | 
					
						
							|  |  |  | 	// 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); | 
					
						
							|  |  |  | 	dir := pathutil.Clean("src/lib/" + name); | 
					
						
							|  |  |  | 	if isDir(dir) { | 
					
						
							|  |  |  | 		parent, pak := pathutil.Split(dir); | 
					
						
							|  |  |  | 		addDirectory(pmap, dir); | 
					
						
							|  |  |  | 		paks := mapValues(pmap); | 
					
						
							|  |  |  | 		if len(paks) == 1 { | 
					
						
							|  |  |  | 			p := paks[0]; | 
					
						
							|  |  |  | 			if p.dirname == dir && p.pakname == pak { | 
					
						
							|  |  |  | 				return p, nil; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, paks; | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  | 	addDirectory(pmap, parentdir); | 
					
						
							|  |  |  | 	if p, ok := pmap[dir]; ok { | 
					
						
							|  |  |  | 		return p, nil; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 	return nil, nil; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func servePkg(c *http.Conn, path string) { | 
					
						
							|  |  |  | 	pak, paks := findPackages(path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// TODO: canonicalize path and redirect if needed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case pak != nil: | 
					
						
							|  |  |  | 		servePackage(c, pak); | 
					
						
							|  |  |  | 	case len(paks) > 0: | 
					
						
							|  |  |  | 		servePackageList(c, paks); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		serveError(c, "No packages found", path); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ---------------------------------------------------------------------------- | 
					
						
							|  |  |  | // Server | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | func makeFixedFileServer(filename string) (func(c *http.Conn, path string)) { | 
					
						
							|  |  |  | 	return func(c *http.Conn, path string) { | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		serveFile(c, filename); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | func installHandler(prefix string, handler func(c *http.Conn, path string)) { | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 	// create a handler customized with prefix | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 	f := func(c *http.Conn, req *http.Request) { | 
					
						
							|  |  |  | 		path := req.Url.Path; | 
					
						
							|  |  |  | 		if *verbose { | 
					
						
							| 
									
										
										
										
											2009-04-14 00:06:49 -07:00
										 |  |  | 			log.Stderrf("%s\t%s", req.Host, path); | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		handler(c, path[len(prefix) : len(path)]); | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-02 21:59:37 -07:00
										 |  |  | 	// install the customized handler | 
					
						
							|  |  |  | 	http.Handle(prefix, http.HandlerFunc(f)); | 
					
						
							| 
									
										
										
										
											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-13 13:28:53 -07:00
										 |  |  | 	if *httpaddr != "" { | 
					
						
							|  |  |  | 		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-13 13:28:53 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		installHandler("/mem", makeFixedFileServer("doc/go_mem.html")); | 
					
						
							|  |  |  | 		installHandler("/spec", makeFixedFileServer("doc/go_spec.html")); | 
					
						
							|  |  |  | 		installHandler("/pkg/", servePkg); | 
					
						
							|  |  |  | 		installHandler(filePrefix, serveFile); | 
					
						
							| 
									
										
										
										
											2009-04-03 16:19:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-13 13:28:53 -07:00
										 |  |  | 		if err := http.ListenAndServe(*httpaddr, nil); err != nil { | 
					
						
							|  |  |  | 			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
										 |  |  | 
 | 
					
						
							|  |  |  | 	log.Exitf("godoc command-line not implemented"); | 
					
						
							| 
									
										
										
										
											2009-04-02 15:58:58 -07:00
										 |  |  | } |