1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2024-11-01 12:34:28 +00:00
arikawa/bot/extras/shellwords/shellwords.go
2021-06-06 12:40:24 -07:00

123 lines
2 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package shellwords
import (
"strings"
)
var escaper = strings.NewReplacer(
"__", "\\_\\_",
"\\", "\\\\",
)
// MissingCloseError is returned when the parsed line is missing a closing quote.
type MissingCloseError struct {
Position int
Words string // joined
}
func (e MissingCloseError) Error() string {
// Underline 7 characters around.
var start = e.Position
errstr := strings.Builder{}
errstr.WriteString("missing quote close")
if e.Words[start:] != "" {
errstr.WriteString(": ")
errstr.WriteString(escaper.Replace(e.Words[:start]))
errstr.WriteString("__")
errstr.WriteString(escaper.Replace(e.Words[start:]))
errstr.WriteString("__")
}
return errstr.String()
}
// Parse parses the given text to a slice of words.
func Parse(line string) ([]string, error) {
var args []string
var escaped, doubleQuoted, singleQuoted bool
var buf strings.Builder
buf.Grow(len(line))
got := false
cursor := 0
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 {
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
}
singleQuoted = !singleQuoted
continue
}
}
got = true
buf.WriteRune(r)
}
if got {
args = append(args, buf.String())
}
if escaped || singleQuoted || doubleQuoted {
return args, MissingCloseError{
Position: cursor + buf.Len(),
Words: strings.Join(args, " "),
}
}
return args, nil
}
func isSpace(r rune) bool {
switch r {
case ' ', '\t', '\r', '\n', ' ':
return true
}
return false
}