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 func Split(s string) ([]string, error) {
24
25
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
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
60
61
62
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
104
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 }
128
View as plain text