cmd/doc: fix pretty printing of paths

The code to strip GOROOT and GOPATH had a bug: it assumed there
were bytes after the GOROOT prefix but there might not be.
Fix this and other issues by taking care the prefix is really a
file name prefix for the path, not just a string prefix, and
handle the case where GOROOT==path.

Change-Id: I8066865fd05f938bb6dbf3bb8ab1fc58e5cf6bb5
Reviewed-on: https://go-review.googlesource.com/15112
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go
index 40057dd..7c72b87 100644
--- a/src/cmd/doc/doc_test.go
+++ b/src/cmd/doc/doc_test.go
@@ -401,3 +401,33 @@
 		}
 	}
 }
+
+type trimTest struct {
+	path   string
+	prefix string
+	result string
+	ok     bool
+}
+
+var trimTests = []trimTest{
+	{"", "", "", true},
+	{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
+	{"/usr/gopher/bar", "/usr/gopher", "bar", true},
+	{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
+	{"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
+	{"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
+}
+
+func TestTrim(t *testing.T) {
+	for _, test := range trimTests {
+		result, ok := trim(test.path, test.prefix)
+		if ok != test.ok {
+			t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok)
+			continue
+		}
+		if result != test.result {
+			t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result)
+			continue
+		}
+	}
+}
diff --git a/src/cmd/doc/main.go b/src/cmd/doc/main.go
index bd65c17..df1890f 100644
--- a/src/cmd/doc/main.go
+++ b/src/cmd/doc/main.go
@@ -316,7 +316,6 @@
 			return path, true
 		}
 	}
-	return "", false
 }
 
 // splitGopath splits $GOPATH into a list of roots.
diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index 0aef208..f99df59 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -60,21 +60,39 @@
 	if path != "." && path != "" {
 		return path
 	}
-	// Conver the source directory into a more useful path.
-	path = filepath.Clean(pkg.build.Dir)
+	// Convert the source directory into a more useful path.
+	// Also convert everything to slash-separated paths for uniform handling.
+	path = filepath.Clean(filepath.ToSlash(pkg.build.Dir))
 	// Can we find a decent prefix?
 	goroot := filepath.Join(build.Default.GOROOT, "src")
-	if strings.HasPrefix(path, goroot) {
-		return path[len(goroot)+1:]
+	if p, ok := trim(path, filepath.ToSlash(goroot)); ok {
+		return p
 	}
 	for _, gopath := range splitGopath() {
-		if strings.HasPrefix(path, gopath) {
-			return path[len(gopath)+1:]
+		if p, ok := trim(path, filepath.ToSlash(gopath)); ok {
+			return p
 		}
 	}
 	return path
 }
 
+// trim trims the directory prefix from the path, paying attention
+// to the path separator. If they are the same string or the prefix
+// is not present the original is returned. The boolean reports whether
+// the prefix is present. That path and prefix have slashes for separators.
+func trim(path, prefix string) (string, bool) {
+	if !strings.HasPrefix(path, prefix) {
+		return path, false
+	}
+	if path == prefix {
+		return path, true
+	}
+	if path[len(prefix)] == '/' {
+		return path[len(prefix)+1:], true
+	}
+	return path, false // Textual prefix but not a path prefix.
+}
+
 // pkg.Fatalf is like log.Fatalf, but panics so it can be recovered in the
 // main do function, so it doesn't cause an exit. Allows testing to work
 // without running a subprocess. The log prefix will be added when