Black Lives Matter. Support the Equal Justice Initiative.

Source file src/path/filepath/path_windows.go

Documentation: path/filepath

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

View as plain text