Skip to content

Commit 2f21180

Browse files
committed
filepath/clean: Add Windows support
And once we have Windows support for Clean, we can remove the guard from Abs. I don't have a Windows machine around to test with, so the expected values are my best guesses. Signed-off-by: W. Trevor King <[email protected]>
1 parent 17ce13a commit 2f21180

File tree

5 files changed

+226
-7
lines changed

5 files changed

+226
-7
lines changed

filepath/abs.go

-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package filepath
22

33
import (
4-
"errors"
54
"regexp"
65
"strings"
76
)
@@ -11,9 +10,6 @@ var windowsAbs = regexp.MustCompile(`^[a-zA-Z]:\\.*$`)
1110
// Abs is a version of path/filepath's Abs with an explicit operating
1211
// system and current working directory.
1312
func Abs(os, path, cwd string) (_ string, err error) {
14-
if os == "windows" {
15-
return "", errors.New("Abs() does not support windows yet")
16-
}
1713
if IsAbs(os, path) {
1814
return Clean(os, path), nil
1915
}

filepath/abs_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,60 @@ func TestAbs(t *testing.T) {
6868
cwd: "/cwd",
6969
expected: "/b",
7070
},
71+
{
72+
os: "windows",
73+
path: "c:\\",
74+
cwd: "/cwd",
75+
expected: "c:\\",
76+
},
77+
{
78+
os: "windows",
79+
path: "c:\\a",
80+
cwd: "c:\\cwd",
81+
expected: "c:\\a",
82+
},
83+
{
84+
os: "windows",
85+
path: "c:\\a\\",
86+
cwd: "c:\\cwd",
87+
expected: "c:\\a",
88+
},
89+
{
90+
os: "windows",
91+
path: "c:\\\\a",
92+
cwd: "c:\\cwd",
93+
expected: "c:\\a",
94+
},
95+
{
96+
os: "windows",
97+
path: ".",
98+
cwd: "c:\\cwd",
99+
expected: "c:\\cwd",
100+
},
101+
{
102+
os: "windows",
103+
path: ".\\c",
104+
cwd: "c:\\a\\b",
105+
expected: "c:\\a\\b\\c",
106+
},
107+
{
108+
os: "windows",
109+
path: ".\\\\c",
110+
cwd: "c:\\a\\b",
111+
expected: "c:\\a\\b\\c",
112+
},
113+
{
114+
os: "windows",
115+
path: "..\\a",
116+
cwd: "c:\\cwd",
117+
expected: "c:\\a",
118+
},
119+
{
120+
os: "windows",
121+
path: "..\\..\\b",
122+
cwd: "c:\\cwd",
123+
expected: "c:\\b",
124+
},
71125
} {
72126
t.Run(
73127
fmt.Sprintf("Abs(%q,%q,%q)", test.os, test.path, test.cwd),

filepath/ancestor_test.go

+98
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,104 @@ func TestIsAncestor(t *testing.T) {
9797
cwd: "/cwd",
9898
expected: true,
9999
},
100+
{
101+
os: "windows",
102+
pathA: "c:\\",
103+
pathB: "c:\\a",
104+
cwd: "c:\\cwd",
105+
expected: true,
106+
},
107+
{
108+
os: "windows",
109+
pathA: "c:\\",
110+
pathB: "d:\\a",
111+
cwd: "c:\\cwd",
112+
expected: false,
113+
},
114+
{
115+
os: "windows",
116+
pathA: "c:\\",
117+
pathB: ".",
118+
cwd: "d:\\cwd",
119+
expected: false,
120+
},
121+
{
122+
os: "windows",
123+
pathA: "c:\\a",
124+
pathB: "c:\\a",
125+
cwd: "c:\\cwd",
126+
expected: false,
127+
},
128+
{
129+
os: "windows",
130+
pathA: "c:\\a",
131+
pathB: "c:\\",
132+
cwd: "c:\\cwd",
133+
expected: false,
134+
},
135+
{
136+
os: "windows",
137+
pathA: "c:\\a",
138+
pathB: "c:\\ab",
139+
cwd: "c:\\cwd",
140+
expected: false,
141+
},
142+
{
143+
os: "windows",
144+
pathA: "c:\\a\\",
145+
pathB: "c:\\a",
146+
cwd: "c:\\cwd",
147+
expected: false,
148+
},
149+
{
150+
os: "windows",
151+
pathA: "c:\\\\a",
152+
pathB: "c:\\a",
153+
cwd: "c:\\cwd",
154+
expected: false,
155+
},
156+
{
157+
os: "windows",
158+
pathA: "c:\\\\a",
159+
pathB: "c:\\a\\b",
160+
cwd: "c:\\cwd",
161+
expected: true,
162+
},
163+
{
164+
os: "windows",
165+
pathA: "c:\\a",
166+
pathB: "c:\\a\\",
167+
cwd: "c:\\cwd",
168+
expected: false,
169+
},
170+
{
171+
os: "windows",
172+
pathA: "c:\\a",
173+
pathB: ".",
174+
cwd: "c:\\cwd",
175+
expected: false,
176+
},
177+
{
178+
os: "windows",
179+
pathA: "c:\\a",
180+
pathB: "b",
181+
cwd: "c:\\a",
182+
expected: true,
183+
},
184+
{
185+
os: "windows",
186+
pathA: "c:\\a",
187+
pathB: "..\\a",
188+
cwd: "c:\\cwd",
189+
expected: false,
190+
},
191+
{
192+
os: "windows",
193+
pathA: "c:\\a",
194+
pathB: "..\\a\\b",
195+
cwd: "c:\\cwd",
196+
expected: true,
197+
},
100198
} {
101199
t.Run(
102200
fmt.Sprintf("IsAncestor(%q,%q,%q,%q)", test.os, test.pathA, test.pathB, test.cwd),

filepath/clean.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ func Clean(os, path string) string {
3030
// Eliminate each inner .. path name element (the parent directory)
3131
// along with the non-.. element that precedes it.
3232
for i := 1; i < len(elements); i++ {
33+
if i == 1 && abs && sep == '\\' {
34+
continue
35+
}
3336
if i > 0 && elements[i] == ".." {
3437
elements = append(elements[:i-1], elements[i+1:]...)
3538
i -= 2
@@ -39,15 +42,23 @@ func Clean(os, path string) string {
3942
// Eliminate .. elements that begin a rooted path:
4043
// that is, replace "/.." by "/" at the beginning of a path,
4144
// assuming Separator is '/'.
45+
offset := 0
46+
if sep == '\\' {
47+
offset = 1
48+
}
4249
if abs {
43-
for len(elements) > 0 && elements[0] == ".." {
44-
elements = elements[1:]
50+
for len(elements) > offset && elements[offset] == ".." {
51+
elements = append(elements[:offset], elements[offset+1:]...)
4552
}
4653
}
4754

4855
cleaned := strings.Join(elements, string(sep))
4956
if abs {
50-
cleaned = fmt.Sprintf("%c%s", sep, cleaned)
57+
if sep == '/' {
58+
cleaned = fmt.Sprintf("%c%s", sep, cleaned)
59+
} else if len(elements) == 1 {
60+
cleaned = fmt.Sprintf("%s%c", cleaned, sep)
61+
}
5162
}
5263

5364
// If the result of this process is an empty string, Clean returns

filepath/clean_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,66 @@ func TestClean(t *testing.T) {
7373
path: "a/..",
7474
expected: ".",
7575
},
76+
{
77+
os: "windows",
78+
path: "c:\\",
79+
expected: "c:\\",
80+
},
81+
{
82+
os: "windows",
83+
path: "c:\\\\",
84+
expected: "c:\\",
85+
},
86+
{
87+
os: "windows",
88+
path: "c:\\a",
89+
expected: "c:\\a",
90+
},
91+
{
92+
os: "windows",
93+
path: "c:\\a\\",
94+
expected: "c:\\a",
95+
},
96+
{
97+
os: "windows",
98+
path: "c:\\\\a",
99+
expected: "c:\\a",
100+
},
101+
{
102+
os: "windows",
103+
path: "c:\\..",
104+
expected: "c:\\",
105+
},
106+
{
107+
os: "windows",
108+
path: "c:\\..\\a",
109+
expected: "c:\\a",
110+
},
111+
{
112+
os: "windows",
113+
path: ".",
114+
expected: ".",
115+
},
116+
{
117+
os: "windows",
118+
path: ".\\c",
119+
expected: "c",
120+
},
121+
{
122+
os: "windows",
123+
path: "..\\.\\a",
124+
expected: "..\\a",
125+
},
126+
{
127+
os: "windows",
128+
path: "a\\..\\b",
129+
expected: "b",
130+
},
131+
{
132+
os: "windows",
133+
path: "a\\..",
134+
expected: ".",
135+
},
76136
} {
77137
t.Run(
78138
fmt.Sprintf("Clean(%q,%q)", test.os, test.path),

0 commit comments

Comments
 (0)