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 // IsAbs reports whether the path is absolute. 17 func IsAbs(path string) (b bool) { 18 l := volumeNameLen(path) 19 if l == 0 { 20 return false 21 } 22 path = path[l:] 23 if path == "" { 24 return false 25 } 26 return isSlash(path[0]) 27 } 28 29 // volumeNameLen returns length of the leading volume name on Windows. 30 // It returns 0 elsewhere. 31 func volumeNameLen(path string) int { 32 if len(path) < 2 { 33 return 0 34 } 35 // with drive letter 36 c := path[0] 37 if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') { 38 return 2 39 } 40 // is it UNC 41 if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && 42 !isSlash(path[2]) && path[2] != '.' { 43 // first, leading `\\` and next shouldn't be `\`. its server name. 44 for n := 3; n < l-1; n++ { 45 // second, next '\' shouldn't be repeated. 46 if isSlash(path[n]) { 47 n++ 48 // third, following something characters. its share name. 49 if !isSlash(path[n]) { 50 if path[n] == '.' { 51 break 52 } 53 for ; n < l; n++ { 54 if isSlash(path[n]) { 55 break 56 } 57 } 58 return n 59 } 60 break 61 } 62 } 63 } 64 return 0 65 } 66 67 // HasPrefix exists for historical compatibility and should not be used. 68 func HasPrefix(p, prefix string) bool { 69 if strings.HasPrefix(p, prefix) { 70 return true 71 } 72 return strings.HasPrefix(strings.ToLower(p), strings.ToLower(prefix)) 73 } 74 75 func splitList(path string) []string { 76 // The same implementation is used in LookPath in os/exec; 77 // consider changing os/exec when changing this. 78 79 if path == "" { 80 return []string{} 81 } 82 83 // Split path, respecting but preserving quotes. 84 list := []string{} 85 start := 0 86 quo := false 87 for i := 0; i < len(path); i++ { 88 switch c := path[i]; { 89 case c == '"': 90 quo = !quo 91 case c == ListSeparator && !quo: 92 list = append(list, path[start:i]) 93 start = i + 1 94 } 95 } 96 list = append(list, path[start:]) 97 98 // Remove quotes. 99 for i, s := range list { 100 if strings.Contains(s, `"`) { 101 list[i] = strings.Replace(s, `"`, ``, -1) 102 } 103 } 104 105 return list 106 } 107 108 func abs(path string) (string, error) { 109 return syscall.FullPath(path) 110 } 111 112 func join(elem []string) string { 113 for i, e := range elem { 114 if e != "" { 115 return joinNonEmpty(elem[i:]) 116 } 117 } 118 return "" 119 } 120 121 // joinNonEmpty is like join, but it assumes that the first element is non-empty. 122 func joinNonEmpty(elem []string) string { 123 // The following logic prevents Join from inadvertently creating a 124 // UNC path on Windows. Unless the first element is a UNC path, Join 125 // shouldn't create a UNC path. See golang.org/issue/9167. 126 p := Clean(strings.Join(elem, string(Separator))) 127 if !isUNC(p) { 128 return p 129 } 130 // p == UNC only allowed when the first element is a UNC path. 131 head := Clean(elem[0]) 132 if isUNC(head) { 133 return p 134 } 135 // head + tail == UNC, but joining two non-UNC paths should not result 136 // in a UNC path. Undo creation of UNC path. 137 tail := Clean(strings.Join(elem[1:], string(Separator))) 138 if head[len(head)-1] == Separator { 139 return head + tail 140 } 141 return head + string(Separator) + tail 142 } 143 144 // isUNC reports whether path is a UNC path. 145 func isUNC(path string) bool { 146 return volumeNameLen(path) > 2 147 } 148