arikawa/bot/extras/shellwords/shellwords.go

138 lines
2.1 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.

package shellwords
import (
"fmt"
"strings"
)
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.
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
}
// // 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())
}
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)])
)
return args, &ErrParse{
Position: cursor,
ErrorStart: start,
ErrorPart: part,
ErrorEnd: end,
}
}
return args, nil
}
func isSpace(r rune) bool {
switch r {
case ' ', '\t', '\r', '\n', ' ':
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
}