arikawa/bot/extras/shellwords/shellwords.go

138 lines
2.1 KiB
Go
Raw Normal View History

package shellwords
import (
"fmt"
"strings"
)
2020-01-26 07:17:18 +00:00
type ErrParse struct {
Position int
ErrorStart,
ErrorPart,
ErrorEnd string
}
func (e ErrParse) Error() string {
return fmt.Sprintf(
"Unexpected quote or escape: %s__%s__%s",
e.ErrorStart, e.ErrorPart, e.ErrorEnd,
)
}
// Parse parses the given text to a slice of words.
2020-01-26 07:17:18 +00:00
func Parse(line string) ([]string, error) {
var args []string
2020-01-26 07:17:18 +00:00
var escaped, doubleQuoted, singleQuoted bool
var buf strings.Builder
buf.Grow(len(line))
got := false
2020-01-26 07:17:18 +00:00
cursor := 0
2020-01-26 07:17:18 +00:00
runes := []rune(line)
for _, r := range runes {
if escaped {
buf.WriteRune(r)
escaped = false
continue
}
if r == '\\' {
if singleQuoted {
buf.WriteRune(r)
} else {
escaped = true
}
continue
}
if isSpace(r) {
switch {
2020-01-26 07:17:18 +00:00
case singleQuoted, doubleQuoted:
buf.WriteRune(r)
case got:
cursor += buf.Len()
args = append(args, buf.String())
buf.Reset()
got = false
}
continue
}
switch r {
case '"':
if !singleQuoted {
if doubleQuoted {
got = true
}
doubleQuoted = !doubleQuoted
continue
}
case '\'', '`':
if !doubleQuoted {
if singleQuoted {
got = true
}
// // If this is a backtick, then write it.
// if r == '`' {
// buf.WriteByte('`')
// }
singleQuoted = !singleQuoted
continue
}
}
got = true
buf.WriteRune(r)
}
if got {
args = append(args, buf.String())
}
2020-01-26 07:17:18 +00:00
if escaped || singleQuoted || doubleQuoted {
// the number of characters to highlight
var (
pos = cursor + 5
start = string(runes[max(cursor-100, 0) : pos-1])
end = string(runes[pos+1 : min(cursor+100, len(runes))])
part = string(runes[max(pos-1, 0):min(len(runes), pos+2)])
2020-01-26 07:17:18 +00:00
)
return args, &ErrParse{
Position: cursor,
ErrorStart: start,
ErrorPart: part,
ErrorEnd: end,
2020-01-26 07:17:18 +00:00
}
}
return args, nil
}
2020-01-26 07:17:18 +00:00
func isSpace(r rune) bool {
switch r {
case ' ', '\t', '\r', '\n', ' ':
2020-01-26 07:17:18 +00:00
return true
}
return false
}
func min(i, j int) int {
if i < j {
return i
}
return j
}
func max(i, j int) int {
if i < j {
return j
}
return i
}