1
1
package shellwords
2
2
3
3
import (
4
+ "bytes"
4
5
"errors"
5
6
"os"
6
7
"regexp"
7
8
"strings"
9
+ "unicode"
8
10
)
9
11
10
12
var (
@@ -27,13 +29,72 @@ func replaceEnv(getenv func(string) string, s string) string {
27
29
getenv = os .Getenv
28
30
}
29
31
30
- return envRe .ReplaceAllStringFunc (s , func (s string ) string {
31
- s = s [1 :]
32
- if s [0 ] == '{' {
33
- s = s [1 : len (s )- 1 ]
32
+ var buf bytes.Buffer
33
+ rs := []rune (s )
34
+ for i := 0 ; i < len (rs ); i ++ {
35
+ r := rs [i ]
36
+ if r == '\\' {
37
+ i ++
38
+ if i == len (rs ) {
39
+ break
40
+ }
41
+ buf .WriteRune (rs [i ])
42
+ continue
43
+ } else if r == '$' {
44
+ i ++
45
+ if i == len (rs ) {
46
+ buf .WriteRune (r )
47
+ break
48
+ }
49
+ if rs [i ] == 0x7b {
50
+ i ++
51
+ p := i
52
+ for ; i < len (rs ); i ++ {
53
+ r = rs [i ]
54
+ if r == '\\' {
55
+ i ++
56
+ if i == len (rs ) {
57
+ return s
58
+ }
59
+ continue
60
+ }
61
+ if r == 0x7d || (! unicode .IsLetter (r ) && r != '_' && ! unicode .IsDigit (r )) {
62
+ break
63
+ }
64
+ }
65
+ if r != 0x7d {
66
+ return s
67
+ }
68
+ if i > p {
69
+ buf .WriteString (getenv (s [p :i ]))
70
+ }
71
+ } else {
72
+ p := i
73
+ for ; i < len (rs ); i ++ {
74
+ r := rs [i ]
75
+ if r == '\\' {
76
+ i ++
77
+ if i == len (rs ) {
78
+ return s
79
+ }
80
+ continue
81
+ }
82
+ if ! unicode .IsLetter (r ) && r != '_' && ! unicode .IsDigit (r ) {
83
+ break
84
+ }
85
+ }
86
+ if i > p {
87
+ buf .WriteString (getenv (s [p :i ]))
88
+ i --
89
+ } else {
90
+ buf .WriteString (s [p :])
91
+ }
92
+ }
93
+ } else {
94
+ buf .WriteRune (r )
34
95
}
35
- return getenv ( s )
36
- } )
96
+ }
97
+ return buf . String ( )
37
98
}
38
99
39
100
type Parser struct {
@@ -56,14 +117,22 @@ func NewParser() *Parser {
56
117
}
57
118
}
58
119
120
+ type argType int
121
+
122
+ const (
123
+ argNo argType = iota
124
+ argSingle
125
+ argQuoted
126
+ )
127
+
59
128
func (p * Parser ) Parse (line string ) ([]string , error ) {
60
129
args := []string {}
61
130
buf := ""
62
131
var escaped , doubleQuoted , singleQuoted , backQuote , dollarQuote bool
63
132
backtick := ""
64
133
65
134
pos := - 1
66
- got := false
135
+ got := argNo
67
136
68
137
i := - 1
69
138
loop:
72
141
if escaped {
73
142
buf += string (r )
74
143
escaped = false
75
- got = true
144
+ got = argSingle
76
145
continue
77
146
}
78
147
@@ -89,21 +158,25 @@ loop:
89
158
if singleQuoted || doubleQuoted || backQuote || dollarQuote {
90
159
buf += string (r )
91
160
backtick += string (r )
92
- } else if got {
161
+ } else if got != argNo {
93
162
if p .ParseEnv {
94
- parser := & Parser {ParseEnv : false , ParseBacktick : false , Position : 0 , Dir : p .Dir }
95
- strs , err := parser .Parse (replaceEnv (p .Getenv , buf ))
96
- if err != nil {
97
- return nil , err
98
- }
99
- for _ , str := range strs {
100
- args = append (args , str )
163
+ if got == argSingle {
164
+ parser := & Parser {ParseEnv : false , ParseBacktick : false , Position : 0 , Dir : p .Dir }
165
+ strs , err := parser .Parse (replaceEnv (p .Getenv , buf ))
166
+ if err != nil {
167
+ return nil , err
168
+ }
169
+ for _ , str := range strs {
170
+ args = append (args , str )
171
+ }
172
+ } else {
173
+ args = append (args , replaceEnv (p .Getenv , buf ))
101
174
}
102
175
} else {
103
176
args = append (args , buf )
104
177
}
105
178
buf = ""
106
- got = false
179
+ got = argNo
107
180
}
108
181
continue
109
182
}
@@ -156,15 +229,15 @@ loop:
156
229
case '"' :
157
230
if ! singleQuoted && ! dollarQuote {
158
231
if doubleQuoted {
159
- got = true
232
+ got = argQuoted
160
233
}
161
234
doubleQuoted = ! doubleQuoted
162
235
continue
163
236
}
164
237
case '\'' :
165
238
if ! doubleQuoted && ! dollarQuote {
166
239
if singleQuoted {
167
- got = true
240
+ got = argSingle
168
241
}
169
242
singleQuoted = ! singleQuoted
170
243
continue
@@ -174,30 +247,34 @@ loop:
174
247
if r == '>' && len (buf ) > 0 {
175
248
if c := buf [0 ]; '0' <= c && c <= '9' {
176
249
i -= 1
177
- got = false
250
+ got = argNo
178
251
}
179
252
}
180
253
pos = i
181
254
break loop
182
255
}
183
256
}
184
257
185
- got = true
258
+ got = argSingle
186
259
buf += string (r )
187
260
if backQuote || dollarQuote {
188
261
backtick += string (r )
189
262
}
190
263
}
191
264
192
- if got {
265
+ if got != argNo {
193
266
if p .ParseEnv {
194
- parser := & Parser {ParseEnv : false , ParseBacktick : false , Position : 0 , Dir : p .Dir }
195
- strs , err := parser .Parse (replaceEnv (p .Getenv , buf ))
196
- if err != nil {
197
- return nil , err
198
- }
199
- for _ , str := range strs {
200
- args = append (args , str )
267
+ if got == argSingle {
268
+ parser := & Parser {ParseEnv : false , ParseBacktick : false , Position : 0 , Dir : p .Dir }
269
+ strs , err := parser .Parse (replaceEnv (p .Getenv , buf ))
270
+ if err != nil {
271
+ return nil , err
272
+ }
273
+ for _ , str := range strs {
274
+ args = append (args , str )
275
+ }
276
+ } else {
277
+ args = append (args , replaceEnv (p .Getenv , buf ))
201
278
}
202
279
} else {
203
280
args = append (args , buf )
0 commit comments