Source file src/path/filepath/path_windows.go

     1  // Copyright 2010 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 filepath
     6  
     7  import (
     8  	"strings"
     9  	"syscall"
    10  )
    11  
    12  func isSlash(c uint8) bool {
    13  	return c == '\\' || c == '/'
    14  }
    15  
    16  // reservedNames lists reserved Windows names. Search for PRN in
    17  // https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
    18  // for details.
    19  var reservedNames = []string{
    20  	"CON", "PRN", "AUX", "NUL",
    21  	"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
    22  	"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
    23  }
    24  
    25  // isReservedName returns true, if path is Windows reserved name.
    26  // See reservedNames for the full list.
    27  func isReservedName(path string) bool {
    28  	if len(path) == 0 {
    29  		return false
    30  	}
    31  	for _, reserved := range reservedNames {
    32  		if strings.EqualFold(path, reserved) {
    33  			return true
    34  		}
    35  	}
    36  	return false
    37  }
    38  
    39  // IsAbs reports whether the path is absolute.
    40  func IsAbs(path string) (b bool) {
    41  	if isReservedName(path) {
    42  		return true
    43  	}
    44  	l := volumeNameLen(path)
    45  	if l == 0 {
    46  		return false
    47  	}
    48  	// If the volume name starts with a double slash, this is a UNC path.
    49  	if isSlash(path[0]) && isSlash(path[1]) {
    50  		return true
    51  	}
    52  	path = path[l:]
    53  	if path == "" {
    54  		return false
    55  	}
    56  	return isSlash(path[0])
    57  }
    58  
    59  // volumeNameLen returns length of the leading volume name on Windows.
    60  // It returns 0 elsewhere.
    61  func volumeNameLen(path string) int {
    62  	if len(path) < 2 {
    63  		return 0
    64  	}
    65  	// with drive letter
    66  	c := path[0]
    67  	if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
    68  		return 2
    69  	}
    70  	// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
    71  	if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
    72  		!isSlash(path[2]) && path[2] != '.' {
    73  		// first, leading `\\` and next shouldn't be `\`. its server name.
    74  		for n := 3; n < l-1; n++ {
    75  			// second, next '\' shouldn't be repeated.
    76  			if isSlash(path[n]) {
    77  				n++
    78  				// third, following something characters. its share name.
    79  				if !isSlash(path[n]) {
    80  					if path[n] == '.' {
    81  						break
    82  					}
    83  					for ; n < l; n++ {
    84  						if isSlash(path[n]) {
    85  							break
    86  						}
    87  					}
    88  					return n
    89  				}
    90  				break
    91  			}
    92  		}
    93  	}
    94  	return 0
    95  }
    96  
    97  // HasPrefix exists for historical compatibility and should not be used.
    98  //
    99  // Deprecated: HasPrefix does not respect path boundaries and
   100  // does not ignore case when required.
   101  func HasPrefix(p, prefix string) bool {
   102  	if strings.HasPrefix(p, prefix) {
   103  		return true
   104  	}
   105  	return strings.HasPrefix(strings.ToLower(p), strings.ToLower(prefix))
   106  }
   107  
   108  func splitList(path string) []string {
   109  	// The same implementation is used in LookPath in os/exec;
   110  	// consider changing os/exec when changing this.
   111  
   112  	if path == "" {
   113  		return []string{}
   114  	}
   115  
   116  	// Split path, respecting but preserving quotes.
   117  	list := []string{}
   118  	start := 0
   119  	quo := false
   120  	for i := 0; i < len(path); i++ {
   121  		switch c := path[i]; {
   122  		case c == '"':
   123  			quo = !quo
   124  		case c == ListSeparator && !quo:
   125  			list = append(list, path[start:i])
   126  			start = i + 1
   127  		}
   128  	}
   129  	list = append(list, path[start:])
   130  
   131  	// Remove quotes.
   132  	for i, s := range list {
   133  		list[i] = strings.ReplaceAll(s, `"`, ``)
   134  	}
   135  
   136  	return list
   137  }
   138  
   139  func abs(path string) (string, error) {
   140  	if path == "" {
   141  		// syscall.FullPath returns an error on empty path, because it's not a valid path.
   142  		// To implement Abs behavior of returning working directory on empty string input,
   143  		// special-case empty path by changing it to "." path. See golang.org/issue/24441.
   144  		path = "."
   145  	}
   146  	fullPath, err := syscall.FullPath(path)
   147  	if err != nil {
   148  		return "", err
   149  	}
   150  	return Clean(fullPath), nil
   151  }
   152  
   153  func join(elem []string) string {
   154  	for i, e := range elem {
   155  		if e != "" {
   156  			return joinNonEmpty(elem[i:])
   157  		}
   158  	}
   159  	return ""
   160  }
   161  
   162  // joinNonEmpty is like join, but it assumes that the first element is non-empty.
   163  func joinNonEmpty(elem []string) string {
   164  	if len(elem[0]) == 2 && elem[0][1] == ':' {
   165  		// First element is drive letter without terminating slash.
   166  		// Keep path relative to current directory on that drive.
   167  		// Skip empty elements.
   168  		i := 1
   169  		for ; i < len(elem); i++ {
   170  			if elem[i] != "" {
   171  				break
   172  			}
   173  		}
   174  		return Clean(elem[0] + strings.Join(elem[i:], string(Separator)))
   175  	}
   176  	// The following logic prevents Join from inadvertently creating a
   177  	// UNC path on Windows. Unless the first element is a UNC path, Join
   178  	// shouldn't create a UNC path. See golang.org/issue/9167.
   179  	p := Clean(strings.Join(elem, string(Separator)))
   180  	if !isUNC(p) {
   181  		return p
   182  	}
   183  	// p == UNC only allowed when the first element is a UNC path.
   184  	head := Clean(elem[0])
   185  	if isUNC(head) {
   186  		return p
   187  	}
   188  	// head + tail == UNC, but joining two non-UNC paths should not result
   189  	// in a UNC path. Undo creation of UNC path.
   190  	tail := Clean(strings.Join(elem[1:], string(Separator)))
   191  	if head[len(head)-1] == Separator {
   192  		return head + tail
   193  	}
   194  	return head + string(Separator) + tail
   195  }
   196  
   197  // isUNC reports whether path is a UNC path.
   198  func isUNC(path string) bool {
   199  	return volumeNameLen(path) > 2
   200  }
   201  
   202  func sameWord(a, b string) bool {
   203  	return strings.EqualFold(a, b)
   204  }
   205  

View as plain text