-
Notifications
You must be signed in to change notification settings - Fork 0
/
split.go
135 lines (127 loc) · 3.1 KB
/
split.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package cmdlineposix
// SplitCommand is like Split, but the first argument is returned separately.
func SplitCommand(cl string) (name string, args []string) {
a := Split(cl)
switch len(a) {
case 0:
return
case 1:
name = a[0]
default:
name = a[0]
args = a[1:]
}
return
}
// Split breaks the given command line into arguments. The arguments are split
// according to posix shell parsing rules.
//
// For an introduction to command line parsing in unix shells, see:
// http://www.grymoire.com/Unix/Quote.html
//
// For details on posix shell parsing requirements see:
// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18
func Split(cl string) (args []string) {
var (
buffer = make([]rune, len(cl)) // Buffer for current argument
inEscape bool // Are we in a section escaped by weak quotes?
inSingleQuote bool // Are we in a section escaped by strong quotes?
inDoubleQuote bool // Are we in a section escaped by weak quotes?
inArg bool // Have we read some portion of an argument?
b int // Current position within buffer
)
for _, runeValue := range cl {
switch runeValue {
case '\\':
inArg = true
switch {
case inEscape:
inEscape = false
fallthrough
case inSingleQuote:
b = write(buffer, b, runeValue)
default:
inEscape = true
}
case '\'':
inArg = true
switch {
case inDoubleQuote && inEscape:
b = write(buffer, b, '\\')
fallthrough
case inEscape:
inEscape = false
fallthrough
case inDoubleQuote:
b = write(buffer, b, runeValue)
default:
inSingleQuote = !inSingleQuote
}
case '"':
inArg = true
switch {
case inEscape:
inEscape = false
fallthrough
case inSingleQuote:
b = write(buffer, b, runeValue)
default:
inDoubleQuote = !inDoubleQuote
}
case ' ', '\t':
switch {
case inEscape:
// A whitespace character after an escaping backslash
inEscape = false
fallthrough
case inSingleQuote, inDoubleQuote:
// A whitespace character within a quoted section
b = write(buffer, b, runeValue)
default:
if inArg {
// A whitespace character terminating an argument
b = flushArg(&args, buffer, b)
inArg = false
}
}
case '\n':
switch {
case inEscape:
// A line return after a trailing backslash, indicating continuation
inEscape = false
case inArg:
// A whitespace character terminating an argument
b = flushArg(&args, buffer, b)
inArg = false
}
default:
inArg = true
switch {
case inEscape:
inEscape = false
switch runeValue {
case '$', '`':
default:
b = write(buffer, b, '\\')
}
}
b = write(buffer, b, runeValue)
}
}
if inEscape {
inArg = true
b = write(buffer, b, '\\')
}
if inArg {
flushArg(&args, buffer, b)
}
return
}
func write(buffer []rune, b int, runeValue rune) int {
buffer[b] = runeValue
return b + 1
}
func flushArg(args *[]string, buffer []rune, b int) int {
*args = append(*args, string(buffer[0:b]))
return 0
}