aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/src-d/gcfg/read.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/src-d/gcfg/read.go')
-rw-r--r--vendor/github.com/src-d/gcfg/read.go273
1 files changed, 273 insertions, 0 deletions
diff --git a/vendor/github.com/src-d/gcfg/read.go b/vendor/github.com/src-d/gcfg/read.go
new file mode 100644
index 0000000000..fff0448c78
--- /dev/null
+++ b/vendor/github.com/src-d/gcfg/read.go
@@ -0,0 +1,273 @@
+package gcfg
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "github.com/src-d/gcfg/scanner"
+ "github.com/src-d/gcfg/token"
+ "gopkg.in/warnings.v0"
+)
+
+var unescape = map[rune]rune{'\\': '\\', '"': '"', 'n': '\n', 't': '\t', 'b': '\b'}
+
+// no error: invalid literals should be caught by scanner
+func unquote(s string) string {
+ u, q, esc := make([]rune, 0, len(s)), false, false
+ for _, c := range s {
+ if esc {
+ uc, ok := unescape[c]
+ switch {
+ case ok:
+ u = append(u, uc)
+ fallthrough
+ case !q && c == '\n':
+ esc = false
+ continue
+ }
+ panic("invalid escape sequence")
+ }
+ switch c {
+ case '"':
+ q = !q
+ case '\\':
+ esc = true
+ default:
+ u = append(u, c)
+ }
+ }
+ if q {
+ panic("missing end quote")
+ }
+ if esc {
+ panic("invalid escape sequence")
+ }
+ return string(u)
+}
+
+func read(c *warnings.Collector, callback func(string, string, string, string, bool) error,
+ fset *token.FileSet, file *token.File, src []byte) error {
+ //
+ var s scanner.Scanner
+ var errs scanner.ErrorList
+ s.Init(file, src, func(p token.Position, m string) { errs.Add(p, m) }, 0)
+ sect, sectsub := "", ""
+ pos, tok, lit := s.Scan()
+ errfn := func(msg string) error {
+ return fmt.Errorf("%s: %s", fset.Position(pos), msg)
+ }
+ for {
+ if errs.Len() > 0 {
+ if err := c.Collect(errs.Err()); err != nil {
+ return err
+ }
+ }
+ switch tok {
+ case token.EOF:
+ return nil
+ case token.EOL, token.COMMENT:
+ pos, tok, lit = s.Scan()
+ case token.LBRACK:
+ pos, tok, lit = s.Scan()
+ if errs.Len() > 0 {
+ if err := c.Collect(errs.Err()); err != nil {
+ return err
+ }
+ }
+ if tok != token.IDENT {
+ if err := c.Collect(errfn("expected section name")); err != nil {
+ return err
+ }
+ }
+ sect, sectsub = lit, ""
+ pos, tok, lit = s.Scan()
+ if errs.Len() > 0 {
+ if err := c.Collect(errs.Err()); err != nil {
+ return err
+ }
+ }
+ if tok == token.STRING {
+ sectsub = unquote(lit)
+ if sectsub == "" {
+ if err := c.Collect(errfn("empty subsection name")); err != nil {
+ return err
+ }
+ }
+ pos, tok, lit = s.Scan()
+ if errs.Len() > 0 {
+ if err := c.Collect(errs.Err()); err != nil {
+ return err
+ }
+ }
+ }
+ if tok != token.RBRACK {
+ if sectsub == "" {
+ if err := c.Collect(errfn("expected subsection name or right bracket")); err != nil {
+ return err
+ }
+ }
+ if err := c.Collect(errfn("expected right bracket")); err != nil {
+ return err
+ }
+ }
+ pos, tok, lit = s.Scan()
+ if tok != token.EOL && tok != token.EOF && tok != token.COMMENT {
+ if err := c.Collect(errfn("expected EOL, EOF, or comment")); err != nil {
+ return err
+ }
+ }
+ // If a section/subsection header was found, ensure a
+ // container object is created, even if there are no
+ // variables further down.
+ err := c.Collect(callback(sect, sectsub, "", "", true))
+ if err != nil {
+ return err
+ }
+ case token.IDENT:
+ if sect == "" {
+ if err := c.Collect(errfn("expected section header")); err != nil {
+ return err
+ }
+ }
+ n := lit
+ pos, tok, lit = s.Scan()
+ if errs.Len() > 0 {
+ return errs.Err()
+ }
+ blank, v := tok == token.EOF || tok == token.EOL || tok == token.COMMENT, ""
+ if !blank {
+ if tok != token.ASSIGN {
+ if err := c.Collect(errfn("expected '='")); err != nil {
+ return err
+ }
+ }
+ pos, tok, lit = s.Scan()
+ if errs.Len() > 0 {
+ if err := c.Collect(errs.Err()); err != nil {
+ return err
+ }
+ }
+ if tok != token.STRING {
+ if err := c.Collect(errfn("expected value")); err != nil {
+ return err
+ }
+ }
+ v = unquote(lit)
+ pos, tok, lit = s.Scan()
+ if errs.Len() > 0 {
+ if err := c.Collect(errs.Err()); err != nil {
+ return err
+ }
+ }
+ if tok != token.EOL && tok != token.EOF && tok != token.COMMENT {
+ if err := c.Collect(errfn("expected EOL, EOF, or comment")); err != nil {
+ return err
+ }
+ }
+ }
+ err := c.Collect(callback(sect, sectsub, n, v, blank))
+ if err != nil {
+ return err
+ }
+ default:
+ if sect == "" {
+ if err := c.Collect(errfn("expected section header")); err != nil {
+ return err
+ }
+ }
+ if err := c.Collect(errfn("expected section header or variable declaration")); err != nil {
+ return err
+ }
+ }
+ }
+ panic("never reached")
+}
+
+func readInto(config interface{}, fset *token.FileSet, file *token.File,
+ src []byte) error {
+ //
+ c := warnings.NewCollector(isFatal)
+ firstPassCallback := func(s string, ss string, k string, v string, bv bool) error {
+ return set(c, config, s, ss, k, v, bv, false)
+ }
+ err := read(c, firstPassCallback, fset, file, src)
+ if err != nil {
+ return err
+ }
+ secondPassCallback := func(s string, ss string, k string, v string, bv bool) error {
+ return set(c, config, s, ss, k, v, bv, true)
+ }
+ err = read(c, secondPassCallback, fset, file, src)
+ if err != nil {
+ return err
+ }
+ return c.Done()
+}
+
+// ReadWithCallback reads gcfg formatted data from reader and calls
+// callback with each section and option found.
+//
+// Callback is called with section, subsection, option key, option value
+// and blank value flag as arguments.
+//
+// When a section is found, callback is called with nil subsection, option key
+// and option value.
+//
+// When a subsection is found, callback is called with nil option key and
+// option value.
+//
+// If blank value flag is true, it means that the value was not set for an option
+// (as opposed to set to empty string).
+//
+// If callback returns an error, ReadWithCallback terminates with an error too.
+func ReadWithCallback(reader io.Reader, callback func(string, string, string, string, bool) error) error {
+ src, err := ioutil.ReadAll(reader)
+ if err != nil {
+ return err
+ }
+
+ fset := token.NewFileSet()
+ file := fset.AddFile("", fset.Base(), len(src))
+ c := warnings.NewCollector(isFatal)
+
+ return read(c, callback, fset, file, src)
+}
+
+// ReadInto reads gcfg formatted data from reader and sets the values into the
+// corresponding fields in config.
+func ReadInto(config interface{}, reader io.Reader) error {
+ src, err := ioutil.ReadAll(reader)
+ if err != nil {
+ return err
+ }
+ fset := token.NewFileSet()
+ file := fset.AddFile("", fset.Base(), len(src))
+ return readInto(config, fset, file, src)
+}
+
+// ReadStringInto reads gcfg formatted data from str and sets the values into
+// the corresponding fields in config.
+func ReadStringInto(config interface{}, str string) error {
+ r := strings.NewReader(str)
+ return ReadInto(config, r)
+}
+
+// ReadFileInto reads gcfg formatted data from the file filename and sets the
+// values into the corresponding fields in config.
+func ReadFileInto(config interface{}, filename string) error {
+ f, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ src, err := ioutil.ReadAll(f)
+ if err != nil {
+ return err
+ }
+ fset := token.NewFileSet()
+ file := fset.AddFile(filename, fset.Base(), len(src))
+ return readInto(config, fset, file, src)
+}