Commit 3c4ca95d authored by Alex Brainman's avatar Alex Brainman

path/filepath: fixes for windows paths

- Clean and IsAbs to handle paths with drive letter properly.
- Clean to replace / with \.

R=golang-dev, adg
CC=golang-dev, mattn.jp
https://golang.org/cl/4758051
parent 95323c59
...@@ -451,24 +451,31 @@ var segments = []segment{ ...@@ -451,24 +451,31 @@ var segments = []segment{
{"\n //line foo:42\n line44", filepath.Join("dir", "foo"), 44}, // bad line comment, ignored {"\n //line foo:42\n line44", filepath.Join("dir", "foo"), 44}, // bad line comment, ignored
{"\n//line foo 42\n line46", filepath.Join("dir", "foo"), 46}, // bad line comment, ignored {"\n//line foo 42\n line46", filepath.Join("dir", "foo"), 46}, // bad line comment, ignored
{"\n//line foo:42 extra text\n line48", filepath.Join("dir", "foo"), 48}, // bad line comment, ignored {"\n//line foo:42 extra text\n line48", filepath.Join("dir", "foo"), 48}, // bad line comment, ignored
{"\n//line /bar:42\n line42", string(filepath.Separator) + "bar", 42},
{"\n//line ./foo:42\n line42", filepath.Join("dir", "foo"), 42}, {"\n//line ./foo:42\n line42", filepath.Join("dir", "foo"), 42},
{"\n//line a/b/c/File1.go:100\n line100", filepath.Join("dir", "a", "b", "c", "File1.go"), 100}, {"\n//line a/b/c/File1.go:100\n line100", filepath.Join("dir", "a", "b", "c", "File1.go"), 100},
} }
var unixsegments = []segment{
{"\n//line /bar:42\n line42", "/bar", 42},
}
var winsegments = []segment{ var winsegments = []segment{
{"\n//line c:\\bar:42\n line42", "c:\\bar", 42},
{"\n//line c:\\dir\\File1.go:100\n line100", "c:\\dir\\File1.go", 100}, {"\n//line c:\\dir\\File1.go:100\n line100", "c:\\dir\\File1.go", 100},
} }
// Verify that comments of the form "//line filename:line" are interpreted correctly. // Verify that comments of the form "//line filename:line" are interpreted correctly.
func TestLineComments(t *testing.T) { func TestLineComments(t *testing.T) {
segs := segments
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
segments = append(segments, winsegments...) segs = append(segs, winsegments...)
} else {
segs = append(segs, unixsegments...)
} }
// make source // make source
var src string var src string
for _, e := range segments { for _, e := range segs {
src += e.srcline src += e.srcline
} }
...@@ -476,7 +483,7 @@ func TestLineComments(t *testing.T) { ...@@ -476,7 +483,7 @@ func TestLineComments(t *testing.T) {
var S Scanner var S Scanner
file := fset.AddFile(filepath.Join("dir", "TestLineComments"), fset.Base(), len(src)) file := fset.AddFile(filepath.Join("dir", "TestLineComments"), fset.Base(), len(src))
S.Init(file, []byte(src), nil, 0) S.Init(file, []byte(src), nil, 0)
for _, s := range segments { for _, s := range segs {
p, _, lit := S.Scan() p, _, lit := S.Scan()
pos := file.Position(p) pos := file.Position(p)
checkPos(t, lit, p, token.Position{s.filename, pos.Offset, s.line, pos.Column}) checkPos(t, lit, p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
......
...@@ -38,19 +38,19 @@ const ( ...@@ -38,19 +38,19 @@ const (
// Getting Dot-Dot right,'' // Getting Dot-Dot right,''
// http://plan9.bell-labs.com/sys/doc/lexnames.html // http://plan9.bell-labs.com/sys/doc/lexnames.html
func Clean(path string) string { func Clean(path string) string {
vol := volumeName(path)
path = path[len(vol):]
if path == "" { if path == "" {
return "." return vol + "."
} }
rooted := IsAbs(path) rooted := os.IsPathSeparator(path[0])
// Invariants: // Invariants:
// reading from path; r is index of next byte to process. // reading from path; r is index of next byte to process.
// writing to buf; w is index of next byte to write. // writing to buf; w is index of next byte to write.
// dotdot is index in buf where .. must stop, either because // dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix. // it is the leading slash or it is a leading ../../.. prefix.
prefix := volumeName(path)
path = path[len(prefix):]
n := len(path) n := len(path)
buf := []byte(path) buf := []byte(path)
r, w, dotdot := 0, 0, 0 r, w, dotdot := 0, 0, 0
...@@ -110,7 +110,7 @@ func Clean(path string) string { ...@@ -110,7 +110,7 @@ func Clean(path string) string {
w++ w++
} }
return prefix + string(buf[0:w]) return FromSlash(vol + string(buf[0:w]))
} }
// ToSlash returns the result of replacing each separator character // ToSlash returns the result of replacing each separator character
......
...@@ -67,9 +67,27 @@ var cleantests = []PathTest{ ...@@ -67,9 +67,27 @@ var cleantests = []PathTest{
{"abc/../../././../def", "../../def"}, {"abc/../../././../def", "../../def"},
} }
var wincleantests = []PathTest{
{`c:`, `c:.`},
{`c:\`, `c:\`},
{`c:\abc`, `c:\abc`},
{`c:abc\..\..\.\.\..\def`, `c:..\..\def`},
{`c:\abc\def\..\..`, `c:\`},
{`c:..\abc`, `c:..\abc`},
{`\`, `\`},
{`/`, `\`},
}
func TestClean(t *testing.T) { func TestClean(t *testing.T) {
for _, test := range cleantests { tests := cleantests
if s := filepath.ToSlash(filepath.Clean(test.path)); s != test.result { if runtime.GOOS == "windows" {
for i, _ := range tests {
tests[i].result = filepath.FromSlash(tests[i].result)
}
tests = append(tests, wincleantests...)
}
for _, test := range tests {
if s := filepath.Clean(test.path); s != test.result {
t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
} }
} }
...@@ -399,16 +417,30 @@ var winisabstests = []IsAbsTest{ ...@@ -399,16 +417,30 @@ var winisabstests = []IsAbsTest{
{`C:\`, true}, {`C:\`, true},
{`c\`, false}, {`c\`, false},
{`c::`, false}, {`c::`, false},
{`/`, true}, {`c:`, false},
{`\`, true}, {`/`, false},
{`\Windows`, true}, {`\`, false},
{`\Windows`, false},
{`c:a\b`, false},
} }
func TestIsAbs(t *testing.T) { func TestIsAbs(t *testing.T) {
var tests []IsAbsTest
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
isabstests = append(isabstests, winisabstests...) tests = append(tests, winisabstests...)
// All non-windows tests should fail, because they have no volume letter.
for _, test := range isabstests {
tests = append(tests, IsAbsTest{test.path, false})
}
// All non-windows test should work as intended if prefixed with volume letter.
for _, test := range isabstests {
tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
}
} else {
tests = isabstests
} }
for _, test := range isabstests {
for _, test := range tests {
if r := filepath.IsAbs(test.path); r != test.isAbs { if r := filepath.IsAbs(test.path); r != test.isAbs {
t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs) t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
} }
......
...@@ -4,25 +4,31 @@ ...@@ -4,25 +4,31 @@
package filepath package filepath
import "os"
// 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) (b bool) {
return path != "" && (volumeName(path) != "" || os.IsPathSeparator(path[0])) v := volumeName(path)
if v == "" {
return false
}
path = path[len(v):]
if path == "" {
return false
}
return path[0] == '/' || path[0] == '\\'
} }
// volumeName return leading volume name. // volumeName return leading volume name.
// If given "C:\foo\bar", return "C:" on windows. // If given "C:\foo\bar", return "C:" on windows.
func volumeName(path string) string { func volumeName(path string) (v string) {
if path == "" { if len(path) < 2 {
return "" return ""
} }
// with drive letter // with drive letter
c := path[0] c := path[0]
if len(path) > 2 && path[1] == ':' && os.IsPathSeparator(path[2]) && if path[1] == ':' &&
('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
'A' <= c && c <= 'Z') { 'A' <= c && c <= 'Z') {
return path[0:2] return path[:2]
} }
return "" return ""
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment