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