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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
package shellescape
import (
"strings"
"unicode"
"github.com/juju/errors"
)
func Escape(parts []string) string {
eParts := make([]string, 0, len(parts))
for _, part := range parts {
needEscape := false
for _, r := range part {
if !unicode.IsLetter(r) && !unicode.IsNumber(r) && r != '-' && r != '_' && r != '.' && r != '/' {
needEscape = true
break
}
}
if len(part) == 0 {
needEscape = true
}
if needEscape {
part = "'" + strings.Replace(part, "'", "'\"'\"'", -1) + "'"
}
eParts = append(eParts, part)
}
return strings.Join(eParts, " ")
}
type parserQuoteState int
const (
parserQuoteStateNone parserQuoteState = iota
parserQuoteStateSingle
parserQuoteStateDouble
parserQuoteStateDoubleEscaped
)
func Parse(shellCmd string) ([]string, error) {
var parts []string
partBuilder := strings.Builder{}
inPart := false
quoteState := parserQuoteStateNone
finalizePart := func() {
parts = append(parts, partBuilder.String())
partBuilder.Reset()
}
for _, r := range shellCmd {
// Sanity check, TODO: perhaps remove it
if !inPart && quoteState != parserQuoteStateNone {
panic("should never be here")
}
isSpace := unicode.IsSpace(r)
if !inPart {
if !isSpace {
inPart = true
} else {
// We're in the whitespace, nothing else to do here.
continue
}
}
switch quoteState {
case parserQuoteStateNone:
switch r {
case '\'':
quoteState = parserQuoteStateSingle
case '"':
quoteState = parserQuoteStateDouble
default:
if !isSpace {
partBuilder.WriteRune(r)
} else {
finalizePart()
inPart = false
}
}
case parserQuoteStateSingle:
switch r {
case '\'':
quoteState = parserQuoteStateNone
default:
partBuilder.WriteRune(r)
}
case parserQuoteStateDouble:
switch r {
case '"':
quoteState = parserQuoteStateNone
case '\\':
quoteState = parserQuoteStateDoubleEscaped
default:
partBuilder.WriteRune(r)
}
case parserQuoteStateDoubleEscaped:
switch r {
case '\\':
partBuilder.WriteRune(r)
case '"':
partBuilder.WriteRune(r)
default:
partBuilder.WriteRune('\\')
partBuilder.WriteRune(r)
}
quoteState = parserQuoteStateDouble
}
}
if inPart {
if quoteState != parserQuoteStateNone {
return nil, errors.Errorf("unfinished quote")
}
finalizePart()
}
return parts, nil
}
|