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