1
2
3
4
5
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
20
21
22
23
24
25 func Split(s string) ([]string, error) {
26
27
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
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
62
63
64
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
106
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 }
130
View as plain text