1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
8 PathSeparator = '\\' // OS-specific path separator
9 PathListSeparator = ';' // OS-specific path list separator
12 // IsPathSeparator reports whether c is a directory separator character.
13 func IsPathSeparator(c uint8) bool {
14 // NOTE: Windows accept / as path separator.
15 return c == '\\' || c == '/'
18 // basename removes trailing slashes and the leading
19 // directory name and drive letter from path name.
20 func basename(name string) string {
21 // Remove drive letter
22 if len(name) == 2 && name[1] == ':' {
24 } else if len(name) > 2 && name[1] == ':' {
28 // Remove trailing slashes
29 for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
32 // Remove leading directory name
33 for i--; i >= 0; i-- {
34 if name[i] == '/' || name[i] == '\\' {
42 func isAbs(path string) (b bool) {
51 return IsPathSeparator(path[0])
54 func volumeName(path string) (v string) {
61 ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
62 'A' <= c && c <= 'Z') {
66 if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) &&
67 !IsPathSeparator(path[2]) && path[2] != '.' {
68 // first, leading `\\` and next shouldn't be `\`. its server name.
69 for n := 3; n < l-1; n++ {
70 // second, next '\' shouldn't be repeated.
71 if IsPathSeparator(path[n]) {
73 // third, following something characters. its share name.
74 if !IsPathSeparator(path[n]) {
79 if IsPathSeparator(path[n]) {
92 func fromSlash(path string) string {
93 // Replace each '/' with '\\' if present
96 for i, b := range path {
99 pathbuf = make([]byte, len(path))
101 copy(pathbuf[lastSlash:], path[lastSlash:i])
110 copy(pathbuf[lastSlash:], path[lastSlash:])
111 return string(pathbuf)
114 func dirname(path string) string {
115 vol := volumeName(path)
117 for i >= len(vol) && !IsPathSeparator(path[i]) {
120 dir := path[len(vol) : i+1]
122 if last > 0 && IsPathSeparator(dir[last]) {
131 // fixLongPath returns the extended-length (\\?\-prefixed) form of
132 // path when needed, in order to avoid the default 260 character file
133 // path limit imposed by Windows. If path is not easily converted to
134 // the extended-length form (for example, if path is a relative path
135 // or contains .. elements), or is short enough, fixLongPath returns
138 // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
139 func fixLongPath(path string) string {
140 // Do nothing (and don't allocate) if the path is "short".
141 // Empirically (at least on the Windows Server 2013 builder),
142 // the kernel is arbitrarily okay with < 248 bytes. That
143 // matches what the docs above say:
144 // "When using an API to create a directory, the specified
145 // path cannot be so long that you cannot append an 8.3 file
146 // name (that is, the directory name cannot exceed MAX_PATH
147 // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
149 // The MSDN docs appear to say that a normal path that is 248 bytes long
150 // will work; empirically the path must be less then 248 bytes long.
152 // Don't fix. (This is how Go 1.7 and earlier worked,
153 // not automatically generating the \\?\ form)
157 // The extended form begins with \\?\, as in
158 // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
159 // The extended form disables evaluation of . and .. path
160 // elements and disables the interpretation of / as equivalent
161 // to \. The conversion here rewrites / to \ and elides
162 // . elements as well as trailing or duplicate separators. For
163 // simplicity it avoids the conversion entirely for relative
164 // paths or paths containing .. elements. For now,
165 // \\server\share paths are not converted to
166 // \\?\UNC\server\share paths because the rules for doing so
167 // are less well-specified.
168 if len(path) >= 2 && path[:2] == `\\` {
169 // Don't canonicalize UNC paths.
179 pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
180 copy(pathbuf, prefix)
182 r, w := 0, len(prefix)
185 case IsPathSeparator(path[r]):
188 case path[r] == '.' && (r+1 == n || IsPathSeparator(path[r+1])):
191 case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || IsPathSeparator(path[r+2])):
192 // /../ is currently unhandled
197 for ; r < n && !IsPathSeparator(path[r]); r++ {
203 // A drive's root directory needs a trailing \
204 if w == len(`\\?\c:`) {
208 return string(pathbuf[:w])
211 // fixRootDirectory fixes a reference to a drive's root directory to
212 // have the required trailing slash.
213 func fixRootDirectory(p string) string {
214 if len(p) == len(`\\?\c:`) {
215 if IsPathSeparator(p[0]) && IsPathSeparator(p[1]) && p[2] == '?' && IsPathSeparator(p[3]) && p[5] == ':' {