···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package str
1616+1717+import (
1818+ "path/filepath"
1919+ "strings"
2020+)
2121+2222+// HasPath reports whether the slash-separated path s
2323+// begins with the elements in prefix.
2424+func HasPathPrefix(s, prefix string) bool {
2525+ if len(s) == len(prefix) {
2626+ return s == prefix
2727+ }
2828+ if prefix == "" {
2929+ return true
3030+ }
3131+ if len(s) > len(prefix) {
3232+ if prefix[len(prefix)-1] == '/' || s[len(prefix)] == '/' {
3333+ return s[:len(prefix)] == prefix
3434+ }
3535+ }
3636+ return false
3737+}
3838+3939+// HasFilePathPrefix reports whether the filesystem path s
4040+// begins with the elements in prefix.
4141+func HasFilePathPrefix(s, prefix string) bool {
4242+ sv := strings.ToUpper(filepath.VolumeName(s))
4343+ pv := strings.ToUpper(filepath.VolumeName(prefix))
4444+ s = s[len(sv):]
4545+ prefix = prefix[len(pv):]
4646+ switch {
4747+ default:
4848+ return false
4949+ case sv != pv:
5050+ return false
5151+ case len(s) == len(prefix):
5252+ return s == prefix
5353+ case prefix == "":
5454+ return true
5555+ case len(s) > len(prefix):
5656+ if prefix[len(prefix)-1] == filepath.Separator {
5757+ return strings.HasPrefix(s, prefix)
5858+ }
5959+ return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
6060+ }
6161+}
+151
internal/str/str.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+// Package str provides string manipulation utilities.
1616+package str // import "cuelang.org/go/internal/str"
1717+1818+import (
1919+ "bytes"
2020+ "fmt"
2121+ "unicode"
2222+ "unicode/utf8"
2323+)
2424+2525+// StringList flattens its arguments into a single []string.
2626+// Each argument in args must have type string or []string.
2727+func StringList(args ...interface{}) []string {
2828+ var x []string
2929+ for _, arg := range args {
3030+ switch arg := arg.(type) {
3131+ case []string:
3232+ x = append(x, arg...)
3333+ case string:
3434+ x = append(x, arg)
3535+ default:
3636+ panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
3737+ }
3838+ }
3939+ return x
4040+}
4141+4242+// ToFold returns a string with the property that
4343+// strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
4444+// This lets us test a large set of strings for fold-equivalent
4545+// duplicates without making a quadratic number of calls
4646+// to EqualFold. Note that strings.ToUpper and strings.ToLower
4747+// do not have the desired property in some corner cases.
4848+func ToFold(s string) string {
4949+ // Fast path: all ASCII, no upper case.
5050+ // Most paths look like this already.
5151+ for i := 0; i < len(s); i++ {
5252+ c := s[i]
5353+ if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
5454+ goto Slow
5555+ }
5656+ }
5757+ return s
5858+5959+Slow:
6060+ var buf bytes.Buffer
6161+ for _, r := range s {
6262+ // SimpleFold(x) cycles to the next equivalent rune > x
6363+ // or wraps around to smaller values. Iterate until it wraps,
6464+ // and we've found the minimum value.
6565+ for {
6666+ r0 := r
6767+ r = unicode.SimpleFold(r0)
6868+ if r <= r0 {
6969+ break
7070+ }
7171+ }
7272+ // Exception to allow fast path above: A-Z => a-z
7373+ if 'A' <= r && r <= 'Z' {
7474+ r += 'a' - 'A'
7575+ }
7676+ buf.WriteRune(r)
7777+ }
7878+ return buf.String()
7979+}
8080+8181+// FoldDup reports a pair of strings from the list that are
8282+// equal according to strings.EqualFold.
8383+// It returns "", "" if there are no such strings.
8484+func FoldDup(list []string) (string, string) {
8585+ clash := map[string]string{}
8686+ for _, s := range list {
8787+ fold := ToFold(s)
8888+ if t := clash[fold]; t != "" {
8989+ if s > t {
9090+ s, t = t, s
9191+ }
9292+ return s, t
9393+ }
9494+ clash[fold] = s
9595+ }
9696+ return "", ""
9797+}
9898+9999+// Contains reports whether x contains s.
100100+func Contains(x []string, s string) bool {
101101+ for _, t := range x {
102102+ if t == s {
103103+ return true
104104+ }
105105+ }
106106+ return false
107107+}
108108+109109+func isSpaceByte(c byte) bool {
110110+ return c == ' ' || c == '\t' || c == '\n' || c == '\r'
111111+}
112112+113113+// SplitQuotedFields splits s into a list of fields,
114114+// allowing single or double quotes around elements.
115115+// There is no unescaping or other processing within
116116+// quoted fields.
117117+func SplitQuotedFields(s string) ([]string, error) {
118118+ // Split fields allowing '' or "" around elements.
119119+ // Quotes further inside the string do not count.
120120+ var f []string
121121+ for len(s) > 0 {
122122+ for len(s) > 0 && isSpaceByte(s[0]) {
123123+ s = s[1:]
124124+ }
125125+ if len(s) == 0 {
126126+ break
127127+ }
128128+ // Accepted quoted string. No unescaping inside.
129129+ if s[0] == '"' || s[0] == '\'' {
130130+ quote := s[0]
131131+ s = s[1:]
132132+ i := 0
133133+ for i < len(s) && s[i] != quote {
134134+ i++
135135+ }
136136+ if i >= len(s) {
137137+ return nil, fmt.Errorf("unterminated %c string", quote)
138138+ }
139139+ f = append(f, s[:i])
140140+ s = s[i+1:]
141141+ continue
142142+ }
143143+ i := 0
144144+ for i < len(s) && !isSpaceByte(s[i]) {
145145+ i++
146146+ }
147147+ f = append(f, s[:i])
148148+ s = s[i:]
149149+ }
150150+ return f, nil
151151+}