@@ -19,12 +19,15 @@ import (
19
19
// tokens. Tries to mimic bash shell process.
20
20
// It doesn't support all flavors of ${xx:...} formats but new ones can
21
21
// be added by adding code to the "special ${} format processing" section
22
+ //
23
+ // It is not safe to call methods on a Lex instance concurrently.
22
24
type Lex struct {
23
25
escapeToken rune
24
26
RawQuotes bool
25
27
RawEscapes bool
26
28
SkipProcessQuotes bool
27
29
SkipUnsetEnv bool
30
+ shellWord shellWord
28
31
}
29
32
30
33
// NewLex creates a new Lex which uses escapeToken to escape quotes.
@@ -70,40 +73,29 @@ type ProcessWordResult struct {
70
73
// ProcessWordWithMatches will use the 'env' list of environment variables,
71
74
// replace any env var references in 'word' and return the env that were used.
72
75
func (s * Lex ) ProcessWordWithMatches (word string , env map [string ]string ) (ProcessWordResult , error ) {
73
- sw := s .init (word , env )
74
- word , words , err := sw .process (word )
75
- return ProcessWordResult {
76
- Result : word ,
77
- Words : words ,
78
- Matched : sw .matches ,
79
- Unmatched : sw .nonmatches ,
80
- }, err
76
+ return s .process (word , env )
81
77
}
82
78
83
79
func (s * Lex ) ProcessWordsWithMap (word string , env map [string ]string ) ([]string , error ) {
84
80
result , err := s .process (word , env )
85
81
return result .Words , err
86
82
}
87
83
88
- func (s * Lex ) init (word string , env map [string ]string ) * shellWord {
89
- sw := & shellWord {
90
- envs : env ,
91
- escapeToken : s .escapeToken ,
92
- skipUnsetEnv : s .SkipUnsetEnv ,
93
- skipProcessQuotes : s .SkipProcessQuotes ,
94
- rawQuotes : s .RawQuotes ,
95
- rawEscapes : s .RawEscapes ,
96
- matches : make (map [string ]struct {}),
97
- nonmatches : make (map [string ]struct {}),
98
- }
84
+ func (s * Lex ) initWord (word string , env map [string ]string ) * shellWord {
85
+ sw := & s .shellWord
86
+ sw .Lex = s
87
+ sw .envs = env
88
+ sw .rawEscapes = s .RawEscapes
89
+ sw .matches = make (map [string ]struct {}) // TODO: create these maps lazily
90
+ sw .nonmatches = make (map [string ]struct {})
99
91
sw .scanner .Init (strings .NewReader (word ))
100
92
return sw
101
93
}
102
94
103
- func (s * Lex ) process (word string , env map [string ]string ) (* ProcessWordResult , error ) {
104
- sw := s .init (word , env )
95
+ func (s * Lex ) process (word string , env map [string ]string ) (ProcessWordResult , error ) {
96
+ sw := s .initWord (word , env )
105
97
word , words , err := sw .process (word )
106
- return & ProcessWordResult {
98
+ return ProcessWordResult {
107
99
Result : word ,
108
100
Words : words ,
109
101
Matched : sw .matches ,
@@ -112,15 +104,12 @@ func (s *Lex) process(word string, env map[string]string) (*ProcessWordResult, e
112
104
}
113
105
114
106
type shellWord struct {
115
- scanner scanner.Scanner
116
- envs map [string ]string
117
- escapeToken rune
118
- rawQuotes bool
119
- rawEscapes bool
120
- skipUnsetEnv bool
121
- skipProcessQuotes bool
122
- matches map [string ]struct {}
123
- nonmatches map [string ]struct {}
107
+ * Lex
108
+ scanner scanner.Scanner
109
+ envs map [string ]string
110
+ rawEscapes bool
111
+ matches map [string ]struct {}
112
+ nonmatches map [string ]struct {}
124
113
}
125
114
126
115
func (sw * shellWord ) process (source string ) (string , []string , error ) {
@@ -185,7 +174,7 @@ func (sw *shellWord) processStopOn(stopChar rune, rawEscapes bool) (string, []st
185
174
var charFuncMapping = map [rune ]func () (string , error ){
186
175
'$' : sw .processDollar ,
187
176
}
188
- if ! sw .skipProcessQuotes {
177
+ if ! sw .SkipProcessQuotes {
189
178
charFuncMapping ['\'' ] = sw .processSingleQuote
190
179
charFuncMapping ['"' ] = sw .processDoubleQuote
191
180
}
@@ -262,7 +251,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
262
251
var result bytes.Buffer
263
252
264
253
ch := sw .scanner .Next ()
265
- if sw .rawQuotes {
254
+ if sw .RawQuotes {
266
255
result .WriteRune (ch )
267
256
}
268
257
@@ -272,7 +261,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
272
261
case scanner .EOF :
273
262
return "" , errors .New ("unexpected end of statement while looking for matching single-quote" )
274
263
case '\'' :
275
- if sw .rawQuotes {
264
+ if sw .RawQuotes {
276
265
result .WriteRune (ch )
277
266
}
278
267
return result .String (), nil
@@ -297,7 +286,7 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
297
286
var result bytes.Buffer
298
287
299
288
ch := sw .scanner .Next ()
300
- if sw .rawQuotes {
289
+ if sw .RawQuotes {
301
290
result .WriteRune (ch )
302
291
}
303
292
@@ -307,7 +296,7 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
307
296
return "" , errors .New ("unexpected end of statement while looking for matching double-quote" )
308
297
case '"' :
309
298
ch := sw .scanner .Next ()
310
- if sw .rawQuotes {
299
+ if sw .RawQuotes {
311
300
result .WriteRune (ch )
312
301
}
313
302
return result .String (), nil
@@ -351,7 +340,7 @@ func (sw *shellWord) processDollar() (string, error) {
351
340
return "$" , nil
352
341
}
353
342
value , found := sw .getEnv (name )
354
- if ! found && sw .skipUnsetEnv {
343
+ if ! found && sw .SkipUnsetEnv {
355
344
return "$" + name , nil
356
345
}
357
346
return value , nil
@@ -374,7 +363,7 @@ func (sw *shellWord) processDollar() (string, error) {
374
363
case '}' :
375
364
// Normal ${xx} case
376
365
value , set := sw .getEnv (name )
377
- if ! set && sw .skipUnsetEnv {
366
+ if ! set && sw .SkipUnsetEnv {
378
367
return fmt .Sprintf ("${%s}" , name ), nil
379
368
}
380
369
return value , nil
@@ -396,7 +385,7 @@ func (sw *shellWord) processDollar() (string, error) {
396
385
// Grab the current value of the variable in question so we
397
386
// can use it to determine what to do based on the modifier
398
387
value , set := sw .getEnv (name )
399
- if sw .skipUnsetEnv && ! set {
388
+ if sw .SkipUnsetEnv && ! set {
400
389
return fmt .Sprintf ("${%s%s%s}" , name , chs , word ), nil
401
390
}
402
391
@@ -466,7 +455,7 @@ func (sw *shellWord) processDollar() (string, error) {
466
455
}
467
456
468
457
value , set := sw .getEnv (name )
469
- if sw .skipUnsetEnv && ! set {
458
+ if sw .SkipUnsetEnv && ! set {
470
459
return fmt .Sprintf ("${%s/%s/%s}" , name , pattern , replacement ), nil
471
460
}
472
461
0 commit comments