github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/os/path_windows.go (about)

     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.
     4  
     5  package os
     6  
     7  const (
     8  	PathSeparator     = '\\' // OS-specific path separator
     9  	PathListSeparator = ';'  // OS-specific path list separator
    10  )
    11  
    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 == '/'
    16  }
    17  
    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] == ':' {
    23  		name = "."
    24  	} else if len(name) > 2 && name[1] == ':' {
    25  		name = name[2:]
    26  	}
    27  	i := len(name) - 1
    28  	// Remove trailing slashes
    29  	for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
    30  		name = name[:i]
    31  	}
    32  	// Remove leading directory name
    33  	for i--; i >= 0; i-- {
    34  		if name[i] == '/' || name[i] == '\\' {
    35  			name = name[i+1:]
    36  			break
    37  		}
    38  	}
    39  	return name
    40  }
    41  
    42  func isAbs(path string) (b bool) {
    43  	v := volumeName(path)
    44  	if v == "" {
    45  		return false
    46  	}
    47  	path = path[len(v):]
    48  	if path == "" {
    49  		return false
    50  	}
    51  	return IsPathSeparator(path[0])
    52  }
    53  
    54  func volumeName(path string) (v string) {
    55  	if len(path) < 2 {
    56  		return ""
    57  	}
    58  	// with drive letter
    59  	c := path[0]
    60  	if path[1] == ':' &&
    61  		('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
    62  			'A' <= c && c <= 'Z') {
    63  		return path[:2]
    64  	}
    65  	// is it UNC
    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]) {
    72  				n++
    73  				// third, following something characters. its share name.
    74  				if !IsPathSeparator(path[n]) {
    75  					if path[n] == '.' {
    76  						break
    77  					}
    78  					for ; n < l; n++ {
    79  						if IsPathSeparator(path[n]) {
    80  							break
    81  						}
    82  					}
    83  					return path[:n]
    84  				}
    85  				break
    86  			}
    87  		}
    88  	}
    89  	return ""
    90  }
    91  
    92  func fromSlash(path string) string {
    93  	// Replace each '/' with '\\' if present
    94  	var pathbuf []byte
    95  	var lastSlash int
    96  	for i, b := range path {
    97  		if b == '/' {
    98  			if pathbuf == nil {
    99  				pathbuf = make([]byte, len(path))
   100  			}
   101  			copy(pathbuf[lastSlash:], path[lastSlash:i])
   102  			pathbuf[i] = '\\'
   103  			lastSlash = i + 1
   104  		}
   105  	}
   106  	if pathbuf == nil {
   107  		return path
   108  	}
   109  
   110  	copy(pathbuf[lastSlash:], path[lastSlash:])
   111  	return string(pathbuf)
   112  }
   113  
   114  func dirname(path string) string {
   115  	vol := volumeName(path)
   116  	i := len(path) - 1
   117  	for i >= len(vol) && !IsPathSeparator(path[i]) {
   118  		i--
   119  	}
   120  	dir := path[len(vol) : i+1]
   121  	last := len(dir) - 1
   122  	if last > 0 && IsPathSeparator(dir[last]) {
   123  		dir = dir[:last]
   124  	}
   125  	if dir == "" {
   126  		dir = "."
   127  	}
   128  	return vol + dir
   129  }
   130  
   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
   136  // path unmodified.
   137  //
   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.
   148  	//
   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.
   151  	if len(path) < 248 {
   152  		// Don't fix. (This is how Go 1.7 and earlier worked,
   153  		// not automatically generating the \\?\ form)
   154  		return path
   155  	}
   156  
   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.
   170  		return path
   171  	}
   172  	if !isAbs(path) {
   173  		// Relative path
   174  		return path
   175  	}
   176  
   177  	const prefix = `\\?`
   178  
   179  	pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
   180  	copy(pathbuf, prefix)
   181  	n := len(path)
   182  	r, w := 0, len(prefix)
   183  	for r < n {
   184  		switch {
   185  		case IsPathSeparator(path[r]):
   186  			// empty block
   187  			r++
   188  		case path[r] == '.' && (r+1 == n || IsPathSeparator(path[r+1])):
   189  			// /./
   190  			r++
   191  		case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || IsPathSeparator(path[r+2])):
   192  			// /../ is currently unhandled
   193  			return path
   194  		default:
   195  			pathbuf[w] = '\\'
   196  			w++
   197  			for ; r < n && !IsPathSeparator(path[r]); r++ {
   198  				pathbuf[w] = path[r]
   199  				w++
   200  			}
   201  		}
   202  	}
   203  	// A drive's root directory needs a trailing \
   204  	if w == len(`\\?\c:`) {
   205  		pathbuf[w] = '\\'
   206  		w++
   207  	}
   208  	return string(pathbuf[:w])
   209  }