Skip to content

Commit

Permalink
Merge pull request #539 from wking/windows-clean
Browse files Browse the repository at this point in the history
filepath/clean: Add Windows support
  • Loading branch information
Ma Shimiao committed Dec 13, 2017
2 parents 9518371 + 2f21180 commit d1751c1
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 11 deletions.
4 changes: 0 additions & 4 deletions filepath/abs.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package filepath

import (
"errors"
"regexp"
"strings"
)
Expand All @@ -11,9 +10,6 @@ var windowsAbs = regexp.MustCompile(`^[a-zA-Z]:\\.*$`)
// Abs is a version of path/filepath's Abs with an explicit operating
// system and current working directory.
func Abs(os, path, cwd string) (_ string, err error) {
if os == "windows" {
return "", errors.New("Abs() does not support windows yet")
}
if IsAbs(os, path) {
return Clean(os, path), nil
}
Expand Down
66 changes: 64 additions & 2 deletions filepath/abs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package filepath

import (
"fmt"
"path/filepath"
"runtime"
"testing"
)

Expand Down Expand Up @@ -66,6 +68,60 @@ func TestAbs(t *testing.T) {
cwd: "/cwd",
expected: "/b",
},
{
os: "windows",
path: "c:\\",
cwd: "/cwd",
expected: "c:\\",
},
{
os: "windows",
path: "c:\\a",
cwd: "c:\\cwd",
expected: "c:\\a",
},
{
os: "windows",
path: "c:\\a\\",
cwd: "c:\\cwd",
expected: "c:\\a",
},
{
os: "windows",
path: "c:\\\\a",
cwd: "c:\\cwd",
expected: "c:\\a",
},
{
os: "windows",
path: ".",
cwd: "c:\\cwd",
expected: "c:\\cwd",
},
{
os: "windows",
path: ".\\c",
cwd: "c:\\a\\b",
expected: "c:\\a\\b\\c",
},
{
os: "windows",
path: ".\\\\c",
cwd: "c:\\a\\b",
expected: "c:\\a\\b\\c",
},
{
os: "windows",
path: "..\\a",
cwd: "c:\\cwd",
expected: "c:\\a",
},
{
os: "windows",
path: "..\\..\\b",
cwd: "c:\\cwd",
expected: "c:\\b",
},
} {
t.Run(
fmt.Sprintf("Abs(%q,%q,%q)", test.os, test.path, test.cwd),
Expand All @@ -74,7 +130,7 @@ func TestAbs(t *testing.T) {
if err != nil {
t.Error(err)
} else if abs != test.expected {
t.Errorf("unexpected result: %q (expected %q)\n", abs, test.expected)
t.Errorf("unexpected result: %q (expected %q)", abs, test.expected)
}
},
)
Expand Down Expand Up @@ -163,7 +219,13 @@ func TestIsAbs(t *testing.T) {
func(t *testing.T) {
abs := IsAbs(test.os, test.path)
if abs != test.expected {
t.Errorf("unexpected result: %t (expected %t)\n", abs, test.expected)
t.Errorf("unexpected result: %t (expected %t)", abs, test.expected)
}
if runtime.GOOS == test.os {
stdAbs := filepath.IsAbs(test.path)
if abs != stdAbs {
t.Errorf("non-standard result: %t (%t is standard)", abs, stdAbs)
}
}
},
)
Expand Down
98 changes: 98 additions & 0 deletions filepath/ancestor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,104 @@ func TestIsAncestor(t *testing.T) {
cwd: "/cwd",
expected: true,
},
{
os: "windows",
pathA: "c:\\",
pathB: "c:\\a",
cwd: "c:\\cwd",
expected: true,
},
{
os: "windows",
pathA: "c:\\",
pathB: "d:\\a",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\",
pathB: ".",
cwd: "d:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\a",
pathB: "c:\\a",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\a",
pathB: "c:\\",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\a",
pathB: "c:\\ab",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\a\\",
pathB: "c:\\a",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\\\a",
pathB: "c:\\a",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\\\a",
pathB: "c:\\a\\b",
cwd: "c:\\cwd",
expected: true,
},
{
os: "windows",
pathA: "c:\\a",
pathB: "c:\\a\\",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\a",
pathB: ".",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\a",
pathB: "b",
cwd: "c:\\a",
expected: true,
},
{
os: "windows",
pathA: "c:\\a",
pathB: "..\\a",
cwd: "c:\\cwd",
expected: false,
},
{
os: "windows",
pathA: "c:\\a",
pathB: "..\\a\\b",
cwd: "c:\\cwd",
expected: true,
},
} {
t.Run(
fmt.Sprintf("IsAncestor(%q,%q,%q,%q)", test.os, test.pathA, test.pathB, test.cwd),
Expand Down
26 changes: 22 additions & 4 deletions filepath/clean.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ func Clean(os, path string) string {
// Eliminate each inner .. path name element (the parent directory)
// along with the non-.. element that precedes it.
for i := 1; i < len(elements); i++ {
if i == 1 && abs && sep == '\\' {
continue
}
if i > 0 && elements[i] == ".." {
elements = append(elements[:i-1], elements[i+1:]...)
i -= 2
Expand All @@ -39,16 +42,31 @@ func Clean(os, path string) string {
// Eliminate .. elements that begin a rooted path:
// that is, replace "/.." by "/" at the beginning of a path,
// assuming Separator is '/'.
if abs && len(elements) > 0 {
for elements[0] == ".." {
elements = elements[1:]
offset := 0
if sep == '\\' {
offset = 1
}
if abs {
for len(elements) > offset && elements[offset] == ".." {
elements = append(elements[:offset], elements[offset+1:]...)
}
}

cleaned := strings.Join(elements, string(sep))
if abs {
cleaned = fmt.Sprintf("%c%s", sep, cleaned)
if sep == '/' {
cleaned = fmt.Sprintf("%c%s", sep, cleaned)
} else if len(elements) == 1 {
cleaned = fmt.Sprintf("%s%c", cleaned, sep)
}
}

// If the result of this process is an empty string, Clean returns
// the string ".".
if len(cleaned) == 0 {
cleaned = "."
}

if cleaned == path {
return path
}
Expand Down
85 changes: 84 additions & 1 deletion filepath/clean_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package filepath

import (
"fmt"
"path/filepath"
"runtime"
"testing"
)

Expand Down Expand Up @@ -36,6 +38,16 @@ func TestClean(t *testing.T) {
path: "//a",
expected: "/a",
},
{
os: "linux",
path: "/..",
expected: "/",
},
{
os: "linux",
path: "/../a",
expected: "/a",
},
{
os: "linux",
path: ".",
Expand All @@ -56,13 +68,84 @@ func TestClean(t *testing.T) {
path: "a/../b",
expected: "b",
},
{
os: "linux",
path: "a/..",
expected: ".",
},
{
os: "windows",
path: "c:\\",
expected: "c:\\",
},
{
os: "windows",
path: "c:\\\\",
expected: "c:\\",
},
{
os: "windows",
path: "c:\\a",
expected: "c:\\a",
},
{
os: "windows",
path: "c:\\a\\",
expected: "c:\\a",
},
{
os: "windows",
path: "c:\\\\a",
expected: "c:\\a",
},
{
os: "windows",
path: "c:\\..",
expected: "c:\\",
},
{
os: "windows",
path: "c:\\..\\a",
expected: "c:\\a",
},
{
os: "windows",
path: ".",
expected: ".",
},
{
os: "windows",
path: ".\\c",
expected: "c",
},
{
os: "windows",
path: "..\\.\\a",
expected: "..\\a",
},
{
os: "windows",
path: "a\\..\\b",
expected: "b",
},
{
os: "windows",
path: "a\\..",
expected: ".",
},
} {
t.Run(
fmt.Sprintf("Clean(%q,%q)", test.os, test.path),
func(t *testing.T) {
clean := Clean(test.os, test.path)
if clean != test.expected {
t.Errorf("unexpected result: %q (expected %q)\n", clean, test.expected)
t.Errorf("unexpected result: %q (expected %q)", clean, test.expected)
}
if runtime.GOOS == test.os {
stdClean := filepath.Clean(test.path)
if clean != stdClean {
t.Errorf("non-standard result: %q (%q is standard)", clean, stdClean)
}
}
},
)
Expand Down

0 comments on commit d1751c1

Please sign in to comment.