Commit 04ca4f82 authored by Gustavo Niemeyer's avatar Gustavo Niemeyer Committed by Russ Cox

path/filepath: new OS-specific path support

The path package now contains only functions which
deal with slashed paths, sensible for any OS when dealing
with network paths or URLs.  OS-specific functionality
has been moved into the new path/filepath package.

This also includes fixes for godoc, goinstall and other
packages which were mixing slashed and OS-specific paths.

R=rsc, gri, mattn, brainman
CC=golang-dev
https://golang.org/cl/4252044
parent ce65b725
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
"go/token" "go/token"
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path/filepath"
) )
...@@ -91,7 +91,7 @@ func main() { ...@@ -91,7 +91,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
if path.Ext(filename) == ".html" { if filepath.Ext(filename) == ".html" {
src = extractEBNF(src) src = extractEBNF(src)
} }
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
pathutil "path" "path/filepath"
"strings" "strings"
"unicode" "unicode"
) )
...@@ -32,7 +32,7 @@ type Directory struct { ...@@ -32,7 +32,7 @@ type Directory struct {
func isGoFile(f *os.FileInfo) bool { func isGoFile(f *os.FileInfo) bool {
return f.IsRegular() && return f.IsRegular() &&
!strings.HasPrefix(f.Name, ".") && // ignore .files !strings.HasPrefix(f.Name, ".") && // ignore .files
pathutil.Ext(f.Name) == ".go" filepath.Ext(f.Name) == ".go"
} }
...@@ -123,7 +123,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i ...@@ -123,7 +123,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
// though the directory doesn't contain any real package files - was bug) // though the directory doesn't contain any real package files - was bug)
if synopses[0] == "" { if synopses[0] == "" {
// no "optimal" package synopsis yet; continue to collect synopses // no "optimal" package synopsis yet; continue to collect synopses
file, err := parser.ParseFile(fset, pathutil.Join(path, d.Name), nil, file, err := parser.ParseFile(fset, filepath.Join(path, d.Name), nil,
parser.ParseComments|parser.PackageClauseOnly) parser.ParseComments|parser.PackageClauseOnly)
if err == nil { if err == nil {
hasPkgFiles = true hasPkgFiles = true
...@@ -156,7 +156,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i ...@@ -156,7 +156,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
i := 0 i := 0
for _, d := range list { for _, d := range list {
if isPkgDir(d) { if isPkgDir(d) {
dd := b.newDirTree(fset, pathutil.Join(path, d.Name), d.Name, depth+1) dd := b.newDirTree(fset, filepath.Join(path, d.Name), d.Name, depth+1)
if dd != nil { if dd != nil {
dirs[i] = dd dirs[i] = dd
i++ i++
......
...@@ -18,7 +18,8 @@ import ( ...@@ -18,7 +18,8 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
pathutil "path" "path"
"path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"sort" "sort"
...@@ -81,8 +82,8 @@ var ( ...@@ -81,8 +82,8 @@ var (
func initHandlers() { func initHandlers() {
fsMap.Init(*pkgPath) fsMap.Init(*pkgPath)
fileServer = http.FileServer(*goroot, "") fileServer = http.FileServer(*goroot, "")
cmdHandler = httpHandler{"/cmd/", pathutil.Join(*goroot, "src/cmd"), false} cmdHandler = httpHandler{"/cmd/", filepath.Join(*goroot, "src", "cmd"), false}
pkgHandler = httpHandler{"/pkg/", pathutil.Join(*goroot, "src/pkg"), true} pkgHandler = httpHandler{"/pkg/", filepath.Join(*goroot, "src", "pkg"), true}
} }
...@@ -97,7 +98,7 @@ func registerPublicHandlers(mux *http.ServeMux) { ...@@ -97,7 +98,7 @@ func registerPublicHandlers(mux *http.ServeMux) {
func initFSTree() { func initFSTree() {
fsTree.set(newDirectory(pathutil.Join(*goroot, *testDir), nil, -1)) fsTree.set(newDirectory(filepath.Join(*goroot, *testDir), nil, -1))
invalidateIndex() invalidateIndex()
} }
...@@ -246,27 +247,30 @@ func initDirTrees() { ...@@ -246,27 +247,30 @@ func initDirTrees() {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Path mapping // Path mapping
func absolutePath(path, defaultRoot string) string { // Absolute paths are file system paths (backslash-separated on Windows),
abspath := fsMap.ToAbsolute(path) // but relative paths are always slash-separated.
func absolutePath(relpath, defaultRoot string) string {
abspath := fsMap.ToAbsolute(relpath)
if abspath == "" { if abspath == "" {
// no user-defined mapping found; use default mapping // no user-defined mapping found; use default mapping
abspath = pathutil.Join(defaultRoot, path) abspath = filepath.Join(defaultRoot, filepath.FromSlash(relpath))
} }
return abspath return abspath
} }
func relativePath(path string) string { func relativeURL(abspath string) string {
relpath := fsMap.ToRelative(path) relpath := fsMap.ToRelative(abspath)
if relpath == "" { if relpath == "" {
// prefix must end in '/' // prefix must end in a path separator
prefix := *goroot prefix := *goroot
if len(prefix) > 0 && prefix[len(prefix)-1] != '/' { if len(prefix) > 0 && prefix[len(prefix)-1] != filepath.Separator {
prefix += "/" prefix += string(filepath.Separator)
} }
if strings.HasPrefix(path, prefix) { if strings.HasPrefix(abspath, prefix) {
// no user-defined mapping found; use default mapping // no user-defined mapping found; use default mapping
relpath = path[len(prefix):] relpath = filepath.ToSlash(abspath[len(prefix):])
} }
} }
// Only if path is an invalid absolute path is relpath == "" // Only if path is an invalid absolute path is relpath == ""
...@@ -481,7 +485,7 @@ func urlFmt(w io.Writer, format string, x ...interface{}) { ...@@ -481,7 +485,7 @@ func urlFmt(w io.Writer, format string, x ...interface{}) {
} }
// map path // map path
relpath := relativePath(path) relpath := relativeURL(path)
// convert to relative URLs so that they can also // convert to relative URLs so that they can also
// be used as relative file names in .txt templates // be used as relative file names in .txt templates
...@@ -598,7 +602,7 @@ func dirslashFmt(w io.Writer, format string, x ...interface{}) { ...@@ -598,7 +602,7 @@ func dirslashFmt(w io.Writer, format string, x ...interface{}) {
// Template formatter for "localname" format. // Template formatter for "localname" format.
func localnameFmt(w io.Writer, format string, x ...interface{}) { func localnameFmt(w io.Writer, format string, x ...interface{}) {
_, localname := pathutil.Split(x[0].(string)) _, localname := filepath.Split(x[0].(string))
template.HTMLEscape(w, []byte(localname)) template.HTMLEscape(w, []byte(localname))
} }
...@@ -630,7 +634,7 @@ var fmap = template.FormatterMap{ ...@@ -630,7 +634,7 @@ var fmap = template.FormatterMap{
func readTemplate(name string) *template.Template { func readTemplate(name string) *template.Template {
path := pathutil.Join(*goroot, "lib/godoc/"+name) path := filepath.Join(*goroot, "lib", "godoc", name)
data, err := ioutil.ReadFile(path) data, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
log.Fatalf("ReadFile %s: %v", path, err) log.Fatalf("ReadFile %s: %v", path, err)
...@@ -767,14 +771,13 @@ func applyTemplate(t *template.Template, name string, data interface{}) []byte { ...@@ -767,14 +771,13 @@ func applyTemplate(t *template.Template, name string, data interface{}) []byte {
func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
if canonical := pathutil.Clean(r.URL.Path) + "/"; r.URL.Path != canonical { if canonical := path.Clean(r.URL.Path) + "/"; r.URL.Path != canonical {
http.Redirect(w, r, canonical, http.StatusMovedPermanently) http.Redirect(w, r, canonical, http.StatusMovedPermanently)
redirected = true redirected = true
} }
return return
} }
func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) { func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
src, err := ioutil.ReadFile(abspath) src, err := ioutil.ReadFile(abspath)
if err != nil { if err != nil {
...@@ -785,7 +788,7 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit ...@@ -785,7 +788,7 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("<pre>") buf.WriteString("<pre>")
FormatText(&buf, src, 1, pathutil.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s"))) FormatText(&buf, src, 1, filepath.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s")))
buf.WriteString("</pre>") buf.WriteString("</pre>")
servePage(w, title+" "+relpath, "", "", buf.Bytes()) servePage(w, title+" "+relpath, "", "", buf.Bytes())
...@@ -822,7 +825,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) { ...@@ -822,7 +825,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
// pick off special cases and hand the rest to the standard file server // pick off special cases and hand the rest to the standard file server
switch r.URL.Path { switch r.URL.Path {
case "/": case "/":
serveHTMLDoc(w, r, pathutil.Join(*goroot, "doc/root.html"), "doc/root.html") serveHTMLDoc(w, r, filepath.Join(*goroot, "doc", "root.html"), "doc/root.html")
return return
case "/doc/root.html": case "/doc/root.html":
...@@ -831,9 +834,9 @@ func serveFile(w http.ResponseWriter, r *http.Request) { ...@@ -831,9 +834,9 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
return return
} }
switch pathutil.Ext(abspath) { switch path.Ext(relpath) {
case ".html": case ".html":
if strings.HasSuffix(abspath, "/index.html") { if strings.HasSuffix(relpath, "/index.html") {
// We'll show index.html for the directory. // We'll show index.html for the directory.
// Use the dir/ version as canonical instead of dir/index.html. // Use the dir/ version as canonical instead of dir/index.html.
http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently) http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently)
...@@ -858,8 +861,8 @@ func serveFile(w http.ResponseWriter, r *http.Request) { ...@@ -858,8 +861,8 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
if redirect(w, r) { if redirect(w, r) {
return return
} }
if index := abspath + "/index.html"; isTextFile(index) { if index := filepath.Join(abspath, "index.html"); isTextFile(index) {
serveHTMLDoc(w, r, index, relativePath(index)) serveHTMLDoc(w, r, index, relativeURL(index))
return return
} }
serveDirectory(w, r, abspath, relpath) serveDirectory(w, r, abspath, relpath)
...@@ -955,13 +958,13 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf ...@@ -955,13 +958,13 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
// the package with dirname, and the 3rd choice is a package // the package with dirname, and the 3rd choice is a package
// that is not called "main" if there is exactly one such // that is not called "main" if there is exactly one such
// package. Otherwise, don't select a package. // package. Otherwise, don't select a package.
dirpath, dirname := pathutil.Split(abspath) dirpath, dirname := filepath.Split(abspath)
// If the dirname is "go" we might be in a sub-directory for // If the dirname is "go" we might be in a sub-directory for
// .go files - use the outer directory name instead for better // .go files - use the outer directory name instead for better
// results. // results.
if dirname == "go" { if dirname == "go" {
_, dirname = pathutil.Split(pathutil.Clean(dirpath)) _, dirname = filepath.Split(filepath.Clean(dirpath))
} }
var choice3 *ast.Package var choice3 *ast.Package
...@@ -1002,7 +1005,7 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf ...@@ -1002,7 +1005,7 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
ast.PackageExports(pkg) ast.PackageExports(pkg)
} }
if mode&genDoc != 0 { if mode&genDoc != 0 {
pdoc = doc.NewPackageDoc(pkg, pathutil.Clean(relpath)) // no trailing '/' in importpath pdoc = doc.NewPackageDoc(pkg, path.Clean(relpath)) // no trailing '/' in importpath
} else { } else {
past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments) past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments)
} }
...@@ -1088,13 +1091,13 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -1088,13 +1091,13 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
title = "Package " + info.PDoc.PackageName title = "Package " + info.PDoc.PackageName
case info.PDoc.PackageName == fakePkgName: case info.PDoc.PackageName == fakePkgName:
// assume that the directory name is the command name // assume that the directory name is the command name
_, pkgname := pathutil.Split(pathutil.Clean(relpath)) _, pkgname := path.Split(path.Clean(relpath))
title = "Command " + pkgname title = "Command " + pkgname
default: default:
title = "Command " + info.PDoc.PackageName title = "Command " + info.PDoc.PackageName
} }
default: default:
title = "Directory " + relativePath(info.Dirname) title = "Directory " + relativeURL(info.Dirname)
if *showTimestamps { if *showTimestamps {
subtitle = "Last update: " + time.SecondsToLocalTime(info.DirTime).String() subtitle = "Last update: " + time.SecondsToLocalTime(info.DirTime).String()
} }
......
...@@ -47,7 +47,7 @@ import ( ...@@ -47,7 +47,7 @@ import (
"index/suffixarray" "index/suffixarray"
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path/filepath"
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
...@@ -718,7 +718,7 @@ var whitelisted = map[string]bool{ ...@@ -718,7 +718,7 @@ var whitelisted = map[string]bool{
// of "permitted" files for indexing. The filename must // of "permitted" files for indexing. The filename must
// be the directory-local name of the file. // be the directory-local name of the file.
func isWhitelisted(filename string) bool { func isWhitelisted(filename string) bool {
key := path.Ext(filename) key := filepath.Ext(filename)
if key == "" { if key == "" {
// file has no extension - use entire filename // file has no extension - use entire filename
key = filename key = filename
...@@ -732,7 +732,7 @@ func (x *Indexer) visitFile(dirname string, f *os.FileInfo, fulltextIndex bool) ...@@ -732,7 +732,7 @@ func (x *Indexer) visitFile(dirname string, f *os.FileInfo, fulltextIndex bool)
return return
} }
filename := path.Join(dirname, f.Name) filename := filepath.Join(dirname, f.Name)
goFile := false goFile := false
switch { switch {
...@@ -757,7 +757,7 @@ func (x *Indexer) visitFile(dirname string, f *os.FileInfo, fulltextIndex bool) ...@@ -757,7 +757,7 @@ func (x *Indexer) visitFile(dirname string, f *os.FileInfo, fulltextIndex bool)
if fast != nil { if fast != nil {
// we've got a Go file to index // we've got a Go file to index
x.current = file x.current = file
dir, _ := path.Split(filename) dir, _ := filepath.Split(filename)
pak := Pak{dir, fast.Name.Name} pak := Pak{dir, fast.Name.Name}
x.file = &File{filename, pak} x.file = &File{filename, pak}
ast.Walk(x, fast) ast.Walk(x, fast)
......
...@@ -36,7 +36,7 @@ import ( ...@@ -36,7 +36,7 @@ import (
"io" "io"
"log" "log"
"os" "os"
pathutil "path" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"strings" "strings"
...@@ -314,14 +314,14 @@ func main() { ...@@ -314,14 +314,14 @@ func main() {
if len(path) > 0 && path[0] == '.' { if len(path) > 0 && path[0] == '.' {
// assume cwd; don't assume -goroot // assume cwd; don't assume -goroot
cwd, _ := os.Getwd() // ignore errors cwd, _ := os.Getwd() // ignore errors
path = pathutil.Join(cwd, path) path = filepath.Join(cwd, path)
} }
relpath := path relpath := path
abspath := path abspath := path
if !pathutil.IsAbs(path) { if !filepath.IsAbs(path) {
abspath = absolutePath(path, pkgHandler.fsRoot) abspath = absolutePath(path, pkgHandler.fsRoot)
} else { } else {
relpath = relativePath(path) relpath = relativeURL(path)
} }
var mode PageInfoMode var mode PageInfoMode
...@@ -339,7 +339,7 @@ func main() { ...@@ -339,7 +339,7 @@ func main() {
if info.IsEmpty() { if info.IsEmpty() {
// try again, this time assume it's a command // try again, this time assume it's a command
if !pathutil.IsAbs(path) { if !filepath.IsAbs(path) {
abspath = absolutePath(path, cmdHandler.fsRoot) abspath = absolutePath(path, cmdHandler.fsRoot)
} }
cmdInfo := cmdHandler.getPageInfo(abspath, relpath, "", mode) cmdInfo := cmdHandler.getPageInfo(abspath, relpath, "", mode)
......
...@@ -10,7 +10,8 @@ import ( ...@@ -10,7 +10,8 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
pathutil "path" "path"
"path/filepath"
"sort" "sort"
"strings" "strings"
) )
...@@ -59,10 +60,10 @@ type mapping struct { ...@@ -59,10 +60,10 @@ type mapping struct {
} }
// Init initializes the Mapping from a list of ':'-separated // Init initializes the Mapping from a list of paths separated by
// paths. Empty paths are ignored; relative paths are assumed // filepath.ListSeparator. Empty paths are ignored; relative paths
// to be relative to the current working directory and converted // are assumed to be relative to the current working directory and
// to absolute paths. For each path of the form: // converted to absolute paths. For each path of the form:
// //
// dirname/localname // dirname/localname
// //
...@@ -71,7 +72,7 @@ type mapping struct { ...@@ -71,7 +72,7 @@ type mapping struct {
// localname -> path // localname -> path
// //
// is added to the Mapping object, in the order of occurrence. // is added to the Mapping object, in the order of occurrence.
// For instance, the argument: // For instance, under Unix, the argument:
// //
// /home/user:/home/build/public // /home/user:/home/build/public
// //
...@@ -81,12 +82,12 @@ type mapping struct { ...@@ -81,12 +82,12 @@ type mapping struct {
// public -> /home/build/public // public -> /home/build/public
// //
func (m *Mapping) Init(paths string) { func (m *Mapping) Init(paths string) {
pathlist := canonicalizePaths(strings.Split(paths, ":", -1), nil) pathlist := canonicalizePaths(filepath.SplitList(paths), nil)
list := make([]mapping, len(pathlist)) list := make([]mapping, len(pathlist))
// create mapping list // create mapping list
for i, path := range pathlist { for i, path := range pathlist {
_, prefix := pathutil.Split(path) _, prefix := filepath.Split(path)
list[i] = mapping{prefix, path, new(RWValue)} list[i] = mapping{prefix, path, new(RWValue)}
} }
...@@ -147,7 +148,7 @@ func (m *Mapping) Fprint(w io.Writer) { ...@@ -147,7 +148,7 @@ func (m *Mapping) Fprint(w io.Writer) {
func splitFirst(path string) (head, tail string) { func splitFirst(path string) (head, tail string) {
i := strings.Index(path, "/") i := strings.Index(path, string(filepath.Separator))
if i > 0 { if i > 0 {
// 0 < i < len(path) // 0 < i < len(path)
return path[0:i], path[i+1:] return path[0:i], path[i+1:]
...@@ -156,22 +157,23 @@ func splitFirst(path string) (head, tail string) { ...@@ -156,22 +157,23 @@ func splitFirst(path string) (head, tail string) {
} }
// ToAbsolute maps a relative path to an absolute path using the Mapping // ToAbsolute maps a slash-separated relative path to an absolute filesystem
// specified by the receiver. If the path cannot be mapped, the empty // path using the Mapping specified by the receiver. If the path cannot
// string is returned. // be mapped, the empty string is returned.
// //
func (m *Mapping) ToAbsolute(path string) string { func (m *Mapping) ToAbsolute(spath string) string {
prefix, tail := splitFirst(path) fpath := filepath.FromSlash(spath)
prefix, tail := splitFirst(fpath)
for _, e := range m.list { for _, e := range m.list {
switch { switch {
case e.prefix == prefix: case e.prefix == prefix:
// use tail // use tail
case e.prefix == "": case e.prefix == "":
tail = path tail = fpath
default: default:
continue // no match continue // no match
} }
abspath := pathutil.Join(e.path, tail) abspath := filepath.Join(e.path, tail)
if _, err := os.Stat(abspath); err == nil { if _, err := os.Stat(abspath); err == nil {
return abspath return abspath
} }
...@@ -181,15 +183,16 @@ func (m *Mapping) ToAbsolute(path string) string { ...@@ -181,15 +183,16 @@ func (m *Mapping) ToAbsolute(path string) string {
} }
// ToRelative maps an absolute path to a relative path using the Mapping // ToRelative maps an absolute filesystem path to a relative slash-separated
// specified by the receiver. If the path cannot be mapped, the empty // path using the Mapping specified by the receiver. If the path cannot
// string is returned. // be mapped, the empty string is returned.
// //
func (m *Mapping) ToRelative(path string) string { func (m *Mapping) ToRelative(fpath string) string {
for _, e := range m.list { for _, e := range m.list {
if strings.HasPrefix(path, e.path) { if strings.HasPrefix(fpath, e.path) {
spath := filepath.ToSlash(fpath)
// /absolute/prefix/foo -> prefix/foo // /absolute/prefix/foo -> prefix/foo
return pathutil.Join(e.prefix, path[len(e.path):]) // Join will remove a trailing '/' return path.Join(e.prefix, spath[len(e.path):]) // Join will remove a trailing '/'
} }
} }
return "" // no match return "" // no match
......
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
pathutil "path" "path/filepath"
"sort" "sort"
"strings" "strings"
"sync" "sync"
...@@ -60,10 +60,10 @@ func canonicalizePaths(list []string, filter func(path string) bool) []string { ...@@ -60,10 +60,10 @@ func canonicalizePaths(list []string, filter func(path string) bool) []string {
continue // ignore empty paths (don't assume ".") continue // ignore empty paths (don't assume ".")
} }
// len(path) > 0: normalize path // len(path) > 0: normalize path
if pathutil.IsAbs(path) { if filepath.IsAbs(path) {
path = pathutil.Clean(path) path = filepath.Clean(path)
} else { } else {
path = pathutil.Join(cwd, path) path = filepath.Join(cwd, path)
} }
// we have a non-empty absolute path // we have a non-empty absolute path
if filter != nil && !filter(path) { if filter != nil && !filter(path) {
...@@ -95,7 +95,7 @@ func canonicalizePaths(list []string, filter func(path string) bool) []string { ...@@ -95,7 +95,7 @@ func canonicalizePaths(list []string, filter func(path string) bool) []string {
// atomically renames that file to the file named by filename. // atomically renames that file to the file named by filename.
// //
func writeFileAtomically(filename string, data []byte) os.Error { func writeFileAtomically(filename string, data []byte) os.Error {
f, err := ioutil.TempFile(pathutil.Split(filename)) f, err := ioutil.TempFile(filepath.Split(filename))
if err != nil { if err != nil {
return err return err
} }
...@@ -149,7 +149,7 @@ var textExt = map[string]bool{ ...@@ -149,7 +149,7 @@ var textExt = map[string]bool{
// //
func isTextFile(filename string) bool { func isTextFile(filename string) bool {
// if the extension is known, use it for decision making // if the extension is known, use it for decision making
if isText, found := textExt[pathutil.Ext(filename)]; found { if isText, found := textExt[filepath.Ext(filename)]; found {
return isText return isText
} }
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
"go/token" "go/token"
"io/ioutil" "io/ioutil"
"os" "os"
pathutil "path" "path/filepath"
"strings" "strings"
) )
...@@ -181,7 +181,7 @@ func walkDir(path string) { ...@@ -181,7 +181,7 @@ func walkDir(path string) {
done <- true done <- true
}() }()
// walk the tree // walk the tree
pathutil.Walk(path, v, v) filepath.Walk(path, v, v)
close(v) // terminate error handler loop close(v) // terminate error handler loop
<-done // wait for all errors to be reported <-done // wait for all errors to be reported
} }
......
...@@ -9,7 +9,7 @@ package main ...@@ -9,7 +9,7 @@ package main
import ( import (
"http" "http"
"os" "os"
"path" "path/filepath"
"regexp" "regexp"
"strings" "strings"
) )
...@@ -42,7 +42,7 @@ func download(pkg string) (string, os.Error) { ...@@ -42,7 +42,7 @@ func download(pkg string) (string, os.Error) {
return "", os.ErrorString("invalid path (contains ..)") return "", os.ErrorString("invalid path (contains ..)")
} }
if m := bitbucket.FindStringSubmatch(pkg); m != nil { if m := bitbucket.FindStringSubmatch(pkg); m != nil {
if err := vcsCheckout(&hg, root+m[1], "http://"+m[1], m[1]); err != nil { if err := vcsCheckout(&hg, m[1], "http://"+m[1], m[1]); err != nil {
return "", err return "", err
} }
return root + pkg, nil return root + pkg, nil
...@@ -58,7 +58,7 @@ func download(pkg string) (string, os.Error) { ...@@ -58,7 +58,7 @@ func download(pkg string) (string, os.Error) {
// regexp only allows hg, svn to get through // regexp only allows hg, svn to get through
panic("missing case in download: " + pkg) panic("missing case in download: " + pkg)
} }
if err := vcsCheckout(v, root+m[1], "https://"+m[1], m[1]); err != nil { if err := vcsCheckout(v, m[1], "https://"+m[1], m[1]); err != nil {
return "", err return "", err
} }
return root + pkg, nil return root + pkg, nil
...@@ -67,7 +67,7 @@ func download(pkg string) (string, os.Error) { ...@@ -67,7 +67,7 @@ func download(pkg string) (string, os.Error) {
if strings.HasSuffix(m[1], ".git") { if strings.HasSuffix(m[1], ".git") {
return "", os.ErrorString("repository " + pkg + " should not have .git suffix") return "", os.ErrorString("repository " + pkg + " should not have .git suffix")
} }
if err := vcsCheckout(&git, root+m[1], "http://"+m[1]+".git", m[1]); err != nil { if err := vcsCheckout(&git, m[1], "http://"+m[1]+".git", m[1]); err != nil {
return "", err return "", err
} }
return root + pkg, nil return root + pkg, nil
...@@ -75,7 +75,7 @@ func download(pkg string) (string, os.Error) { ...@@ -75,7 +75,7 @@ func download(pkg string) (string, os.Error) {
if m := launchpad.FindStringSubmatch(pkg); m != nil { if m := launchpad.FindStringSubmatch(pkg); m != nil {
// Either lp.net/<project>[/<series>[/<path>]] // Either lp.net/<project>[/<series>[/<path>]]
// or lp.net/~<user or team>/<project>/<branch>[/<path>] // or lp.net/~<user or team>/<project>/<branch>[/<path>]
if err := vcsCheckout(&bzr, root+m[1], "https://"+m[1], m[1]); err != nil { if err := vcsCheckout(&bzr, m[1], "https://"+m[1], m[1]); err != nil {
return "", err return "", err
} }
return root + pkg, nil return root + pkg, nil
...@@ -172,17 +172,18 @@ func (v *vcs) updateRepo(dst string) os.Error { ...@@ -172,17 +172,18 @@ func (v *vcs) updateRepo(dst string) os.Error {
// exists and -u was specified on the command line) // exists and -u was specified on the command line)
// the repository at tag/branch "release". If there is no // the repository at tag/branch "release". If there is no
// such tag or branch, it falls back to the repository tip. // such tag or branch, it falls back to the repository tip.
func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error { func vcsCheckout(vcs *vcs, pkgprefix, repo, dashpath string) os.Error {
dir, err := os.Stat(dst + "/" + vcs.metadir) dst := filepath.Join(root, filepath.FromSlash(pkgprefix))
dir, err := os.Stat(filepath.Join(dst, vcs.metadir))
if err == nil && !dir.IsDirectory() { if err == nil && !dir.IsDirectory() {
return os.ErrorString("not a directory: " + dst) return os.ErrorString("not a directory: " + dst)
} }
if err != nil { if err != nil {
parent, _ := path.Split(dst) parent, _ := filepath.Split(dst)
if err := os.MkdirAll(parent, 0777); err != nil { if err := os.MkdirAll(parent, 0777); err != nil {
return err return err
} }
if err := run("/", nil, vcs.cmd, vcs.clone, repo, dst); err != nil { if err := run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
return err return err
} }
if err := vcs.updateRepo(dst); err != nil { if err := vcs.updateRepo(dst); err != nil {
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path/filepath"
"runtime" "runtime"
"strings" "strings"
) )
...@@ -34,7 +34,7 @@ var ( ...@@ -34,7 +34,7 @@ var (
parents = make(map[string]string) parents = make(map[string]string)
root = runtime.GOROOT() root = runtime.GOROOT()
visit = make(map[string]status) visit = make(map[string]status)
logfile = path.Join(root, "goinstall.log") logfile = filepath.Join(root, "goinstall.log")
installedPkgs = make(map[string]bool) installedPkgs = make(map[string]bool)
allpkg = flag.Bool("a", false, "install all previously installed packages") allpkg = flag.Bool("a", false, "install all previously installed packages")
...@@ -59,7 +59,7 @@ func main() { ...@@ -59,7 +59,7 @@ func main() {
fmt.Fprintf(os.Stderr, "%s: no $GOROOT\n", argv0) fmt.Fprintf(os.Stderr, "%s: no $GOROOT\n", argv0)
os.Exit(1) os.Exit(1)
} }
root += "/src/pkg/" root += filepath.FromSlash("/src/pkg/")
// special case - "unsafe" is already installed // special case - "unsafe" is already installed
visit["unsafe"] = done visit["unsafe"] = done
...@@ -160,7 +160,7 @@ func install(pkg, parent string) { ...@@ -160,7 +160,7 @@ func install(pkg, parent string) {
dir = pkg dir = pkg
local = true local = true
} else if isStandardPath(pkg) { } else if isStandardPath(pkg) {
dir = path.Join(root, pkg) dir = filepath.Join(root, filepath.FromSlash(pkg))
local = true local = true
} else { } else {
var err os.Error var err os.Error
...@@ -216,7 +216,8 @@ func install(pkg, parent string) { ...@@ -216,7 +216,8 @@ func install(pkg, parent string) {
// Is this a local path? /foo ./foo ../foo . .. // Is this a local path? /foo ./foo ../foo . ..
func isLocalPath(s string) bool { func isLocalPath(s string) bool {
return strings.HasPrefix(s, "/") || strings.HasPrefix(s, "./") || strings.HasPrefix(s, "../") || s == "." || s == ".." const sep = string(filepath.Separator)
return strings.HasPrefix(s, sep) || strings.HasPrefix(s, "."+sep) || strings.HasPrefix(s, ".."+sep) || s == "." || s == ".."
} }
// Is this a standard package path? strings container/vector etc. // Is this a standard package path? strings container/vector etc.
......
...@@ -7,13 +7,13 @@ ...@@ -7,13 +7,13 @@
package main package main
import ( import (
"path"
"os"
"log"
"strings"
"strconv"
"go/ast" "go/ast"
"go/parser" "go/parser"
"log"
"os"
"path/filepath"
"strconv"
"strings"
) )
...@@ -64,7 +64,7 @@ func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) { ...@@ -64,7 +64,7 @@ func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) {
if !strings.HasSuffix(d.Name, ".go") || strings.HasSuffix(d.Name, "_test.go") { if !strings.HasSuffix(d.Name, ".go") || strings.HasSuffix(d.Name, "_test.go") {
continue continue
} }
filename := path.Join(dir, d.Name) filename := filepath.Join(dir, d.Name)
pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly) pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
"go/parser" "go/parser"
"go/token" "go/token"
"os" "os"
"path" "path/filepath"
"strconv" "strconv"
"strings" "strings"
) )
...@@ -99,7 +99,7 @@ func doFile(name string, reader io.Reader) { ...@@ -99,7 +99,7 @@ func doFile(name string, reader io.Reader) {
file.checkFile(name, parsedFile) file.checkFile(name, parsedFile)
} }
// Visitor for path.Walk - trivial. Just calls doFile on each file. // Visitor for filepath.Walk - trivial. Just calls doFile on each file.
// TODO: if govet becomes richer, might want to process // TODO: if govet becomes richer, might want to process
// a directory (package) at a time. // a directory (package) at a time.
type V struct{} type V struct{}
...@@ -124,7 +124,7 @@ func walkDir(root string) { ...@@ -124,7 +124,7 @@ func walkDir(root string) {
} }
done <- true done <- true
}() }()
path.Walk(root, V{}, errors) filepath.Walk(root, V{}, errors)
close(errors) close(errors)
<-done <-done
} }
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"patch" "patch"
"path" "path/filepath"
"sort" "sort"
"strings" "strings"
) )
...@@ -186,7 +186,7 @@ func main() { ...@@ -186,7 +186,7 @@ func main() {
// make parent directory for name, if necessary // make parent directory for name, if necessary
func makeParent(name string) { func makeParent(name string) {
parent, _ := path.Split(name) parent, _ := filepath.Split(name)
chk(mkdirAll(parent, 0755)) chk(mkdirAll(parent, 0755))
} }
......
...@@ -118,6 +118,7 @@ DIRS=\ ...@@ -118,6 +118,7 @@ DIRS=\
os/signal\ os/signal\
patch\ patch\
path\ path\
path/filepath\
rand\ rand\
reflect\ reflect\
regexp\ regexp\
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
pathutil "path" "path/filepath"
) )
...@@ -198,7 +198,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool, ...@@ -198,7 +198,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool,
for i := 0; i < len(list); i++ { for i := 0; i < len(list); i++ {
d := &list[i] d := &list[i]
if filter == nil || filter(d) { if filter == nil || filter(d) {
filenames[n] = pathutil.Join(path, d.Name) filenames[n] = filepath.Join(path, d.Name)
n++ n++
} }
} }
......
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
"go/token" "go/token"
"io" "io"
"os" "os"
"path" "path/filepath"
"runtime" "runtime"
"tabwriter" "tabwriter"
) )
...@@ -244,7 +244,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) { ...@@ -244,7 +244,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
} }
if debug { if debug {
// do not update p.pos - use write0 // do not update p.pos - use write0
_, filename := path.Split(pos.Filename) _, filename := filepath.Split(pos.Filename)
p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column))) p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column)))
} }
p.write(data) p.write(data)
......
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"path" "path/filepath"
"testing" "testing"
) )
...@@ -129,8 +129,8 @@ var data = []entry{ ...@@ -129,8 +129,8 @@ var data = []entry{
func TestFiles(t *testing.T) { func TestFiles(t *testing.T) {
for _, e := range data { for _, e := range data {
source := path.Join(dataDir, e.source) source := filepath.Join(dataDir, e.source)
golden := path.Join(dataDir, e.golden) golden := filepath.Join(dataDir, e.golden)
check(t, source, golden, e.mode) check(t, source, golden, e.mode)
// TODO(gri) check that golden is idempotent // TODO(gri) check that golden is idempotent
//check(t, golden, golden, e.mode); //check(t, golden, golden, e.mode);
......
...@@ -23,7 +23,7 @@ package scanner ...@@ -23,7 +23,7 @@ package scanner
import ( import (
"bytes" "bytes"
"go/token" "go/token"
"path" "path/filepath"
"strconv" "strconv"
"unicode" "unicode"
"utf8" "utf8"
...@@ -118,7 +118,7 @@ func (S *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode uint ...@@ -118,7 +118,7 @@ func (S *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode uint
panic("file size does not match src len") panic("file size does not match src len")
} }
S.file = file S.file = file
S.dir, _ = path.Split(file.Name()) S.dir, _ = filepath.Split(file.Name())
S.src = src S.src = src
S.err = err S.err = err
S.mode = mode S.mode = mode
...@@ -180,10 +180,10 @@ func (S *Scanner) interpretLineComment(text []byte) { ...@@ -180,10 +180,10 @@ func (S *Scanner) interpretLineComment(text []byte) {
if i := bytes.Index(text, []byte{':'}); i > 0 { if i := bytes.Index(text, []byte{':'}); i > 0 {
if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 { if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 {
// valid //line filename:line comment; // valid //line filename:line comment;
filename := path.Clean(string(text[len(prefix):i])) filename := filepath.Clean(string(text[len(prefix):i]))
if filename[0] != '/' { if filename[0] != '/' {
// make filename relative to current directory // make filename relative to current directory
filename = path.Join(S.dir, filename) filename = filepath.Join(S.dir, filename)
} }
// update scanner position // update scanner position
S.file.AddLineInfo(S.lineOffset, filename, line-1) // -1 since comment applies to next line S.file.AddLineInfo(S.lineOffset, filename, line-1) // -1 since comment applies to next line
......
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
"io" "io"
"mime" "mime"
"os" "os"
"path" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "time"
...@@ -112,7 +112,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { ...@@ -112,7 +112,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
// use contents of index.html for directory, if present // use contents of index.html for directory, if present
if d.IsDirectory() { if d.IsDirectory() {
index := name + indexPage index := name + filepath.FromSlash(indexPage)
ff, err := os.Open(index, os.O_RDONLY, 0) ff, err := os.Open(index, os.O_RDONLY, 0)
if err == nil { if err == nil {
defer ff.Close() defer ff.Close()
...@@ -135,7 +135,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { ...@@ -135,7 +135,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
code := StatusOK code := StatusOK
// use extension to find content type. // use extension to find content type.
ext := path.Ext(name) ext := filepath.Ext(name)
if ctype := mime.TypeByExtension(ext); ctype != "" { if ctype := mime.TypeByExtension(ext); ctype != "" {
w.SetHeader("Content-Type", ctype) w.SetHeader("Content-Type", ctype)
} else { } else {
...@@ -202,7 +202,7 @@ func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { ...@@ -202,7 +202,7 @@ func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
return return
} }
path = path[len(f.prefix):] path = path[len(f.prefix):]
serveFile(w, r, f.root+"/"+path, true) serveFile(w, r, filepath.Join(f.root, filepath.FromSlash(path)), true)
} }
// httpRange specifies the byte range to be sent to the client. // httpRange specifies the byte range to be sent to the client.
......
...@@ -6,6 +6,7 @@ package ioutil ...@@ -6,6 +6,7 @@ package ioutil
import ( import (
"os" "os"
"path/filepath"
"strconv" "strconv"
) )
...@@ -46,8 +47,7 @@ func TempFile(dir, prefix string) (f *os.File, err os.Error) { ...@@ -46,8 +47,7 @@ func TempFile(dir, prefix string) (f *os.File, err os.Error) {
nconflict := 0 nconflict := 0
for i := 0; i < 10000; i++ { for i := 0; i < 10000; i++ {
// TODO(rsc): use filepath.Join name := filepath.Join(dir, prefix+nextSuffix())
name := dir + "/" + prefix + nextSuffix()
f, err = os.Open(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) f, err = os.Open(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if pe, ok := err.(*os.PathError); ok && pe.Error == os.EEXIST { if pe, ok := err.(*os.PathError); ok && pe.Error == os.EEXIST {
if nconflict++; nconflict > 10 { if nconflict++; nconflict > 10 {
...@@ -74,8 +74,7 @@ func TempDir(dir, prefix string) (name string, err os.Error) { ...@@ -74,8 +74,7 @@ func TempDir(dir, prefix string) (name string, err os.Error) {
nconflict := 0 nconflict := 0
for i := 0; i < 10000; i++ { for i := 0; i < 10000; i++ {
// TODO(rsc): use filepath.Join try := filepath.Join(dir, prefix+nextSuffix())
try := dir + "/" + prefix + nextSuffix()
err = os.Mkdir(try, 0700) err = os.Mkdir(try, 0700)
if pe, ok := err.(*os.PathError); ok && pe.Error == os.EEXIST { if pe, ok := err.(*os.PathError); ok && pe.Error == os.EEXIST {
if nconflict++; nconflict > 10 { if nconflict++; nconflict > 10 {
......
...@@ -9,18 +9,6 @@ GOFILES=\ ...@@ -9,18 +9,6 @@ GOFILES=\
match.go\ match.go\
path.go\ path.go\
GOFILES_freebsd=\
path_unix.go
GOFILES_darwin=\
path_unix.go
GOFILES_linux=\
path_unix.go
GOFILES_windows=\
path_windows.go
GOFILES+=$(GOFILES_$(GOOS)) GOFILES+=$(GOFILES_$(GOOS))
include ../../Make.pkg include ../../Make.pkg
# 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.
include ../../../Make.inc
TARG=path/filepath
GOFILES=\
match.go\
path.go\
GOFILES_freebsd=\
path_unix.go
GOFILES_darwin=\
path_unix.go
GOFILES_linux=\
path_unix.go
GOFILES_windows=\
path_unix.go
GOFILES+=$(GOFILES_$(GOOS))
include ../../../Make.pkg
// Copyright 2010 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.
package filepath
import (
"os"
"sort"
"strings"
"utf8"
)
var ErrBadPattern = os.NewError("syntax error in pattern")
// Match returns true if name matches the shell file name pattern.
// The pattern syntax is:
//
// pattern:
// { term }
// term:
// '*' matches any sequence of non-Separator characters
// '?' matches any single non-Separator character
// '[' [ '^' ] { character-range } ']'
// character class (must be non-empty)
// c matches character c (c != '*', '?', '\\', '[')
// '\\' c matches character c
//
// character-range:
// c matches character c (c != '\\', '-', ']')
// '\\' c matches character c
// lo '-' hi matches character c for lo <= c <= hi
//
// Match requires pattern to match all of name, not just a substring.
// The only possible error return is when pattern is malformed.
//
func Match(pattern, name string) (matched bool, err os.Error) {
Pattern:
for len(pattern) > 0 {
var star bool
var chunk string
star, chunk, pattern = scanChunk(pattern)
if star && chunk == "" {
// Trailing * matches rest of string unless it has a /.
return strings.Index(name, string(Separator)) < 0, nil
}
// Look for match at current position.
t, ok, err := matchChunk(chunk, name)
// if we're the last chunk, make sure we've exhausted the name
// otherwise we'll give a false result even if we could still match
// using the star
if ok && (len(t) == 0 || len(pattern) > 0) {
name = t
continue
}
if err != nil {
return false, err
}
if star {
// Look for match skipping i+1 bytes.
// Cannot skip /.
for i := 0; i < len(name) && name[i] != Separator; i++ {
t, ok, err := matchChunk(chunk, name[i+1:])
if ok {
// if we're the last chunk, make sure we exhausted the name
if len(pattern) == 0 && len(t) > 0 {
continue
}
name = t
continue Pattern
}
if err != nil {
return false, err
}
}
}
return false, nil
}
return len(name) == 0, nil
}
// scanChunk gets the next segment of pattern, which is a non-star string
// possibly preceded by a star.
func scanChunk(pattern string) (star bool, chunk, rest string) {
for len(pattern) > 0 && pattern[0] == '*' {
pattern = pattern[1:]
star = true
}
inrange := false
var i int
Scan:
for i = 0; i < len(pattern); i++ {
switch pattern[i] {
case '\\':
// error check handled in matchChunk: bad pattern.
if i+1 < len(pattern) {
i++
}
case '[':
inrange = true
case ']':
inrange = false
case '*':
if !inrange {
break Scan
}
}
}
return star, pattern[0:i], pattern[i:]
}
// matchChunk checks whether chunk matches the beginning of s.
// If so, it returns the remainder of s (after the match).
// Chunk is all single-character operators: literals, char classes, and ?.
func matchChunk(chunk, s string) (rest string, ok bool, err os.Error) {
for len(chunk) > 0 {
if len(s) == 0 {
return
}
switch chunk[0] {
case '[':
// character class
r, n := utf8.DecodeRuneInString(s)
s = s[n:]
chunk = chunk[1:]
// possibly negated
notNegated := true
if len(chunk) > 0 && chunk[0] == '^' {
notNegated = false
chunk = chunk[1:]
}
// parse all ranges
match := false
nrange := 0
for {
if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 {
chunk = chunk[1:]
break
}
var lo, hi int
if lo, chunk, err = getEsc(chunk); err != nil {
return
}
hi = lo
if chunk[0] == '-' {
if hi, chunk, err = getEsc(chunk[1:]); err != nil {
return
}
}
if lo <= r && r <= hi {
match = true
}
nrange++
}
if match != notNegated {
return
}
case '?':
if s[0] == Separator {
return
}
_, n := utf8.DecodeRuneInString(s)
s = s[n:]
chunk = chunk[1:]
case '\\':
chunk = chunk[1:]
if len(chunk) == 0 {
err = ErrBadPattern
return
}
fallthrough
default:
if chunk[0] != s[0] {
return
}
s = s[1:]
chunk = chunk[1:]
}
}
return s, true, nil
}
// getEsc gets a possibly-escaped character from chunk, for a character class.
func getEsc(chunk string) (r int, nchunk string, err os.Error) {
if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' {
err = ErrBadPattern
return
}
if chunk[0] == '\\' {
chunk = chunk[1:]
if len(chunk) == 0 {
err = ErrBadPattern
return
}
}
r, n := utf8.DecodeRuneInString(chunk)
if r == utf8.RuneError && n == 1 {
err = ErrBadPattern
}
nchunk = chunk[n:]
if len(nchunk) == 0 {
err = ErrBadPattern
}
return
}
// Glob returns the names of all files matching pattern or nil
// if there is no matching file. The syntax of patterns is the same
// as in Match. The pattern may describe hierarchical names such as
// /usr/*/bin/ed (assuming the Separator is '/').
//
func Glob(pattern string) (matches []string) {
if !hasMeta(pattern) {
if _, err := os.Stat(pattern); err == nil {
return []string{pattern}
}
return nil
}
dir, file := Split(pattern)
switch dir {
case "":
dir = "."
case string(Separator):
// nothing
default:
dir = dir[0 : len(dir)-1] // chop off trailing separator
}
if hasMeta(dir) {
for _, d := range Glob(dir) {
matches = glob(d, file, matches)
}
} else {
return glob(dir, file, nil)
}
return matches
}
// glob searches for files matching pattern in the directory dir
// and appends them to matches.
func glob(dir, pattern string, matches []string) []string {
fi, err := os.Stat(dir)
if err != nil {
return nil
}
if !fi.IsDirectory() {
return matches
}
d, err := os.Open(dir, os.O_RDONLY, 0666)
if err != nil {
return nil
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
return nil
}
sort.SortStrings(names)
for _, n := range names {
matched, err := Match(pattern, n)
if err != nil {
return matches
}
if matched {
matches = append(matches, Join(dir, n))
}
}
return matches
}
// hasMeta returns true if path contains any of the magic characters
// recognized by Match.
func hasMeta(path string) bool {
// TODO(niemeyer): Should other magic characters be added here?
return strings.IndexAny(path, "*?[") >= 0
}
// 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.
package filepath_test
import (
"os"
"path/filepath"
"testing"
)
type MatchTest struct {
pattern, s string
match bool
err os.Error
}
var matchTests = []MatchTest{
{"abc", "abc", true, nil},
{"*", "abc", true, nil},
{"*c", "abc", true, nil},
{"a*", "a", true, nil},
{"a*", "abc", true, nil},
{"a*", "ab/c", false, nil},
{"a*/b", "abc/b", true, nil},
{"a*/b", "a/c/b", false, nil},
{"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil},
{"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil},
{"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil},
{"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil},
{"a*b?c*x", "abxbbxdbxebxczzx", true, nil},
{"a*b?c*x", "abxbbxdbxebxczzy", false, nil},
{"ab[c]", "abc", true, nil},
{"ab[b-d]", "abc", true, nil},
{"ab[e-g]", "abc", false, nil},
{"ab[^c]", "abc", false, nil},
{"ab[^b-d]", "abc", false, nil},
{"ab[^e-g]", "abc", true, nil},
{"a\\*b", "a*b", true, nil},
{"a\\*b", "ab", false, nil},
{"a?b", "a☺b", true, nil},
{"a[^a]b", "a☺b", true, nil},
{"a???b", "a☺b", false, nil},
{"a[^a][^a][^a]b", "a☺b", false, nil},
{"[a-ζ]*", "α", true, nil},
{"*[a-ζ]", "A", false, nil},
{"a?b", "a/b", false, nil},
{"a*b", "a/b", false, nil},
{"[\\]a]", "]", true, nil},
{"[\\-]", "-", true, nil},
{"[x\\-]", "x", true, nil},
{"[x\\-]", "-", true, nil},
{"[x\\-]", "z", false, nil},
{"[\\-x]", "x", true, nil},
{"[\\-x]", "-", true, nil},
{"[\\-x]", "a", false, nil},
{"[]a]", "]", false, filepath.ErrBadPattern},
{"[-]", "-", false, filepath.ErrBadPattern},
{"[x-]", "x", false, filepath.ErrBadPattern},
{"[x-]", "-", false, filepath.ErrBadPattern},
{"[x-]", "z", false, filepath.ErrBadPattern},
{"[-x]", "x", false, filepath.ErrBadPattern},
{"[-x]", "-", false, filepath.ErrBadPattern},
{"[-x]", "a", false, filepath.ErrBadPattern},
{"\\", "a", false, filepath.ErrBadPattern},
{"[a-b-c]", "a", false, filepath.ErrBadPattern},
{"*x", "xxx", true, nil},
}
func TestMatch(t *testing.T) {
for _, tt := range matchTests {
ok, err := filepath.Match(tt.pattern, tt.s)
if ok != tt.match || err != tt.err {
t.Errorf("Match(%#q, %#q) = %v, %v want %v, nil", tt.pattern, tt.s, ok, err, tt.match)
}
}
}
// contains returns true if vector contains the string s.
func contains(vector []string, s string) bool {
for _, elem := range vector {
if elem == s {
return true
}
}
return false
}
var globTests = []struct {
pattern, result string
}{
{"match.go", "match.go"},
{"mat?h.go", "match.go"},
{"*", "match.go"},
{"../*/match.go", "../filepath/match.go"},
}
func TestGlob(t *testing.T) {
for _, tt := range globTests {
matches := filepath.Glob(tt.pattern)
if !contains(matches, tt.result) {
t.Errorf("Glob(%#q) = %#v want %v", tt.pattern, matches, tt.result)
}
}
}
// 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.
// The filepath package implements utility routines for manipulating
// filename paths in a way compatible with the target operating
// system-defined file paths.
package filepath
import (
"os"
"sort"
"strings"
)
// BUG(niemeyer): Package filepath does not yet work on Windows.
// Clean returns the shortest path name equivalent to path
// by purely lexical processing. It applies the following rules
// iteratively until no further processing can be done:
//
// 1. Replace multiple Separator elements with a single one.
// 2. Eliminate each . path name element (the current directory).
// 3. Eliminate each inner .. path name element (the parent directory)
// along with the non-.. element that precedes it.
// 4. Eliminate .. elements that begin a rooted path:
// that is, replace "/.." by "/" at the beginning of a path,
// assuming Separator is '/'.
//
// If the result of this process is an empty string, Clean
// returns the string ".".
//
// See also Rob Pike, ``Lexical File Names in Plan 9 or
// Getting Dot-Dot right,''
// http://plan9.bell-labs.com/sys/doc/lexnames.html
func Clean(path string) string {
if path == "" {
return "."
}
rooted := path[0] == Separator
n := len(path)
// Invariants:
// reading from path; r is index of next byte to process.
// writing to buf; w is index of next byte to write.
// dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix.
buf := []byte(path)
r, w, dotdot := 0, 0, 0
if rooted {
r, w, dotdot = 1, 1, 1
}
for r < n {
switch {
case path[r] == Separator:
// empty path element
r++
case path[r] == '.' && (r+1 == n || path[r+1] == Separator):
// . element
r++
case path[r] == '.' && path[r+1] == '.' && (r+2 == n || path[r+2] == Separator):
// .. element: remove to last separator
r += 2
switch {
case w > dotdot:
// can backtrack
w--
for w > dotdot && buf[w] != Separator {
w--
}
case !rooted:
// cannot backtrack, but not rooted, so append .. element.
if w > 0 {
buf[w] = Separator
w++
}
buf[w] = '.'
w++
buf[w] = '.'
w++
dotdot = w
}
default:
// real path element.
// add slash if needed
if rooted && w != 1 || !rooted && w != 0 {
buf[w] = Separator
w++
}
// copy element
for ; r < n && path[r] != Separator; r++ {
buf[w] = path[r]
w++
}
}
}
// Turn empty string into "."
if w == 0 {
buf[w] = '.'
w++
}
return string(buf[0:w])
}
// ToSlash returns the result of replacing each separator character
// in path with a slash ('/') character.
func ToSlash(path string) string {
if Separator == '/' {
return path
}
return strings.Replace(path, string(Separator), "/", -1)
}
// FromSlash returns the result of replacing each slash ('/') character
// in path with a separator character.
func FromSlash(path string) string {
if Separator == '/' {
return path
}
return strings.Replace(path, "/", string(Separator), -1)
}
// SplitList splits a list of paths joined by the OS-specific ListSeparator.
func SplitList(path string) []string {
if path == "" {
return []string{}
}
return strings.Split(path, string(ListSeparator), -1)
}
// Split splits path immediately following the final Separator,
// partitioning it into a directory and a file name components.
// If there are no separators in path, Split returns an empty base
// and file set to path.
func Split(path string) (dir, file string) {
i := strings.LastIndex(path, string(Separator))
return path[:i+1], path[i+1:]
}
// Join joins any number of path elements into a single path, adding
// a Separator if necessary. All empty strings are ignored.
func Join(elem ...string) string {
for i, e := range elem {
if e != "" {
return Clean(strings.Join(elem[i:], string(Separator)))
}
}
return ""
}
// Ext returns the file name extension used by path.
// The extension is the suffix beginning at the final dot
// in the final element of path; it is empty if there is
// no dot.
func Ext(path string) string {
for i := len(path) - 1; i >= 0 && path[i] != Separator; i-- {
if path[i] == '.' {
return path[i:]
}
}
return ""
}
// Visitor methods are invoked for corresponding file tree entries
// visited by Walk. The parameter path is the full path of f relative
// to root.
type Visitor interface {
VisitDir(path string, f *os.FileInfo) bool
VisitFile(path string, f *os.FileInfo)
}
func walk(path string, f *os.FileInfo, v Visitor, errors chan<- os.Error) {
if !f.IsDirectory() {
v.VisitFile(path, f)
return
}
if !v.VisitDir(path, f) {
return // skip directory entries
}
list, err := readDir(path)
if err != nil {
if errors != nil {
errors <- err
}
}
for _, e := range list {
walk(Join(path, e.Name), e, v, errors)
}
}
// readDir reads the directory named by dirname and returns
// a list of sorted directory entries.
// Copied from io/ioutil to avoid the circular import.
func readDir(dirname string) ([]*os.FileInfo, os.Error) {
f, err := os.Open(dirname, os.O_RDONLY, 0)
if err != nil {
return nil, err
}
list, err := f.Readdir(-1)
f.Close()
if err != nil {
return nil, err
}
fi := make(fileInfoList, len(list))
for i := range list {
fi[i] = &list[i]
}
sort.Sort(fi)
return fi, nil
}
// A dirList implements sort.Interface.
type fileInfoList []*os.FileInfo
func (f fileInfoList) Len() int { return len(f) }
func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name }
func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
// Walk walks the file tree rooted at root, calling v.VisitDir or
// v.VisitFile for each directory or file in the tree, including root.
// If v.VisitDir returns false, Walk skips the directory's entries;
// otherwise it invokes itself for each directory entry in sorted order.
// An error reading a directory does not abort the Walk.
// If errors != nil, Walk sends each directory read error
// to the channel. Otherwise Walk discards the error.
func Walk(root string, v Visitor, errors chan<- os.Error) {
f, err := os.Lstat(root)
if err != nil {
if errors != nil {
errors <- err
}
return // can't progress
}
walk(root, f, v, errors)
}
// Base returns the last element of path.
// Trailing path separators are removed before extracting the last element.
// If the path is empty, Base returns ".".
// If the path consists entirely of separators, Base returns a single separator.
func Base(path string) string {
if path == "" {
return "."
}
// Strip trailing slashes.
for len(path) > 0 && path[len(path)-1] == Separator {
path = path[0 : len(path)-1]
}
// Find the last element
if i := strings.LastIndex(path, string(Separator)); i >= 0 {
path = path[i+1:]
}
// If empty now, it had only slashes.
if path == "" {
return string(Separator)
}
return path
}
// IsAbs returns true if the path is absolute.
func IsAbs(path string) bool {
return len(path) > 0 && path[0] == Separator
}
// 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.
package filepath_test
import (
"os"
"path/filepath"
"reflect"
"testing"
)
type PathTest struct {
path, result string
}
var cleantests = []PathTest{
// Already clean
{"", "."},
{"abc", "abc"},
{"abc/def", "abc/def"},
{"a/b/c", "a/b/c"},
{".", "."},
{"..", ".."},
{"../..", "../.."},
{"../../abc", "../../abc"},
{"/abc", "/abc"},
{"/", "/"},
// Remove trailing slash
{"abc/", "abc"},
{"abc/def/", "abc/def"},
{"a/b/c/", "a/b/c"},
{"./", "."},
{"../", ".."},
{"../../", "../.."},
{"/abc/", "/abc"},
// Remove doubled slash
{"abc//def//ghi", "abc/def/ghi"},
{"//abc", "/abc"},
{"///abc", "/abc"},
{"//abc//", "/abc"},
{"abc//", "abc"},
// Remove . elements
{"abc/./def", "abc/def"},
{"/./abc/def", "/abc/def"},
{"abc/.", "abc"},
// Remove .. elements
{"abc/def/ghi/../jkl", "abc/def/jkl"},
{"abc/def/../ghi/../jkl", "abc/jkl"},
{"abc/def/..", "abc"},
{"abc/def/../..", "."},
{"/abc/def/../..", "/"},
{"abc/def/../../..", ".."},
{"/abc/def/../../..", "/"},
{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
// Combinations
{"abc/./../def", "def"},
{"abc//./../def", "def"},
{"abc/../../././../def", "../../def"},
}
func TestClean(t *testing.T) {
for _, test := range cleantests {
if s := filepath.Clean(test.path); s != test.result {
t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
}
}
}
const sep = filepath.Separator
var slashtests = []PathTest{
{"", ""},
{"/", string(sep)},
{"/a/b", string([]byte{sep, 'a', sep, 'b'})},
{"a//b", string([]byte{'a', sep, sep, 'b'})},
}
func TestFromAndToSlash(t *testing.T) {
for _, test := range slashtests {
if s := filepath.FromSlash(test.path); s != test.result {
t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result)
}
if s := filepath.ToSlash(test.result); s != test.path {
t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path)
}
}
}
type SplitListTest struct {
list string
result []string
}
const lsep = filepath.ListSeparator
var splitlisttests = []SplitListTest{
{"", []string{}},
{string([]byte{'a', lsep, 'b'}), []string{"a", "b"}},
{string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}},
}
func TestSplitList(t *testing.T) {
for _, test := range splitlisttests {
if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) {
t.Errorf("SplitList(%q) = %s, want %s", test.list, l, test.result)
}
}
}
type SplitTest struct {
path, dir, file string
}
var unixsplittests = []SplitTest{
{"a/b", "a/", "b"},
{"a/b/", "a/b/", ""},
{"a/", "a/", ""},
{"a", "", "a"},
{"/", "/", ""},
}
func TestSplit(t *testing.T) {
var splittests []SplitTest
splittests = unixsplittests
for _, test := range splittests {
if d, f := filepath.Split(test.path); d != test.dir || f != test.file {
t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file)
}
}
}
type JoinTest struct {
elem []string
path string
}
var jointests = []JoinTest{
// zero parameters
{[]string{}, ""},
// one parameter
{[]string{""}, ""},
{[]string{"a"}, "a"},
// two parameters
{[]string{"a", "b"}, "a/b"},
{[]string{"a", ""}, "a"},
{[]string{"", "b"}, "b"},
{[]string{"/", "a"}, "/a"},
{[]string{"/", ""}, "/"},
{[]string{"a/", "b"}, "a/b"},
{[]string{"a/", ""}, "a"},
{[]string{"", ""}, ""},
}
// join takes a []string and passes it to Join.
func join(elem []string, args ...string) string {
args = elem
return filepath.Join(args...)
}
func TestJoin(t *testing.T) {
for _, test := range jointests {
if p := join(test.elem); p != test.path {
t.Errorf("join(%q) = %q, want %q", test.elem, p, test.path)
}
}
}
type ExtTest struct {
path, ext string
}
var exttests = []ExtTest{
{"path.go", ".go"},
{"path.pb.go", ".go"},
{"a.dir/b", ""},
{"a.dir/b.go", ".go"},
{"a.dir/", ""},
}
func TestExt(t *testing.T) {
for _, test := range exttests {
if x := filepath.Ext(test.path); x != test.ext {
t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext)
}
}
}
type Node struct {
name string
entries []*Node // nil if the entry is a file
mark int
}
var tree = &Node{
"testdata",
[]*Node{
&Node{"a", nil, 0},
&Node{"b", []*Node{}, 0},
&Node{"c", nil, 0},
&Node{
"d",
[]*Node{
&Node{"x", nil, 0},
&Node{"y", []*Node{}, 0},
&Node{
"z",
[]*Node{
&Node{"u", nil, 0},
&Node{"v", nil, 0},
},
0,
},
},
0,
},
},
0,
}
func walkTree(n *Node, path string, f func(path string, n *Node)) {
f(path, n)
for _, e := range n.entries {
walkTree(e, filepath.Join(path, e.name), f)
}
}
func makeTree(t *testing.T) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.entries == nil {
fd, err := os.Open(path, os.O_CREAT, 0660)
if err != nil {
t.Errorf("makeTree: %v", err)
}
fd.Close()
} else {
os.Mkdir(path, 0770)
}
})
}
func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) }
func checkMarks(t *testing.T) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.mark != 1 {
t.Errorf("node %s mark = %d; expected 1", path, n.mark)
}
n.mark = 0
})
}
// Assumes that each node name is unique. Good enough for a test.
func mark(name string) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.name == name {
n.mark++
}
})
}
type TestVisitor struct{}
func (v *TestVisitor) VisitDir(path string, f *os.FileInfo) bool {
mark(f.Name)
return true
}
func (v *TestVisitor) VisitFile(path string, f *os.FileInfo) {
mark(f.Name)
}
func TestWalk(t *testing.T) {
makeTree(t)
// 1) ignore error handling, expect none
v := &TestVisitor{}
filepath.Walk(tree.name, v, nil)
checkMarks(t)
// 2) handle errors, expect none
errors := make(chan os.Error, 64)
filepath.Walk(tree.name, v, errors)
select {
case err := <-errors:
t.Errorf("no error expected, found: %s", err)
default:
// ok
}
checkMarks(t)
if os.Getuid() != 0 {
// introduce 2 errors: chmod top-level directories to 0
os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0)
// mark respective subtrees manually
markTree(tree.entries[1])
markTree(tree.entries[3])
// correct double-marking of directory itself
tree.entries[1].mark--
tree.entries[3].mark--
// 3) handle errors, expect two
errors = make(chan os.Error, 64)
os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
filepath.Walk(tree.name, v, errors)
Loop:
for i := 1; i <= 2; i++ {
select {
case <-errors:
// ok
default:
t.Errorf("%d. error expected, none found", i)
break Loop
}
}
select {
case err := <-errors:
t.Errorf("only two errors expected, found 3rd: %v", err)
default:
// ok
}
// the inaccessible subtrees were marked manually
checkMarks(t)
}
// cleanup
os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770)
os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770)
if err := os.RemoveAll(tree.name); err != nil {
t.Errorf("removeTree: %v", err)
}
}
var basetests = []PathTest{
{"", "."},
{".", "."},
{"/.", "."},
{"/", "/"},
{"////", "/"},
{"x/", "x"},
{"abc", "abc"},
{"abc/def", "def"},
{"a/b/.x", ".x"},
{"a/b/c.", "c."},
{"a/b/c.x", "c.x"},
}
func TestBase(t *testing.T) {
for _, test := range basetests {
if s := filepath.Base(test.path); s != test.result {
t.Errorf("Base(%q) = %q, want %q", test.path, s, test.result)
}
}
}
type IsAbsTest struct {
path string
isAbs bool
}
var isAbsTests = []IsAbsTest{
{"", false},
{"/", true},
{"/usr/bin/gcc", true},
{"..", false},
{"/a/../bb", true},
{".", false},
{"./", false},
{"lala", false},
}
func TestIsAbs(t *testing.T) {
for _, test := range isAbsTests {
if r := filepath.IsAbs(test.path); r != test.isAbs {
t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
}
}
}
...@@ -2,10 +2,9 @@ ...@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package path package filepath
const ( const (
DirSeps = `/` // directory separators Separator = '/' // OS-specific path separator
VolumeSeps = `` // volume separators ListSeparator = ':' // OS-specific path list separator
PathSeps = DirSeps + VolumeSeps // all path separators
) )
// Copyright 2010 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.
package path package path
import ( import (
"os" "os"
"sort"
"strings" "strings"
"utf8" "utf8"
) )
...@@ -10,7 +13,7 @@ import ( ...@@ -10,7 +13,7 @@ import (
var ErrBadPattern = os.NewError("syntax error in pattern") var ErrBadPattern = os.NewError("syntax error in pattern")
// Match returns true if name matches the shell file name pattern. // Match returns true if name matches the shell file name pattern.
// The syntax used by pattern is: // The pattern syntax is:
// //
// pattern: // pattern:
// { term } // { term }
...@@ -75,7 +78,7 @@ Pattern: ...@@ -75,7 +78,7 @@ Pattern:
return len(name) == 0, nil return len(name) == 0, nil
} }
// scanChunk gets the next section of pattern, which is a non-star string // scanChunk gets the next segment of pattern, which is a non-star string
// possibly preceded by a star. // possibly preceded by a star.
func scanChunk(pattern string) (star bool, chunk, rest string) { func scanChunk(pattern string) (star bool, chunk, rest string) {
for len(pattern) > 0 && pattern[0] == '*' { for len(pattern) > 0 && pattern[0] == '*' {
...@@ -92,7 +95,6 @@ Scan: ...@@ -92,7 +95,6 @@ Scan:
if i+1 < len(pattern) { if i+1 < len(pattern) {
i++ i++
} }
continue
case '[': case '[':
inrange = true inrange = true
case ']': case ']':
...@@ -203,76 +205,3 @@ func getEsc(chunk string) (r int, nchunk string, err os.Error) { ...@@ -203,76 +205,3 @@ func getEsc(chunk string) (r int, nchunk string, err os.Error) {
} }
return return
} }
// Glob returns the names of all files matching pattern or nil
// if there is no matching file. The syntax of patterns is the same
// as in Match. The pattern may describe hierarchical names such as
// /usr/*/bin/ed.
//
func Glob(pattern string) (matches []string) {
if !hasMeta(pattern) {
if _, err := os.Stat(pattern); err == nil {
return []string{pattern}
}
return nil
}
dir, file := Split(pattern)
switch dir {
case "":
dir = "."
case "/":
// nothing
default:
dir = dir[0 : len(dir)-1] // chop off trailing '/'
}
if hasMeta(dir) {
for _, d := range Glob(dir) {
matches = glob(d, file, matches)
}
} else {
return glob(dir, file, nil)
}
return matches
}
// glob searches for files matching pattern in the directory dir
// and appends them to matches.
func glob(dir, pattern string, matches []string) []string {
fi, err := os.Stat(dir)
if err != nil {
return nil
}
if !fi.IsDirectory() {
return matches
}
d, err := os.Open(dir, os.O_RDONLY, 0666)
if err != nil {
return nil
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
return nil
}
sort.SortStrings(names)
for _, n := range names {
matched, err := Match(pattern, n)
if err != nil {
return matches
}
if matched {
matches = append(matches, Join(dir, n))
}
}
return matches
}
// hasMeta returns true if path contains any of the magic characters
// recognized by Match.
func hasMeta(path string) bool {
return strings.IndexAny(path, "*?[") != -1
}
...@@ -75,31 +75,3 @@ func TestMatch(t *testing.T) { ...@@ -75,31 +75,3 @@ func TestMatch(t *testing.T) {
} }
} }
} }
// contains returns true if vector contains the string s.
func contains(vector []string, s string) bool {
for _, elem := range vector {
if elem == s {
return true
}
}
return false
}
var globTests = []struct {
pattern, result string
}{
{"match.go", "match.go"},
{"mat?h.go", "match.go"},
{"*", "match.go"},
{"../*/match.go", "../path/match.go"},
}
func TestGlob(t *testing.T) {
for _, tt := range globTests {
matches := Glob(tt.pattern)
if !contains(matches, tt.result) {
t.Errorf("Glob(%#q) = %#v want %v", tt.pattern, matches, tt.result)
}
}
}
...@@ -7,8 +7,6 @@ ...@@ -7,8 +7,6 @@
package path package path
import ( import (
"io/ioutil"
"os"
"strings" "strings"
) )
...@@ -107,7 +105,7 @@ func Clean(path string) string { ...@@ -107,7 +105,7 @@ func Clean(path string) string {
// If there is no separator in path, Split returns an empty dir and // If there is no separator in path, Split returns an empty dir and
// file set to path. // file set to path.
func Split(path string) (dir, file string) { func Split(path string) (dir, file string) {
i := strings.LastIndexAny(path, PathSeps) i := strings.LastIndex(path, "/")
return path[:i+1], path[i+1:] return path[:i+1], path[i+1:]
} }
...@@ -135,78 +133,30 @@ func Ext(path string) string { ...@@ -135,78 +133,30 @@ func Ext(path string) string {
return "" return ""
} }
// Visitor methods are invoked for corresponding file tree entries // Base returns the last element of path.
// visited by Walk. The parameter path is the full path of f relative // Trailing slashes are removed before extracting the last element.
// to root. // If the path is empty, Base returns ".".
type Visitor interface { // If the path consists entirely of slashes, Base returns "/".
VisitDir(path string, f *os.FileInfo) bool func Base(path string) string {
VisitFile(path string, f *os.FileInfo) if path == "" {
}
func walk(path string, f *os.FileInfo, v Visitor, errors chan<- os.Error) {
if !f.IsDirectory() {
v.VisitFile(path, f)
return
}
if !v.VisitDir(path, f) {
return // skip directory entries
}
list, err := ioutil.ReadDir(path)
if err != nil {
if errors != nil {
errors <- err
}
}
for _, e := range list {
walk(Join(path, e.Name), e, v, errors)
}
}
// Walk walks the file tree rooted at root, calling v.VisitDir or
// v.VisitFile for each directory or file in the tree, including root.
// If v.VisitDir returns false, Walk skips the directory's entries;
// otherwise it invokes itself for each directory entry in sorted order.
// An error reading a directory does not abort the Walk.
// If errors != nil, Walk sends each directory read error
// to the channel. Otherwise Walk discards the error.
func Walk(root string, v Visitor, errors chan<- os.Error) {
f, err := os.Lstat(root)
if err != nil {
if errors != nil {
errors <- err
}
return // can't progress
}
walk(root, f, v, errors)
}
// Base returns the last path element of the slash-separated name.
// Trailing slashes are removed before extracting the last element. If the name is
// empty, "." is returned. If it consists entirely of slashes, "/" is returned.
func Base(name string) string {
if name == "" {
return "." return "."
} }
// Strip trailing slashes. // Strip trailing slashes.
for len(name) > 0 && name[len(name)-1] == '/' { for len(path) > 0 && path[len(path)-1] == '/' {
name = name[0 : len(name)-1] path = path[0 : len(path)-1]
} }
// Find the last element // Find the last element
if i := strings.LastIndex(name, "/"); i >= 0 { if i := strings.LastIndex(path, "/"); i >= 0 {
name = name[i+1:] path = path[i+1:]
} }
// If empty now, it had only slashes. // If empty now, it had only slashes.
if name == "" { if path == "" {
return "/" return "/"
} }
return name return path
} }
// IsAbs returns true if the path is absolute. // IsAbs returns true if the path is absolute.
func IsAbs(path string) bool { func IsAbs(path string) bool {
// TODO: Add Windows support return len(path) > 0 && path[0] == '/'
return strings.HasPrefix(path, "/")
} }
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
package path package path
import ( import (
"os"
"runtime"
"testing" "testing"
) )
...@@ -84,18 +82,7 @@ var splittests = []SplitTest{ ...@@ -84,18 +82,7 @@ var splittests = []SplitTest{
{"/", "/", ""}, {"/", "/", ""},
} }
var winsplittests = []SplitTest{
{`C:\Windows\System32`, `C:\Windows\`, `System32`},
{`C:\Windows\`, `C:\Windows\`, ``},
{`C:\Windows`, `C:\`, `Windows`},
{`C:Windows`, `C:`, `Windows`},
{`\\?\c:\`, `\\?\c:\`, ``},
}
func TestSplit(t *testing.T) { func TestSplit(t *testing.T) {
if runtime.GOOS == "windows" {
splittests = append(splittests, winsplittests...)
}
for _, test := range splittests { for _, test := range splittests {
if d, f := Split(test.path); d != test.dir || f != test.file { if d, f := Split(test.path); d != test.dir || f != test.file {
t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file) t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file)
...@@ -161,152 +148,6 @@ func TestExt(t *testing.T) { ...@@ -161,152 +148,6 @@ func TestExt(t *testing.T) {
} }
} }
type Node struct {
name string
entries []*Node // nil if the entry is a file
mark int
}
var tree = &Node{
"testdata",
[]*Node{
&Node{"a", nil, 0},
&Node{"b", []*Node{}, 0},
&Node{"c", nil, 0},
&Node{
"d",
[]*Node{
&Node{"x", nil, 0},
&Node{"y", []*Node{}, 0},
&Node{
"z",
[]*Node{
&Node{"u", nil, 0},
&Node{"v", nil, 0},
},
0,
},
},
0,
},
},
0,
}
func walkTree(n *Node, path string, f func(path string, n *Node)) {
f(path, n)
for _, e := range n.entries {
walkTree(e, Join(path, e.name), f)
}
}
func makeTree(t *testing.T) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.entries == nil {
fd, err := os.Open(path, os.O_CREAT, 0660)
if err != nil {
t.Errorf("makeTree: %v", err)
}
fd.Close()
} else {
os.Mkdir(path, 0770)
}
})
}
func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) }
func checkMarks(t *testing.T) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.mark != 1 {
t.Errorf("node %s mark = %d; expected 1", path, n.mark)
}
n.mark = 0
})
}
// Assumes that each node name is unique. Good enough for a test.
func mark(name string) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.name == name {
n.mark++
}
})
}
type TestVisitor struct{}
func (v *TestVisitor) VisitDir(path string, f *os.FileInfo) bool {
mark(f.Name)
return true
}
func (v *TestVisitor) VisitFile(path string, f *os.FileInfo) {
mark(f.Name)
}
func TestWalk(t *testing.T) {
makeTree(t)
// 1) ignore error handling, expect none
v := &TestVisitor{}
Walk(tree.name, v, nil)
checkMarks(t)
// 2) handle errors, expect none
errors := make(chan os.Error, 64)
Walk(tree.name, v, errors)
select {
case err := <-errors:
t.Errorf("no error expected, found: %s", err)
default:
// ok
}
checkMarks(t)
if os.Getuid() != 0 {
// introduce 2 errors: chmod top-level directories to 0
os.Chmod(Join(tree.name, tree.entries[1].name), 0)
os.Chmod(Join(tree.name, tree.entries[3].name), 0)
// mark respective subtrees manually
markTree(tree.entries[1])
markTree(tree.entries[3])
// correct double-marking of directory itself
tree.entries[1].mark--
tree.entries[3].mark--
// 3) handle errors, expect two
errors = make(chan os.Error, 64)
os.Chmod(Join(tree.name, tree.entries[1].name), 0)
Walk(tree.name, v, errors)
Loop:
for i := 1; i <= 2; i++ {
select {
case <-errors:
// ok
default:
t.Errorf("%d. error expected, none found", i)
break Loop
}
}
select {
case err := <-errors:
t.Errorf("only two errors expected, found 3rd: %v", err)
default:
// ok
}
// the inaccessible subtrees were marked manually
checkMarks(t)
}
// cleanup
os.Chmod(Join(tree.name, tree.entries[1].name), 0770)
os.Chmod(Join(tree.name, tree.entries[3].name), 0770)
if err := os.RemoveAll(tree.name); err != nil {
t.Errorf("removeTree: %v", err)
}
}
var basetests = []CleanTest{ var basetests = []CleanTest{
// Already clean // Already clean
{"", "."}, {"", "."},
......
// Copyright 2010 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.
package path
const (
DirSeps = `\/` // directory separators
VolumeSeps = `:` // volume separators
PathSeps = DirSeps + VolumeSeps // all path separators
)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment