github.com/knieriem/gointernal@v0.2.0-pre2/cmd/quoted/quoted.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 quoted provides string manipulation utilities. 6 package quoted 7 8 import ( 9 "flag" 10 "fmt" 11 "strings" 12 "unicode" 13 ) 14 15 func isSpaceByte(c byte) bool { 16 return c == ' ' || c == '\t' || c == '\n' || c == '\r' 17 } 18 19 // Split splits s into a list of fields, 20 // allowing single or double quotes around elements. 21 // There is no unescaping or other processing within 22 // quoted fields. 23 func Split(s string) ([]string, error) { 24 // Split fields allowing '' or "" around elements. 25 // Quotes further inside the string do not count. 26 var f []string 27 for len(s) > 0 { 28 for len(s) > 0 && isSpaceByte(s[0]) { 29 s = s[1:] 30 } 31 if len(s) == 0 { 32 break 33 } 34 // Accepted quoted string. No unescaping inside. 35 if s[0] == '"' || s[0] == '\'' { 36 quote := s[0] 37 s = s[1:] 38 i := 0 39 for i < len(s) && s[i] != quote { 40 i++ 41 } 42 if i >= len(s) { 43 return nil, fmt.Errorf("unterminated %c string", quote) 44 } 45 f = append(f, s[:i]) 46 s = s[i+1:] 47 continue 48 } 49 i := 0 50 for i < len(s) && !isSpaceByte(s[i]) { 51 i++ 52 } 53 f = append(f, s[:i]) 54 s = s[i:] 55 } 56 return f, nil 57 } 58 59 // Join joins a list of arguments into a string that can be parsed 60 // with Split. Arguments are quoted only if necessary; arguments 61 // without spaces or quotes are kept as-is. No argument may contain both 62 // single and double quotes. 63 func Join(args []string) (string, error) { 64 var buf []byte 65 for i, arg := range args { 66 if i > 0 { 67 buf = append(buf, ' ') 68 } 69 var sawSpace, sawSingleQuote, sawDoubleQuote bool 70 for _, c := range arg { 71 switch { 72 case c > unicode.MaxASCII: 73 continue 74 case isSpaceByte(byte(c)): 75 sawSpace = true 76 case c == '\'': 77 sawSingleQuote = true 78 case c == '"': 79 sawDoubleQuote = true 80 } 81 } 82 switch { 83 case !sawSpace && !sawSingleQuote && !sawDoubleQuote: 84 buf = append(buf, []byte(arg)...) 85 86 case !sawSingleQuote: 87 buf = append(buf, '\'') 88 buf = append(buf, []byte(arg)...) 89 buf = append(buf, '\'') 90 91 case !sawDoubleQuote: 92 buf = append(buf, '"') 93 buf = append(buf, []byte(arg)...) 94 buf = append(buf, '"') 95 96 default: 97 return "", fmt.Errorf("argument %q contains both single and double quotes and cannot be quoted", arg) 98 } 99 } 100 return string(buf), nil 101 } 102 103 // A Flag parses a list of string arguments encoded with Join. 104 // It is useful for flags like cmd/link's -extldflags. 105 type Flag []string 106 107 var _ flag.Value = (*Flag)(nil) 108 109 func (f *Flag) Set(v string) error { 110 fs, err := Split(v) 111 if err != nil { 112 return err 113 } 114 *f = fs[:len(fs):len(fs)] 115 return nil 116 } 117 118 func (f *Flag) String() string { 119 if f == nil { 120 return "" 121 } 122 s, err := Join(*f) 123 if err != nil { 124 return strings.Join(*f, " ") 125 } 126 return s 127 }