source: code/trunk/vendor/github.com/kballard/go-shellquote/unquote.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 3.6 KB
RevLine 
[822]1package shellquote
2
3import (
4 "bytes"
5 "errors"
6 "strings"
7 "unicode/utf8"
8)
9
10var (
11 UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
12 UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
13 UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
14)
15
16var (
17 splitChars = " \n\t"
18 singleChar = '\''
19 doubleChar = '"'
20 escapeChar = '\\'
21 doubleEscapeChars = "$`\"\n\\"
22)
23
24// Split splits a string according to /bin/sh's word-splitting rules. It
25// supports backslash-escapes, single-quotes, and double-quotes. Notably it does
26// not support the $'' style of quoting. It also doesn't attempt to perform any
27// other sort of expansion, including brace expansion, shell expansion, or
28// pathname expansion.
29//
30// If the given input has an unterminated quoted string or ends in a
31// backslash-escape, one of UnterminatedSingleQuoteError,
32// UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
33func Split(input string) (words []string, err error) {
34 var buf bytes.Buffer
35 words = make([]string, 0)
36
37 for len(input) > 0 {
38 // skip any splitChars at the start
39 c, l := utf8.DecodeRuneInString(input)
40 if strings.ContainsRune(splitChars, c) {
41 input = input[l:]
42 continue
43 } else if c == escapeChar {
44 // Look ahead for escaped newline so we can skip over it
45 next := input[l:]
46 if len(next) == 0 {
47 err = UnterminatedEscapeError
48 return
49 }
50 c2, l2 := utf8.DecodeRuneInString(next)
51 if c2 == '\n' {
52 input = next[l2:]
53 continue
54 }
55 }
56
57 var word string
58 word, input, err = splitWord(input, &buf)
59 if err != nil {
60 return
61 }
62 words = append(words, word)
63 }
64 return
65}
66
67func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
68 buf.Reset()
69
70raw:
71 {
72 cur := input
73 for len(cur) > 0 {
74 c, l := utf8.DecodeRuneInString(cur)
75 cur = cur[l:]
76 if c == singleChar {
77 buf.WriteString(input[0 : len(input)-len(cur)-l])
78 input = cur
79 goto single
80 } else if c == doubleChar {
81 buf.WriteString(input[0 : len(input)-len(cur)-l])
82 input = cur
83 goto double
84 } else if c == escapeChar {
85 buf.WriteString(input[0 : len(input)-len(cur)-l])
86 input = cur
87 goto escape
88 } else if strings.ContainsRune(splitChars, c) {
89 buf.WriteString(input[0 : len(input)-len(cur)-l])
90 return buf.String(), cur, nil
91 }
92 }
93 if len(input) > 0 {
94 buf.WriteString(input)
95 input = ""
96 }
97 goto done
98 }
99
100escape:
101 {
102 if len(input) == 0 {
103 return "", "", UnterminatedEscapeError
104 }
105 c, l := utf8.DecodeRuneInString(input)
106 if c == '\n' {
107 // a backslash-escaped newline is elided from the output entirely
108 } else {
109 buf.WriteString(input[:l])
110 }
111 input = input[l:]
112 }
113 goto raw
114
115single:
116 {
117 i := strings.IndexRune(input, singleChar)
118 if i == -1 {
119 return "", "", UnterminatedSingleQuoteError
120 }
121 buf.WriteString(input[0:i])
122 input = input[i+1:]
123 goto raw
124 }
125
126double:
127 {
128 cur := input
129 for len(cur) > 0 {
130 c, l := utf8.DecodeRuneInString(cur)
131 cur = cur[l:]
132 if c == doubleChar {
133 buf.WriteString(input[0 : len(input)-len(cur)-l])
134 input = cur
135 goto raw
136 } else if c == escapeChar {
137 // bash only supports certain escapes in double-quoted strings
138 c2, l2 := utf8.DecodeRuneInString(cur)
139 cur = cur[l2:]
140 if strings.ContainsRune(doubleEscapeChars, c2) {
141 buf.WriteString(input[0 : len(input)-len(cur)-l-l2])
142 if c2 == '\n' {
143 // newline is special, skip the backslash entirely
144 } else {
145 buf.WriteRune(c2)
146 }
147 input = cur
148 }
149 }
150 }
151 return "", "", UnterminatedDoubleQuoteError
152 }
153
154done:
155 return buf.String(), input, nil
156}
Note: See TracBrowser for help on using the repository browser.