From 09f4261841320f38a0f2f4ee7515a9f03dd4fb68 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 30 Oct 2009 10:58:53 -0700 Subject: [PATCH] split godoc/godoc.go into godoc/godoc.go and godoc/main.go. R=gri CC=r http://go/go-review/1017005 --- src/cmd/godoc/Makefile | 1 + src/cmd/godoc/godoc.go | 227 +++++------------------------------------ src/cmd/godoc/main.go | 212 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 204 deletions(-) create mode 100644 src/cmd/godoc/main.go diff --git a/src/cmd/godoc/Makefile b/src/cmd/godoc/Makefile index cbaa5b3cda..f24b50966f 100644 --- a/src/cmd/godoc/Makefile +++ b/src/cmd/godoc/Makefile @@ -8,6 +8,7 @@ TARG=godoc GOFILES=\ godoc.go\ index.go\ + main.go\ snippet.go\ spec.go\ diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index 1db2795f8a..d20775b403 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -2,28 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// godoc: Go Documentation Server - -// Web server tree: -// -// http://godoc/ main landing page -// http://godoc/doc/ serve from $GOROOT/doc - spec, mem, tutorial, etc. -// 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 compress/zlib -// godoc crypto/block Cipher NewCMAC -// - prints doc for Cipher and NewCMAC in package crypto/block - - package main import ( @@ -108,17 +86,10 @@ var ( tmplroot = flag.String("tmplroot", "lib/godoc", "root template directory (if unrooted, relative to goroot)"); // periodic sync - syncCmd = flag.String("sync", "", "sync command; disabled if empty"); - syncMin = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0"); - syncDelay delayTime; // actual sync delay in minutes; usually syncDelay == syncMin, but delay may back off exponentially syncTime RWValue; // time of last sync // layout control tabwidth = flag.Int("tabwidth", 4, "tab width"); - html = flag.Bool("html", false, "print HTML in command-line mode"); - - // server control - httpaddr = flag.String("http", "", "HTTP service address (e.g., ':6060')"); ) @@ -745,185 +716,33 @@ func search(c *http.Conn, r *http.Request) { // ---------------------------------------------------------------------------- // Server -func loggingHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(c *http.Conn, req *http.Request) { - log.Stderrf("%s\t%s", c.RemoteAddr, req.Url); - h.ServeHTTP(c, req); - }); +func registerPublicHandlers(mux *http.ServeMux) { + mux.Handle(Pkg, http.HandlerFunc(servePkg)); + mux.Handle("/search", http.HandlerFunc(search)); + mux.Handle("/", http.HandlerFunc(serveFile)); } -func exec(c *http.Conn, args []string) bool { - r, w, err := os.Pipe(); - if err != nil { - log.Stderrf("os.Pipe(): %v\n", err); - return false; - } - - bin := args[0]; - fds := []*os.File{nil, w, w}; - if *verbose { - log.Stderrf("executing %v", args); - } - pid, err := os.ForkExec(bin, args, os.Environ(), goroot, fds); - defer r.Close(); - w.Close(); - if err != nil { - log.Stderrf("os.ForkExec(%q): %v\n", bin, err); - return false; - } - - var buf bytes.Buffer; - io.Copy(r, &buf); - wait, err := os.Wait(pid, 0); - if err != nil { - os.Stderr.Write(buf.Bytes()); - log.Stderrf("os.Wait(%d, 0): %v\n", pid, err); - return false; - } - if !wait.Exited() || wait.ExitStatus() != 0 { - os.Stderr.Write(buf.Bytes()); - log.Stderrf("executing %v failed (exit status = %d)", args, wait.ExitStatus()); - return false; - } - - if *verbose { - os.Stderr.Write(buf.Bytes()); - } - if c != nil { - c.SetHeader("content-type", "text/plain; charset=utf-8"); - c.Write(buf.Bytes()); - } - - return true; -} - - -func dosync(c *http.Conn, r *http.Request) { - args := []string{"/bin/sh", "-c", *syncCmd}; - if exec(c, args) { - // sync succeeded - syncTime.set(nil); - syncDelay.set(*syncMin); // revert to regular sync schedule - } else { - // sync failed - back off exponentially, but try at least once a day - syncDelay.backoff(24*60); - } -} - - -func usage() { - fmt.Fprintf(os.Stderr, - "usage: godoc package [name ...]\n" - " godoc -http=:6060\n"); - flag.PrintDefaults(); - os.Exit(2); -} - - -func main() { - flag.Usage = usage; - flag.Parse(); - - // Check usage first; get usage message out early. - switch { - case *httpaddr != "": - if flag.NArg() != 0 { - usage(); - } - default: - if flag.NArg() == 0 { - usage(); - } - } - - if err := os.Chdir(goroot); err != nil { - log.Exitf("chdir %s: %v", goroot, err); - } - - readTemplates(); - - if *httpaddr != "" { - var handler http.Handler = http.DefaultServeMux; - if *verbose { - log.Stderrf("Go Documentation Server\n"); - log.Stderrf("address = %s\n", *httpaddr); - log.Stderrf("goroot = %s\n", goroot); - log.Stderrf("pkgroot = %s\n", *pkgroot); - log.Stderrf("tmplroot = %s\n", *tmplroot); - handler = loggingHandler(handler); - } - - http.Handle(Pkg, http.HandlerFunc(servePkg)); - if *syncCmd != "" { - http.Handle("/debug/sync", http.HandlerFunc(dosync)); - } - http.Handle("/search", http.HandlerFunc(search)); - http.Handle("/", http.HandlerFunc(serveFile)); - - // The server may have been restarted; always wait 1sec to - // give the forking server a chance to shut down and release - // the http port. - time.Sleep(1e9); - - // Start sync goroutine, if enabled. - if *syncCmd != "" && *syncMin > 0 { - syncDelay.set(*syncMin); // initial sync delay - go func() { - for { - dosync(nil, nil); - delay, _ := syncDelay.get(); - if *verbose { - log.Stderrf("next sync in %dmin", delay.(int)); - } - time.Sleep(int64(delay.(int))*60e9); - } - }(); - } - - // Start indexing goroutine. - go func() { - for { - _, ts := syncTime.get(); - if _, timestamp := searchIndex.get(); timestamp < ts { - // index possibly out of date - make a new one - // (could use a channel to send an explicit signal - // from the sync goroutine, but this solution is - // more decoupled, trivial, and works well enough) - start := time.Nanoseconds(); - index := NewIndex("."); - stop := time.Nanoseconds(); - searchIndex.set(index); - if *verbose { - secs := float64((stop-start)/1e6)/1e3; - nwords, nspots := index.Size(); - log.Stderrf("index updated (%gs, %d unique words, %d spots)", secs, nwords, nspots); - } - } - time.Sleep(1*60e9); // try once a minute +// Indexing goroutine. +func indexer() { + for { + _, ts := syncTime.get(); + if _, timestamp := searchIndex.get(); timestamp < ts { + // index possibly out of date - make a new one + // (could use a channel to send an explicit signal + // from the sync goroutine, but this solution is + // more decoupled, trivial, and works well enough) + start := time.Nanoseconds(); + index := NewIndex("."); + stop := time.Nanoseconds(); + searchIndex.set(index); + if *verbose { + secs := float64((stop-start)/1e6)/1e3; + nwords, nspots := index.Size(); + log.Stderrf("index updated (%gs, %d unique words, %d spots)", secs, nwords, nspots); } - }(); - - // Start http server. - if err := http.ListenAndServe(*httpaddr, handler); err != nil { - log.Exitf("ListenAndServe %s: %v", *httpaddr, err); } - return; - } - - if *html { - packageText = packageHtml; - parseerrorText = parseerrorHtml; - } - - info := getPageInfo(flag.Arg(0)); - - if info.PDoc != nil && flag.NArg() > 1 { - args := flag.Args(); - info.PDoc.Filter(args[1:len(args)]); - } - - if err := packageText.Execute(info, os.Stdout); err != nil { - log.Stderrf("packageText.Execute: %s", err); + time.Sleep(1*60e9); // try once a minute } } + diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go new file mode 100644 index 0000000000..b30b589987 --- /dev/null +++ b/src/cmd/godoc/main.go @@ -0,0 +1,212 @@ +// 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 + +// Web server tree: +// +// http://godoc/ main landing page +// http://godoc/doc/ serve from $GOROOT/doc - spec, mem, tutorial, etc. +// 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 compress/zlib +// godoc crypto/block Cipher NewCMAC +// - prints doc for Cipher and NewCMAC in package crypto/block + +package main + +import ( + "bytes"; + "flag"; + "fmt"; + "http"; + "io"; + "log"; + "os"; + "time"; +) + +var ( + // periodic sync + syncCmd = flag.String("sync", "", "sync command; disabled if empty"); + syncMin = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0"); + syncDelay delayTime; // actual sync delay in minutes; usually syncDelay == syncMin, but delay may back off exponentially + + // server control + httpaddr = flag.String("http", "", "HTTP service address (e.g., ':6060')"); + + // layout control + html = flag.Bool("html", false, "print HTML in command-line mode"); +) + + +func exec(c *http.Conn, args []string) bool { + r, w, err := os.Pipe(); + if err != nil { + log.Stderrf("os.Pipe(): %v\n", err); + return false; + } + + bin := args[0]; + fds := []*os.File{nil, w, w}; + if *verbose { + log.Stderrf("executing %v", args); + } + pid, err := os.ForkExec(bin, args, os.Environ(), goroot, fds); + defer r.Close(); + w.Close(); + if err != nil { + log.Stderrf("os.ForkExec(%q): %v\n", bin, err); + return false; + } + + var buf bytes.Buffer; + io.Copy(r, &buf); + wait, err := os.Wait(pid, 0); + if err != nil { + os.Stderr.Write(buf.Bytes()); + log.Stderrf("os.Wait(%d, 0): %v\n", pid, err); + return false; + } + if !wait.Exited() || wait.ExitStatus() != 0 { + os.Stderr.Write(buf.Bytes()); + log.Stderrf("executing %v failed (exit status = %d)", args, wait.ExitStatus()); + return false; + } + + if *verbose { + os.Stderr.Write(buf.Bytes()); + } + if c != nil { + c.SetHeader("content-type", "text/plain; charset=utf-8"); + c.Write(buf.Bytes()); + } + + return true; +} + + +func dosync(c *http.Conn, r *http.Request) { + args := []string{"/bin/sh", "-c", *syncCmd}; + if exec(c, args) { + // sync succeeded + syncTime.set(nil); + syncDelay.set(*syncMin); // revert to regular sync schedule + } else { + // sync failed - back off exponentially, but try at least once a day + syncDelay.backoff(24*60); + } +} + + +func usage() { + fmt.Fprintf(os.Stderr, + "usage: godoc package [name ...]\n" + " godoc -http=:6060\n"); + flag.PrintDefaults(); + os.Exit(2); +} + + +func loggingHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(c *http.Conn, req *http.Request) { + log.Stderrf("%s\t%s", c.RemoteAddr, req.Url); + h.ServeHTTP(c, req); + }); +} + + +func main() { + flag.Usage = usage; + flag.Parse(); + + // Check usage first; get usage message out early. + switch { + case *httpaddr != "": + if flag.NArg() != 0 { + usage(); + } + default: + if flag.NArg() == 0 { + usage(); + } + } + + if err := os.Chdir(goroot); err != nil { + log.Exitf("chdir %s: %v", goroot, err); + } + + readTemplates(); + + if *httpaddr != "" { + var handler http.Handler = http.DefaultServeMux; + if *verbose { + log.Stderrf("Go Documentation Server\n"); + log.Stderrf("address = %s\n", *httpaddr); + log.Stderrf("goroot = %s\n", goroot); + log.Stderrf("pkgroot = %s\n", *pkgroot); + log.Stderrf("tmplroot = %s\n", *tmplroot); + handler = loggingHandler(handler); + } + + registerPublicHandlers(http.DefaultServeMux); + if *syncCmd != "" { + http.Handle("/debug/sync", http.HandlerFunc(dosync)); + } + + // The server may have been restarted; always wait 1sec to + // give the forking server a chance to shut down and release + // the http port. + time.Sleep(1e9); + + // Start sync goroutine, if enabled. + if *syncCmd != "" && *syncMin > 0 { + syncDelay.set(*syncMin); // initial sync delay + go func() { + for { + dosync(nil, nil); + delay, _ := syncDelay.get(); + if *verbose { + log.Stderrf("next sync in %dmin", delay.(int)); + } + time.Sleep(int64(delay.(int))*60e9); + } + }(); + } + + // Start indexing goroutine. + go indexer(); + + // Start http server. + if err := http.ListenAndServe(*httpaddr, handler); err != nil { + log.Exitf("ListenAndServe %s: %v", *httpaddr, err); + } + return; + } + + if *html { + packageText = packageHtml; + parseerrorText = parseerrorHtml; + } + + info := getPageInfo(flag.Arg(0)); + + if info.PDoc != nil && flag.NArg() > 1 { + args := flag.Args(); + info.PDoc.Filter(args[1:len(args)]); + } + + if err := packageText.Execute(info, os.Stdout); err != nil { + log.Stderrf("packageText.Execute: %s", err); + } +} -- 2.50.0