this repo has no description
at master 201 lines 5.2 kB view raw
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 19package path 20 21import ( 22 "strings" 23) 24 25type windowsInfo struct{} 26 27var _ osInfo = windowsInfo{} 28 29const ( 30 windowsSeparator = '\\' 31 windowsListSeparator = ';' 32) 33 34func isSlash(c uint8) bool { 35 return c == '\\' || c == '/' 36} 37 38func (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. 45var 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. 53func (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. 66func (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. 83func (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 119func (os windowsInfo) splitList(path string) []string { 120 // The same implementation is used in LookPath in os/exec; 121 // consider changing os/exec when changing this. 122 123 if path == "" { 124 return []string{} 125 } 126 127 // Split path, respecting but preserving quotes. 128 list := []string{} 129 start := 0 130 quo := false 131 for i := 0; i < len(path); i++ { 132 switch c := path[i]; { 133 case c == '"': 134 quo = !quo 135 case c == windowsListSeparator && !quo: 136 list = append(list, path[start:i]) 137 start = i + 1 138 } 139 } 140 list = append(list, path[start:]) 141 142 // Remove quotes. 143 for i, s := range list { 144 list[i] = strings.ReplaceAll(s, `"`, ``) 145 } 146 147 return list 148} 149 150func (os windowsInfo) join(elem []string) string { 151 for i, e := range elem { 152 if e != "" { 153 return os.joinNonEmpty(elem[i:]) 154 } 155 } 156 return "" 157} 158 159// joinNonEmpty is like join, but it assumes that the first element is non-empty. 160func (o windowsInfo) joinNonEmpty(elem []string) string { 161 if len(elem[0]) == 2 && elem[0][1] == ':' { 162 // First element is drive letter without terminating slash. 163 // Keep path relative to current directory on that drive. 164 // Skip empty elements. 165 i := 1 166 for ; i < len(elem); i++ { 167 if elem[i] != "" { 168 break 169 } 170 } 171 return clean(elem[0]+strings.Join(elem[i:], string(windowsSeparator)), windows) 172 } 173 // The following logic prevents Join from inadvertently creating a 174 // UNC path on Windows. Unless the first element is a UNC path, Join 175 // shouldn't create a UNC path. See golang.org/issue/9167. 176 p := clean(strings.Join(elem, string(windowsSeparator)), windows) 177 if !isUNC(p) { 178 return p 179 } 180 // p == UNC only allowed when the first element is a UNC path. 181 head := clean(elem[0], windows) 182 if isUNC(head) { 183 return p 184 } 185 // head + tail == UNC, but joining two non-UNC paths should not result 186 // in a UNC path. Undo creation of UNC path. 187 tail := clean(strings.Join(elem[1:], string(windowsSeparator)), windows) 188 if head[len(head)-1] == windowsSeparator { 189 return head + tail 190 } 191 return head + string(windowsSeparator) + tail 192} 193 194// isUNC reports whether path is a UNC path. 195func isUNC(path string) bool { 196 return windows.volumeNameLen(path) > 2 197} 198 199func (o windowsInfo) sameWord(a, b string) bool { 200 return strings.EqualFold(a, b) 201}