Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/str/str.go

Documentation: cmd/go/internal/str

     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  // Uniq removes consecutive duplicate strings from ss.
   100  func Uniq(ss *[]string) {
   101  	if len(*ss) <= 1 {
   102  		return
   103  	}
   104  	uniq := (*ss)[:1]
   105  	for _, s := range *ss {
   106  		if s != uniq[len(uniq)-1] {
   107  			uniq = append(uniq, s)
   108  		}
   109  	}
   110  	*ss = uniq
   111  }
   112  
   113  func isSpaceByte(c byte) bool {
   114  	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
   115  }
   116  
   117  // SplitQuotedFields splits s into a list of fields,
   118  // allowing single or double quotes around elements.
   119  // There is no unescaping or other processing within
   120  // quoted fields.
   121  func SplitQuotedFields(s string) ([]string, error) {
   122  	// Split fields allowing '' or "" around elements.
   123  	// Quotes further inside the string do not count.
   124  	var f []string
   125  	for len(s) > 0 {
   126  		for len(s) > 0 && isSpaceByte(s[0]) {
   127  			s = s[1:]
   128  		}
   129  		if len(s) == 0 {
   130  			break
   131  		}
   132  		// Accepted quoted string. No unescaping inside.
   133  		if s[0] == '"' || s[0] == '\'' {
   134  			quote := s[0]
   135  			s = s[1:]
   136  			i := 0
   137  			for i < len(s) && s[i] != quote {
   138  				i++
   139  			}
   140  			if i >= len(s) {
   141  				return nil, fmt.Errorf("unterminated %c string", quote)
   142  			}
   143  			f = append(f, s[:i])
   144  			s = s[i+1:]
   145  			continue
   146  		}
   147  		i := 0
   148  		for i < len(s) && !isSpaceByte(s[i]) {
   149  			i++
   150  		}
   151  		f = append(f, s[:i])
   152  		s = s[i:]
   153  	}
   154  	return f, nil
   155  }
   156  

View as plain text