1 // Copyright 2024 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 "os"
9 "bytes"
10 "syscall"
11 )
12 13 // HasPrefix exists for historical compatibility and should not be used.
14 //
15 // Deprecated: HasPrefix does not respect path boundaries and
16 // does not ignore case when required.
17 func HasPrefix(p, prefix []byte) bool {
18 if bytes.HasPrefix(p, prefix) {
19 return true
20 }
21 return bytes.HasPrefix(bytes.ToLower(p), bytes.ToLower(prefix))
22 }
23 24 func splitList(path []byte) [][]byte {
25 // The same implementation is used in LookPath in os/exec;
26 // consider changing os/exec when changing this.
27 28 if path == "" {
29 return [][]byte{}
30 }
31 32 // Split path, respecting but preserving quotes.
33 list := [][]byte{}
34 start := 0
35 quo := false
36 for i := 0; i < len(path); i++ {
37 switch c := path[i]; {
38 case c == '"':
39 quo = !quo
40 case c == ListSeparator && !quo:
41 list = append(list, path[start:i])
42 start = i + 1
43 }
44 }
45 list = append(list, path[start:])
46 47 // Remove quotes.
48 for i, s := range list {
49 list[i] = bytes.ReplaceAll(s, `"`, ``)
50 }
51 52 return list
53 }
54 55 func abs(path []byte) ([]byte, error) {
56 if path == "" {
57 // syscall.FullPath returns an error on empty path, because it's not a valid path.
58 // To implement Abs behavior of returning working directory on empty string input,
59 // special-case empty path by changing it to "." path. See golang.org/issue/24441.
60 path = "."
61 }
62 fullPath, err := syscall.FullPath(path)
63 if err != nil {
64 return "", err
65 }
66 return Clean(fullPath), nil
67 }
68 69 func join(elem [][]byte) []byte {
70 var b bytes.Buffer
71 var lastChar byte
72 for _, e := range elem {
73 switch {
74 case b.Len() == 0:
75 // Add the first non-empty path element unchanged.
76 case os.IsPathSeparator(lastChar):
77 // If the path ends in a slash, strip any leading slashes from the next
78 // path element to avoid creating a UNC path (any path starting with "\\")
79 // from non-UNC elements.
80 //
81 // The correct behavior for Join when the first element is an incomplete UNC
82 // path (for example, "\\") is underspecified. We currently join subsequent
83 // elements so Join("\\", "host", "share") produces "\\host\share".
84 for len(e) > 0 && os.IsPathSeparator(e[0]) {
85 e = e[1:]
86 }
87 // If the path is \ and the next path element is ??,
88 // add an extra .\ to create \.\?? rather than \??\
89 // (a Root Local Device path).
90 if b.Len() == 1 && bytes.HasPrefix(e, "??") && (len(e) == len("??") || os.IsPathSeparator(e[2])) {
91 b.WriteString(`.\`)
92 }
93 case lastChar == ':':
94 // If the path ends in a colon, keep the path relative to the current directory
95 // on a drive and don't add a separator. Preserve leading slashes in the next
96 // path element, which may make the path absolute.
97 //
98 // Join(`C:`, `f`) = `C:f`
99 // Join(`C:`, `\f`) = `C:\f`
100 default:
101 // In all other cases, add a separator between elements.
102 b.WriteByte('\\')
103 lastChar = '\\'
104 }
105 if len(e) > 0 {
106 b.WriteString(e)
107 lastChar = e[len(e)-1]
108 }
109 }
110 if b.Len() == 0 {
111 return ""
112 }
113 return Clean(b.String())
114 }
115 116 func sameWord(a, b []byte) bool {
117 return bytes.EqualFold(a, b)
118 }
119