godoc: exclude internal packages and cmd from packages page.
Internal packages are now only included with ?m=all

Fixes golang/go#8879

LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/155910044
diff --git a/godoc/server.go b/godoc/server.go
index e81c673..0373679 100644
--- a/godoc/server.go
+++ b/godoc/server.go
@@ -36,9 +36,10 @@
 // This should probably merge into something else.
 type handlerServer struct {
 	p       *Presentation
-	c       *Corpus // copy of p.Corpus
-	pattern string  // url pattern; e.g. "/pkg/"
-	fsRoot  string  // file system root to which the pattern is mapped
+	c       *Corpus  // copy of p.Corpus
+	pattern string   // url pattern; e.g. "/pkg/"
+	fsRoot  string   // file system root to which the pattern is mapped; e.g. "/src"
+	exclude []string // file system paths to exclude; e.g. "/src/cmd"
 }
 
 func (s *handlerServer) registerWithMux(mux *http.ServeMux) {
@@ -66,7 +67,14 @@
 	ctxt := build.Default
 	ctxt.IsAbsPath = pathpkg.IsAbs
 	ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
-		return h.c.fs.ReadDir(filepath.ToSlash(dir))
+		f, err := h.c.fs.ReadDir(filepath.ToSlash(dir))
+		filtered := make([]os.FileInfo, 0, len(f))
+		for _, i := range f {
+			if mode&NoFiltering != 0 || i.Name() != "internal" {
+				filtered = append(filtered, i)
+			}
+		}
+		return filtered, err
 	}
 	ctxt.OpenFile = func(name string) (r io.ReadCloser, err error) {
 		data, err := vfs.ReadFile(h.c.fs, filepath.ToSlash(name))
@@ -188,13 +196,35 @@
 		dir = h.c.newDirectory(abspath, 1)
 		timestamp = time.Now()
 	}
-	info.Dirs = dir.listing(true)
+	info.Dirs = dir.listing(true, func(path string) bool { return h.includePath(path, mode) })
 	info.DirTime = timestamp
 	info.DirFlat = mode&FlatDir != 0
 
 	return info
 }
 
+func (h *handlerServer) includePath(path string, mode PageInfoMode) (r bool) {
+	// if the path is under one of the exclusion paths, don't list.
+	for _, e := range h.exclude {
+		if strings.HasPrefix(path, e) {
+			return false
+		}
+	}
+
+	// if the path includes 'internal', don't list unless we are in the NoFiltering mode.
+	if mode&NoFiltering != 0 {
+		return true
+	}
+	if strings.Contains(path, "internal") {
+		for _, c := range strings.Split(path, string(os.PathSeparator)) {
+			if c == "internal" {
+				return false
+			}
+		}
+	}
+	return true
+}
+
 type funcsByName []*doc.Func
 
 func (s funcsByName) Len() int           { return len(s) }