src/path/filepath/path.go | 32 ++++++++++++++++++++------------ src/path/filepath/path_test.go | 4 ++++ diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go index 32dd887998650bff7cff4456c1ea1e023fcaa522..326ca006b2452904c944fa418c9619ce513fa3e1 100644 --- a/src/path/filepath/path.go +++ b/src/path/filepath/path.go @@ -52,6 +52,11 @@ b.buf[b.w] = c b.w++ } +func (b *lazybuf) prepend(prefix ...byte) { + b.buf = append(prefix, b.buf...) + b.w += len(prefix) +} + func (b *lazybuf) string() string { if b.buf == nil { return b.volAndPath[:b.volLen+b.w] @@ -146,18 +151,6 @@ // add slash if needed if rooted && out.w != 1 || !rooted && out.w != 0 { out.append(Separator) } - // If a ':' appears in the path element at the start of a Windows path, - // insert a .\ at the beginning to avoid converting relative paths - // like a/../c: into c:. - if runtime.GOOS == "windows" && out.w == 0 && out.volLen == 0 && r != 0 { - for i := r; i < n && !os.IsPathSeparator(path[i]); i++ { - if path[i] == ':' { - out.append('.') - out.append(Separator) - break - } - } - } // copy element for ; r < n && !os.IsPathSeparator(path[r]); r++ { out.append(path[r]) @@ -168,6 +161,21 @@ // Turn empty string into "." if out.w == 0 { out.append('.') + } + + if runtime.GOOS == "windows" && out.volLen == 0 && out.buf != nil { + // If a ':' appears in the path element at the start of a Windows path, + // insert a .\ at the beginning to avoid converting relative paths + // like a/../c: into c:. + for _, c := range out.buf { + if os.IsPathSeparator(c) { + break + } + if c == ':' { + out.prepend('.', Separator) + break + } + } } return FromSlash(out.string()) diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go index 697bcc672d73c300482e335eeee3523cd82721a8..1a2e53a795a9f710e2833a7e26bca80418b1b839 100644 --- a/src/path/filepath/path_test.go +++ b/src/path/filepath/path_test.go @@ -66,6 +66,7 @@ {"abc/def/../../..", ".."}, {"/abc/def/../../..", "/"}, {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, {"/../abc", "/abc"}, + {"a/../b:/../../c", `../c`}, // Combinations {"abc/./../def", "def"}, @@ -88,6 +89,7 @@ {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, {`c:\abc\def\..\..`, `c:\`}, {`c:\..\abc`, `c:\abc`}, {`c:..\abc`, `c:..\abc`}, + {`c:\b:\..\..\..\d`, `c:\d`}, {`\`, `\`}, {`/`, `\`}, {`\\i\..\c$`, `\\i\..\c$`}, @@ -168,6 +170,7 @@ {"a/../a", true}, {"a/", true}, {"a/.", true}, {"a/./b/./c", true}, + {`a/../b:/../../c`, false}, } var winislocaltests = []IsLocalTest{ @@ -379,6 +382,7 @@ {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`}, {[]string{`\\a`, `b`, `c`}, `\\a\b\c`}, {[]string{`\\a\`, `b`, `c`}, `\\a\b\c`}, {[]string{`//`, `a`}, `\\a`}, + {[]string{`a:\b\c`, `x\..\y:\..\..\z`}, `a:\b\z`}, } func TestJoin(t *testing.T) {