diff options
Diffstat (limited to 'src/cmd/godoc/utils.go')
-rw-r--r-- | src/cmd/godoc/utils.go | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go index 55cf87841..a032bd331 100644 --- a/src/cmd/godoc/utils.go +++ b/src/cmd/godoc/utils.go @@ -15,11 +15,13 @@ import ( "strings" "sync" "time" + "utf8" ) // An RWValue wraps a value and permits mutually exclusive // access to it and records the time the value was last set. +// type RWValue struct { mutex sync.RWMutex value interface{} @@ -107,3 +109,63 @@ func writeFileAtomically(filename string, data []byte) os.Error { } return os.Rename(f.Name(), filename) } + + +// isText returns true if a significant prefix of s looks like correct UTF-8; +// that is, if it is likely that s is human-readable text. +// +func isText(s []byte) bool { + const max = 1024 // at least utf8.UTFMax + if len(s) > max { + s = s[0:max] + } + for i, c := range string(s) { + if i+utf8.UTFMax > len(s) { + // last char may be incomplete - ignore + break + } + if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' { + // decoding error or control character - not a text file + return false + } + } + return true +} + + +// TODO(gri): Should have a mapping from extension to handler, eventually. + +// textExt[x] is true if the extension x indicates a text file, and false otherwise. +var textExt = map[string]bool{ + ".css": false, // must be served raw + ".js": false, // must be served raw +} + + +// isTextFile returns true if the file has a known extension indicating +// a text file, or if a significant chunk of the specified file looks like +// correct UTF-8; that is, if it is likely that the file contains human- +// readable text. +// +func isTextFile(filename string) bool { + // if the extension is known, use it for decision making + if isText, found := textExt[pathutil.Ext(filename)]; found { + return isText + } + + // the extension is not known; read an initial chunk + // of the file and check if it looks like text + f, err := os.Open(filename, os.O_RDONLY, 0) + if err != nil { + return false + } + defer f.Close() + + var buf [1024]byte + n, err := f.Read(buf[0:]) + if err != nil { + return false + } + + return isText(buf[0:n]) +} |