summaryrefslogtreecommitdiff
path: root/cgitrc/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'cgitrc/parser.go')
-rw-r--r--cgitrc/parser.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/cgitrc/parser.go b/cgitrc/parser.go
new file mode 100644
index 0000000..4af57e7
--- /dev/null
+++ b/cgitrc/parser.go
@@ -0,0 +1,118 @@
+package cgitrc
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strings"
+)
+
+type parserState int
+
+const (
+ parserStateLineStart parserState = iota
+ parserStateComment
+ parserStateKey
+ parserStateValue
+)
+
+// Parser parses a cgitrc file.
+type Parser struct {
+ Fields []*ParserField
+
+ state parserState
+ keys []string
+ key bytes.Buffer
+ value bytes.Buffer
+
+ lineNo uint
+}
+
+// ParserField describes a single key-value pair.
+type ParserField struct {
+ Keys []string
+ Value string
+ LineNo uint
+}
+
+// Write pushes data to the parser.
+func (p *Parser) Write(buf []byte) (n int, err error) {
+ for _, b := range buf {
+ switch p.state {
+ case parserStateLineStart:
+ err = p.parseLineStart(b)
+ case parserStateComment:
+ if b == '\n' {
+ p.state = parserStateLineStart
+ }
+ case parserStateKey:
+ err = p.parseKey(b)
+ case parserStateValue:
+ p.parseValue(b)
+ }
+ if err != nil {
+ err = fmt.Errorf("line %d: %v", p.lineNo, err)
+ return
+ }
+ n++
+ }
+ return
+}
+
+// Close finishes parsing.
+func (p *Parser) Close() (err error) {
+ if p.state != parserStateLineStart {
+ err = errors.New("unexpected EOF")
+ }
+ return
+}
+
+func (p *Parser) parseLineStart(b byte) (err error) {
+ p.lineNo++
+ switch b {
+ case '\n':
+ case '#':
+ p.state = parserStateComment
+ default:
+ p.state = parserStateKey
+ err = p.parseKey(b)
+ }
+ return
+}
+
+func (p *Parser) parseKey(b byte) (err error) {
+ switch b {
+ case '\n':
+ err = errors.New("unexpected linefeed")
+ case '.', '=':
+ p.keys = append(p.keys, p.key.String())
+ p.key.Reset()
+ if b == '=' {
+ p.state = parserStateValue
+ }
+ default:
+ p.key.WriteByte(b)
+ }
+ return
+}
+
+func (p *Parser) parseValue(b byte) {
+ switch b {
+ case '\n':
+ p.Fields = append(p.Fields, &ParserField{
+ Keys: p.keys,
+ Value: p.value.String(),
+ LineNo: p.lineNo,
+ })
+ p.keys = nil
+ p.value.Reset()
+ p.state = parserStateLineStart
+ default:
+ p.value.WriteByte(b)
+ }
+}
+
+// Key returns the field keys joined by '.'.
+func (f ParserField) Key() string {
+ return strings.Join(f.Keys, ".")
+}