...

Source file src/cuelang.org/go/pkg/path/path_win.go

Documentation: cuelang.org/go/pkg/path

     1  // Copyright 2020 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Copyright 2010 The Go Authors. All rights reserved.
    16  // Use of this source code is governed by a BSD-style
    17  // license that can be found in the LICENSE file.
    18  
    19  package path
    20  
    21  import (
    22  	"strings"
    23  )
    24  
    25  type windowsInfo struct{}
    26  
    27  var _ osInfo = windowsInfo{}
    28  
    29  const (
    30  	windowsSeparator     = '\\'
    31  	windowsListSeparator = ';'
    32  )
    33  
    34  func isSlash(c uint8) bool {
    35  	return c == '\\' || c == '/'
    36  }
    37  
    38  func (os windowsInfo) IsPathSeparator(b byte) bool {
    39  	return isSlash(b)
    40  }
    41  
    42  // reservedNames lists reserved Windows names. Search for PRN in
    43  // https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
    44  // for details.
    45  var reservedNames = []string{
    46  	"CON", "PRN", "AUX", "NUL",
    47  	"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
    48  	"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
    49  }
    50  
    51  // isReservedName returns true, if path is Windows reserved name.
    52  // See reservedNames for the full list.
    53  func (os windowsInfo) isReservedName(path string) bool {
    54  	if len(path) == 0 {
    55  		return false
    56  	}
    57  	for _, reserved := range reservedNames {
    58  		if strings.EqualFold(path, reserved) {
    59  			return true
    60  		}
    61  	}
    62  	return false
    63  }
    64  
    65  // IsAbs reports whether the path is absolute.
    66  func (os windowsInfo) IsAbs(path string) (b bool) {
    67  	if os.isReservedName(path) {
    68  		return true
    69  	}
    70  	l := os.volumeNameLen(path)
    71  	if l == 0 {
    72  		return false
    73  	}
    74  	path = path[l:]
    75  	if path == "" {
    76  		return false
    77  	}
    78  	return isSlash(path[0])
    79  }
    80  
    81  // volumeNameLen returns length of the leading volume name on Windows.
    82  // It returns 0 elsewhere.
    83  func (os windowsInfo) volumeNameLen(path string) int {
    84  	if len(path) < 2 {
    85  		return 0
    86  	}
    87  	// with drive letter
    88  	c := path[0]
    89  	if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
    90  		return 2
    91  	}
    92  	// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
    93  	if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
    94  		!isSlash(path[2]) && path[2] != '.' {
    95  		// first, leading `\\` and next shouldn't be `\`. its server name.
    96  		for n := 3; n < l-1; n++ {
    97  			// second, next '\' shouldn't be repeated.
    98  			if isSlash(path[n]) {
    99  				n++
   100  				// third, following something characters. its share name.
   101  				if !isSlash(path[n]) {
   102  					if path[n] == '.' {
   103  						break
   104  					}
   105  					for ; n < l; n++ {
   106  						if isSlash(path[n]) {
   107  							break
   108  						}
   109  					}
   110  					return n
   111  				}
   112  				break
   113  			}
   114  		}
   115  	}
   116  	return 0
   117  }
   118  
   119  // HasPrefix exists for historical compatibility and should not be used.
   120  //
   121  // Deprecated: HasPrefix does not respect path boundaries and
   122  // does not ignore case when required.
   123  func (os windowsInfo) HasPrefix(p, prefix string) bool {
   124  	if strings.HasPrefix(p, prefix) {
   125  		return true
   126  	}
   127  	return strings.HasPrefix(strings.ToLower(p), strings.ToLower(prefix))
   128  }
   129  
   130  func (os windowsInfo) splitList(path string) []string {
   131  	// The same implementation is used in LookPath in os/exec;
   132  	// consider changing os/exec when changing this.
   133  
   134  	if path == "" {
   135  		return []string{}
   136  	}
   137  
   138  	// Split path, respecting but preserving quotes.
   139  	list := []string{}
   140  	start := 0
   141  	quo := false
   142  	for i := 0; i < len(path); i++ {
   143  		switch c := path[i]; {
   144  		case c == '"':
   145  			quo = !quo
   146  		case c == windowsListSeparator && !quo:
   147  			list = append(list, path[start:i])
   148  			start = i + 1
   149  		}
   150  	}
   151  	list = append(list, path[start:])
   152  
   153  	// Remove quotes.
   154  	for i, s := range list {
   155  		list[i] = strings.ReplaceAll(s, `"`, ``)
   156  	}
   157  
   158  	return list
   159  }
   160  
   161  func (os windowsInfo) join(elem []string) string {
   162  	for i, e := range elem {
   163  		if e != "" {
   164  			return os.joinNonEmpty(elem[i:])
   165  		}
   166  	}
   167  	return ""
   168  }
   169  
   170  // joinNonEmpty is like join, but it assumes that the first element is non-empty.
   171  func (o windowsInfo) joinNonEmpty(elem []string) string {
   172  	if len(elem[0]) == 2 && elem[0][1] == ':' {
   173  		// First element is drive letter without terminating slash.
   174  		// Keep path relative to current directory on that drive.
   175  		// Skip empty elements.
   176  		i := 1
   177  		for ; i < len(elem); i++ {
   178  			if elem[i] != "" {
   179  				break
   180  			}
   181  		}
   182  		return clean(elem[0]+strings.Join(elem[i:], string(windowsSeparator)), windows)
   183  	}
   184  	// The following logic prevents Join from inadvertently creating a
   185  	// UNC path on Windows. Unless the first element is a UNC path, Join
   186  	// shouldn't create a UNC path. See golang.org/issue/9167.
   187  	p := clean(strings.Join(elem, string(windowsSeparator)), windows)
   188  	if !isUNC(p) {
   189  		return p
   190  	}
   191  	// p == UNC only allowed when the first element is a UNC path.
   192  	head := clean(elem[0], windows)
   193  	if isUNC(head) {
   194  		return p
   195  	}
   196  	// head + tail == UNC, but joining two non-UNC paths should not result
   197  	// in a UNC path. Undo creation of UNC path.
   198  	tail := clean(strings.Join(elem[1:], string(windowsSeparator)), windows)
   199  	if head[len(head)-1] == windowsSeparator {
   200  		return head + tail
   201  	}
   202  	return head + string(windowsSeparator) + tail
   203  }
   204  
   205  // isUNC reports whether path is a UNC path.
   206  func isUNC(path string) bool {
   207  	return windows.volumeNameLen(path) > 2
   208  }
   209  
   210  func (o windowsInfo) sameWord(a, b string) bool {
   211  	return strings.EqualFold(a, b)
   212  }
   213  

View as plain text