conf/read.go

122 lines
2.6 KiB
Go
Raw Normal View History

2010-03-28 22:44:26 +00:00
package conf
import (
"io"
"os"
"bytes"
"bufio"
"strings"
)
// ReadConfigFile reads a file and returns a new configuration representation.
// This representation can be queried with GetString, etc.
func ReadConfigFile(fname string) (c *ConfigFile, err os.Error) {
2010-04-08 17:39:05 +00:00
var file *os.File
2010-03-28 22:44:26 +00:00
if file, err = os.Open(fname); err != nil {
2010-03-28 22:44:26 +00:00
return nil, err
}
2010-04-08 17:39:05 +00:00
c = NewConfigFile()
2010-03-28 22:44:26 +00:00
if err = c.Read(file); err != nil {
return nil, err
}
if err = file.Close(); err != nil {
return nil, err
}
return c, nil
}
func ReadConfigBytes(conf []byte) (c *ConfigFile, err os.Error) {
buf := bytes.NewBuffer(conf)
2010-04-08 17:39:05 +00:00
c = NewConfigFile()
2010-03-28 22:44:26 +00:00
if err = c.Read(buf); err != nil {
return nil, err
}
2010-04-08 17:39:05 +00:00
2010-03-28 22:44:26 +00:00
return c, err
}
// Read reads an io.Reader and returns a configuration representation. This
// representation can be queried with GetString, etc.
func (c *ConfigFile) Read(reader io.Reader) (err os.Error) {
buf := bufio.NewReader(reader)
2010-04-08 17:39:05 +00:00
var section, option string
2010-03-28 22:44:26 +00:00
section = "default"
for {
2010-04-08 17:39:05 +00:00
l, buferr := buf.ReadString('\n') // parse line-by-line
2010-04-20 00:34:51 +00:00
l = strings.TrimSpace(l)
if buferr != nil {
if buferr != os.EOF {
return err
}
if len(l) == 0 {
break
}
2010-03-28 22:44:26 +00:00
}
2010-04-08 17:39:05 +00:00
2010-03-28 22:44:26 +00:00
// switch written for readability (not performance)
switch {
2010-04-08 17:39:05 +00:00
case len(l) == 0: // empty line
2010-03-28 22:44:26 +00:00
continue
2010-04-08 17:39:05 +00:00
case l[0] == '#': // comment
2010-03-28 22:44:26 +00:00
continue
2010-04-08 17:39:05 +00:00
case l[0] == ';': // comment
2010-03-28 22:44:26 +00:00
continue
2010-04-08 17:39:05 +00:00
case len(l) >= 3 && strings.ToLower(l[0:3]) == "rem": // comment (for windows users)
2010-03-28 22:44:26 +00:00
continue
2010-04-08 17:39:05 +00:00
case l[0] == '[' && l[len(l)-1] == ']': // new section
option = "" // reset multi-line value
section = strings.TrimSpace(l[1 : len(l)-1])
c.AddSection(section)
2010-03-28 22:44:26 +00:00
2010-04-08 17:39:05 +00:00
case section == "": // not new section and no section defined so far
2010-03-28 22:44:26 +00:00
return ReadError{BlankSection, l}
2010-04-08 17:39:05 +00:00
default: // other alternatives
i := strings.IndexAny(l, "=:")
2010-03-28 22:44:26 +00:00
switch {
2010-04-08 17:39:05 +00:00
case i > 0: // option and value
i := strings.IndexAny(l, "=:")
2010-04-08 17:39:05 +00:00
option = strings.TrimSpace(l[0:i])
value := strings.TrimSpace(stripComments(l[i+1:]))
c.AddOption(section, option, value)
2010-03-28 22:44:26 +00:00
2010-04-08 17:39:05 +00:00
case section != "" && option != "": // continuation of multi-line value
prev, _ := c.GetRawString(section, option)
value := strings.TrimSpace(stripComments(l))
c.AddOption(section, option, prev+"\n"+value)
2010-03-28 22:44:26 +00:00
default:
return ReadError{CouldNotParse, l}
}
}
2010-04-08 17:39:05 +00:00
2010-03-31 19:39:10 +00:00
// Reached end of file
if buferr == os.EOF {
break
}
2010-03-28 22:44:26 +00:00
}
2010-04-08 17:39:05 +00:00
return nil
2010-03-28 22:44:26 +00:00
}
2010-06-22 11:30:23 +00:00
func stripComments(l string) string {
// comments are preceded by space or TAB
for _, c := range []string{" ;", "\t;", " #", "\t#"} {
if i := strings.Index(l, c); i != -1 {
l = l[0:i]
}
}
return l
}