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
|
package backend
import (
"unicode"
"github.com/restic/restic/internal/errors"
)
// shellSplitter splits a command string into separated arguments. It supports
// single and double quoted strings.
type shellSplitter struct {
quote rune
lastChar rune
}
func (s *shellSplitter) isSplitChar(c rune) bool {
// only test for quotes if the last char was not a backslash
if s.lastChar != '\\' {
// quote ended
if s.quote != 0 && c == s.quote {
s.quote = 0
return true
}
// quote starts
if s.quote == 0 && (c == '"' || c == '\'') {
s.quote = c
return true
}
}
s.lastChar = c
// within quote
if s.quote != 0 {
return false
}
// outside quote
return c == '\\' || unicode.IsSpace(c)
}
// SplitShellStrings returns the list of shell strings from a shell command string.
func SplitShellStrings(data string) (strs []string, err error) {
s := &shellSplitter{}
// derived from strings.SplitFunc
fieldStart := -1 // Set to -1 when looking for start of field.
for i, r := range data {
if s.isSplitChar(r) {
if fieldStart >= 0 {
strs = append(strs, data[fieldStart:i])
fieldStart = -1
}
} else if fieldStart == -1 {
fieldStart = i
}
}
if fieldStart >= 0 { // Last field might end at EOF.
strs = append(strs, data[fieldStart:])
}
switch s.quote {
case '\'':
return nil, errors.New("single-quoted string not terminated")
case '"':
return nil, errors.New("double-quoted string not terminated")
}
if len(strs) == 0 {
return nil, errors.New("command string is empty")
}
return strs, nil
}
|