package rules import ( "bytes" "unicode" E "github.com/yusing/go-proxy/internal/error" ) var escapedChars = map[rune]rune{ 'n': '\n', 't': '\t', 'r': '\r', '\'': '\'', '"': '"', '\\': '\\', '$': '$', ' ': ' ', } // parse expression to subject and args // with support for quotes and escaped chars, e.g. // // error 403 "Forbidden 'foo' 'bar'" // error 403 Forbidden\ \"foo\"\ \"bar\". func parse(v string) (subject string, args []string, err E.Error) { buf := bytes.NewBuffer(make([]byte, 0, len(v))) escaped := false quote := rune(0) flush := func(quoted bool) { part := buf.String() if !quoted { beg := 0 for i, r := range part { if unicode.IsSpace(r) { beg = i + 1 } else { break } } if beg == len(part) { // all spaces return } part = part[beg:] // trim leading spaces } if subject == "" { subject = part } else { args = append(args, part) } buf.Reset() } for _, r := range v { if escaped { if ch, ok := escapedChars[r]; ok { buf.WriteRune(ch) } else { err = ErrUnsupportedEscapeChar.Subjectf("\\%c", r) return } escaped = false continue } switch r { case '\\': escaped = true continue case '"', '\'': switch { case quote == 0: quote = r flush(false) case r == quote: quote = 0 flush(true) default: buf.WriteRune(r) } case ' ': if quote == 0 { flush(false) continue } fallthrough default: buf.WriteRune(r) } } if quote != 0 { err = ErrUnterminatedQuotes } else { flush(false) } return }