mirror of
https://github.com/golang/go
synced 2024-11-25 07:17:56 -07:00
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
This commit is contained in:
parent
ce65b72508
commit
04ca4f8242
@ -13,7 +13,7 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -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 (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
pathutil "path"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
@ -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
|
|||||||
// 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
|
|||||||
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 (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
pathutil "path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
@ -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) {
|
|||||||
|
|
||||||
|
|
||||||
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() {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// 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{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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{}) {
|
|||||||
|
|
||||||
// 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{
|
|||||||
|
|
||||||
|
|
||||||
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 {
|
|||||||
|
|
||||||
|
|
||||||
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
|
|||||||
|
|
||||||
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) {
|
|||||||
// 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) {
|
|||||||
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) {
|
|||||||
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
|
|||||||
// 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
|
|||||||
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) {
|
|||||||
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 (
|
|||||||
"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{
|
|||||||
// 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)
|
|||||||
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)
|
|||||||
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 (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
pathutil "path"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -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() {
|
|||||||
|
|
||||||
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 (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
pathutil "path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -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 {
|
|||||||
// 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 {
|
|||||||
// 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) {
|
|||||||
|
|
||||||
|
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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 (
|
|||||||
"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 {
|
|||||||
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 {
|
|||||||
// 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{
|
|||||||
//
|
//
|
||||||
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 (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
pathutil "path"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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
|
|||||||
import (
|
import (
|
||||||
"http"
|
"http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -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) {
|
|||||||
// 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) {
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
// 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 (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -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() {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
|
|
||||||
// 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 @@
|
|||||||
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) {
|
|||||||
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 (
|
|||||||
"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) {
|
|||||||
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) {
|
|||||||
}
|
}
|
||||||
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 (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"patch"
|
"patch"
|
||||||
"path"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -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=\
|
|||||||
os/signal\
|
os/signal\
|
||||||
patch\
|
patch\
|
||||||
path\
|
path\
|
||||||
|
path/filepath\
|
||||||
rand\
|
rand\
|
||||||
reflect\
|
reflect\
|
||||||
regexp\
|
regexp\
|
||||||
|
@ -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,
|
|||||||
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 (
|
|||||||
"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) {
|
|||||||
}
|
}
|
||||||
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 (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"path"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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
|
|||||||
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
|
|||||||
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) {
|
|||||||
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 (
|
|||||||
"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) {
|
|||||||
|
|
||||||
// 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) {
|
|||||||
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) {
|
|||||||
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
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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) {
|
|||||||
|
|
||||||
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=\
|
|||||||
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
|
||||||
|
26
src/pkg/path/filepath/Makefile
Normal file
26
src/pkg/path/filepath/Makefile
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# 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
|
282
src/pkg/path/filepath/match.go
Normal file
282
src/pkg/path/filepath/match.go
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
// 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
|
||||||
|
}
|
106
src/pkg/path/filepath/match_test.go
Normal file
106
src/pkg/path/filepath/match_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
270
src/pkg/path/filepath/path.go
Normal file
270
src/pkg/path/filepath/path.go
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
// 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
|
||||||
|
}
|
387
src/pkg/path/filepath/path_test.go
Normal file
387
src/pkg/path/filepath/path_test.go
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/pkg/path/filepath/path_unix.go
Normal file
10
src/pkg/path/filepath/path_unix.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
const (
|
||||||
|
Separator = '/' // OS-specific path separator
|
||||||
|
ListSeparator = ':' // OS-specific path list separator
|
||||||
|
)
|
@ -1,8 +1,11 @@
|
|||||||
|
// 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 (
|
|||||||
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:
|
|||||||
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:
|
|||||||
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) {
|
|||||||
}
|
}
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 @@
|
|||||||
package path
|
package path
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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 {
|
|||||||
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 @@
|
|||||||
package path
|
package path
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
{"", "."},
|
{"", "."},
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
// 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
|
|
||||||
)
|
|
@ -1,11 +0,0 @@
|
|||||||
// 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
|
|
||||||
)
|
|
Loading…
Reference in New Issue
Block a user