File: shell_split.go

package info (click to toggle)
restic 0.18.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 30,824 kB
  • sloc: sh: 3,704; makefile: 50; python: 34
file content (76 lines) | stat: -rw-r--r-- 1,565 bytes parent folder | download | duplicates (2)
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
}