github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/go/internal/str/str.go (about) 1 // Copyright 2017 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 str provides string manipulation utilities. 6 package str 7 8 import ( 9 "bytes" 10 "fmt" 11 "unicode" 12 "unicode/utf8" 13 ) 14 15 // StringList flattens its arguments into a single []string. 16 // Each argument in args must have type string or []string. 17 func StringList(args ...interface{}) []string { 18 var x []string 19 for _, arg := range args { 20 switch arg := arg.(type) { 21 case []string: 22 x = append(x, arg...) 23 case string: 24 x = append(x, arg) 25 default: 26 panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg)) 27 } 28 } 29 return x 30 } 31 32 // ToFold returns a string with the property that 33 // strings.EqualFold(s, t) iff ToFold(s) == ToFold(t) 34 // This lets us test a large set of strings for fold-equivalent 35 // duplicates without making a quadratic number of calls 36 // to EqualFold. Note that strings.ToUpper and strings.ToLower 37 // do not have the desired property in some corner cases. 38 func ToFold(s string) string { 39 // Fast path: all ASCII, no upper case. 40 // Most paths look like this already. 41 for i := 0; i < len(s); i++ { 42 c := s[i] 43 if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { 44 goto Slow 45 } 46 } 47 return s 48 49 Slow: 50 var buf bytes.Buffer 51 for _, r := range s { 52 // SimpleFold(x) cycles to the next equivalent rune > x 53 // or wraps around to smaller values. Iterate until it wraps, 54 // and we've found the minimum value. 55 for { 56 r0 := r 57 r = unicode.SimpleFold(r0) 58 if r <= r0 { 59 break 60 } 61 } 62 // Exception to allow fast path above: A-Z => a-z 63 if 'A' <= r && r <= 'Z' { 64 r += 'a' - 'A' 65 } 66 buf.WriteRune(r) 67 } 68 return buf.String() 69 } 70 71 // FoldDup reports a pair of strings from the list that are 72 // equal according to strings.EqualFold. 73 // It returns "", "" if there are no such strings. 74 func FoldDup(list []string) (string, string) { 75 clash := map[string]string{} 76 for _, s := range list { 77 fold := ToFold(s) 78 if t := clash[fold]; t != "" { 79 if s > t { 80 s, t = t, s 81 } 82 return s, t 83 } 84 clash[fold] = s 85 } 86 return "", "" 87 } 88 89 // Contains reports whether x contains s. 90 func Contains(x []string, s string) bool { 91 for _, t := range x { 92 if t == s { 93 return true 94 } 95 } 96 return false 97 } 98 99 func isSpaceByte(c byte) bool { 100 return c == ' ' || c == '\t' || c == '\n' || c == '\r' 101 } 102 103 // SplitQuotedFields splits s into a list of fields, 104 // allowing single or double quotes around elements. 105 // There is no unescaping or other processing within 106 // quoted fields. 107 func SplitQuotedFields(s string) ([]string, error) { 108 // Split fields allowing '' or "" around elements. 109 // Quotes further inside the string do not count. 110 var f []string 111 for len(s) > 0 { 112 for len(s) > 0 && isSpaceByte(s[0]) { 113 s = s[1:] 114 } 115 if len(s) == 0 { 116 break 117 } 118 // Accepted quoted string. No unescaping inside. 119 if s[0] == '"' || s[0] == '\'' { 120 quote := s[0] 121 s = s[1:] 122 i := 0 123 for i < len(s) && s[i] != quote { 124 i++ 125 } 126 if i >= len(s) { 127 return nil, fmt.Errorf("unterminated %c string", quote) 128 } 129 f = append(f, s[:i]) 130 s = s[i+1:] 131 continue 132 } 133 i := 0 134 for i < len(s) && !isSpaceByte(s[i]) { 135 i++ 136 } 137 f = append(f, s[:i]) 138 s = s[i:] 139 } 140 return f, nil 141 }