source: code/trunk/vendor/github.com/kballard/go-shellquote/quote.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: 2.3 KB
RevLine 
[822]1package shellquote
2
3import (
4 "bytes"
5 "strings"
6 "unicode/utf8"
7)
8
9// Join quotes each argument and joins them with a space.
10// If passed to /bin/sh, the resulting string will be split back into the
11// original arguments.
12func Join(args ...string) string {
13 var buf bytes.Buffer
14 for i, arg := range args {
15 if i != 0 {
16 buf.WriteByte(' ')
17 }
18 quote(arg, &buf)
19 }
20 return buf.String()
21}
22
23const (
24 specialChars = "\\'\"`${[|&;<>()*?!"
25 extraSpecialChars = " \t\n"
26 prefixChars = "~"
27)
28
29func quote(word string, buf *bytes.Buffer) {
30 // We want to try to produce a "nice" output. As such, we will
31 // backslash-escape most characters, but if we encounter a space, or if we
32 // encounter an extra-special char (which doesn't work with
33 // backslash-escaping) we switch over to quoting the whole word. We do this
34 // with a space because it's typically easier for people to read multi-word
35 // arguments when quoted with a space rather than with ugly backslashes
36 // everywhere.
37 origLen := buf.Len()
38
39 if len(word) == 0 {
40 // oops, no content
41 buf.WriteString("''")
42 return
43 }
44
45 cur, prev := word, word
46 atStart := true
47 for len(cur) > 0 {
48 c, l := utf8.DecodeRuneInString(cur)
49 cur = cur[l:]
50 if strings.ContainsRune(specialChars, c) || (atStart && strings.ContainsRune(prefixChars, c)) {
51 // copy the non-special chars up to this point
52 if len(cur) < len(prev) {
53 buf.WriteString(prev[0 : len(prev)-len(cur)-l])
54 }
55 buf.WriteByte('\\')
56 buf.WriteRune(c)
57 prev = cur
58 } else if strings.ContainsRune(extraSpecialChars, c) {
59 // start over in quote mode
60 buf.Truncate(origLen)
61 goto quote
62 }
63 atStart = false
64 }
65 if len(prev) > 0 {
66 buf.WriteString(prev)
67 }
68 return
69
70quote:
71 // quote mode
72 // Use single-quotes, but if we find a single-quote in the word, we need
73 // to terminate the string, emit an escaped quote, and start the string up
74 // again
75 inQuote := false
76 for len(word) > 0 {
77 i := strings.IndexRune(word, '\'')
78 if i == -1 {
79 break
80 }
81 if i > 0 {
82 if !inQuote {
83 buf.WriteByte('\'')
84 inQuote = true
85 }
86 buf.WriteString(word[0:i])
87 }
88 word = word[i+1:]
89 if inQuote {
90 buf.WriteByte('\'')
91 inQuote = false
92 }
93 buf.WriteString("\\'")
94 }
95 if len(word) > 0 {
96 if !inQuote {
97 buf.WriteByte('\'')
98 }
99 buf.WriteString(word)
100 buf.WriteByte('\'')
101 }
102}
Note: See TracBrowser for help on using the repository browser.