M go.mod => go.mod +1 -4
@@ 2,7 2,4 @@ module git.sr.ht/~emersion/go-scfg
go 1.15
-require (
- github.com/davecgh/go-spew v1.1.1
- github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
-)
+require github.com/davecgh/go-spew v1.1.1
M go.sum => go.sum +0 -2
@@ 1,4 1,2 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
M reader.go => reader.go +48 -3
@@ 6,8 6,7 @@ import (
"fmt"
"io"
"os"
-
- "github.com/google/shlex"
+ "strings"
)
// Block is a list of directives.
@@ 87,7 86,7 @@ func Read(r io.Reader) (Block, error) {
func readBlock(scanner *bufio.Scanner) (block Block, closingBrace bool, err error) {
for scanner.Scan() {
l := scanner.Text()
- words, err := shlex.Split(l)
+ words, err := splitWords(l)
if err != nil {
return nil, false, fmt.Errorf("failed to parse configuration file: %v", err)
} else if len(words) == 0 {
@@ 130,3 129,49 @@ func readBlock(scanner *bufio.Scanner) (block Block, closingBrace bool, err erro
return block, closingBrace, nil
}
+
+func splitWords(l string) ([]string, error) {
+ var (
+ words []string
+ sb strings.Builder
+ escape bool
+ quote rune
+ wantWSP bool
+ )
+ for _, ch := range l {
+ switch {
+ case escape:
+ sb.WriteRune(ch)
+ escape = false
+ case wantWSP && (ch != ' ' && ch != '\t'):
+ return words, fmt.Errorf("atom not allowed after quoted string")
+ case ch == '\\':
+ escape = true
+ case quote != 0 && ch == quote:
+ quote = 0
+ wantWSP = true
+ case quote == 0 && len(words) == 0 && sb.Len() == 0 && ch == '#':
+ return nil, nil
+ case quote == 0 && (ch == '\'' || ch == '"'):
+ if sb.Len() > 0 {
+ return words, fmt.Errorf("quoted string not allowed after atom")
+ }
+ quote = ch
+ case quote == 0 && (ch == ' ' || ch == '\t'):
+ if sb.Len() > 0 {
+ words = append(words, sb.String())
+ }
+ sb.Reset()
+ wantWSP = false
+ default:
+ sb.WriteRune(ch)
+ }
+ }
+ if quote != 0 {
+ return words, fmt.Errorf("unterminated quoted string")
+ }
+ if sb.Len() > 0 {
+ words = append(words, sb.String())
+ }
+ return words, nil
+}
M reader_test.go => reader_test.go +7 -0
@@ 128,6 128,13 @@ block4 {
},
},
},
+ {
+ name: "quotes",
+ src: `"a \b ' \" c" 'd \e \' " f' a\"b`,
+ want: Block{
+ {Name: "a b ' \" c", Params: []string{"d e ' \" f", "a\"b"}},
+ },
+ },
}
func TestRead(t *testing.T) {