summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/wiggin77/cfg/ini
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/wiggin77/cfg/ini')
-rw-r--r--vendor/github.com/wiggin77/cfg/ini/ini.go167
-rw-r--r--vendor/github.com/wiggin77/cfg/ini/parser.go142
-rw-r--r--vendor/github.com/wiggin77/cfg/ini/section.go109
3 files changed, 418 insertions, 0 deletions
diff --git a/vendor/github.com/wiggin77/cfg/ini/ini.go b/vendor/github.com/wiggin77/cfg/ini/ini.go
new file mode 100644
index 00000000..d28d7444
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/ini/ini.go
@@ -0,0 +1,167 @@
+package ini
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "sync"
+ "time"
+)
+
+// Ini provides parsing and querying of INI format or simple name/value pairs
+// such as a simple config file.
+// A name/value pair format is just an INI with no sections, and properties can
+// be queried using an empty section name.
+type Ini struct {
+ mutex sync.RWMutex
+ m map[string]*Section
+ lm time.Time
+}
+
+// LoadFromFilespec loads an INI file from string containing path and filename.
+func (ini *Ini) LoadFromFilespec(filespec string) error {
+ f, err := os.Open(filespec)
+ if err != nil {
+ return err
+ }
+ return ini.LoadFromFile(f)
+}
+
+// LoadFromFile loads an INI file from `os.File`.
+func (ini *Ini) LoadFromFile(file *os.File) error {
+
+ fi, err := file.Stat()
+ if err != nil {
+ return err
+ }
+ lm := fi.ModTime()
+
+ if err := ini.LoadFromReader(file); err != nil {
+ return err
+ }
+ ini.lm = lm
+ return nil
+}
+
+// LoadFromReader loads an INI file from an `io.Reader`.
+func (ini *Ini) LoadFromReader(reader io.Reader) error {
+ data, err := ioutil.ReadAll(reader)
+ if err != nil {
+ return err
+ }
+ return ini.LoadFromString(string(data))
+}
+
+// LoadFromString parses an INI from a string .
+func (ini *Ini) LoadFromString(s string) error {
+ m, err := getSections(s)
+ if err != nil {
+ return err
+ }
+ ini.mutex.Lock()
+ ini.m = m
+ ini.lm = time.Now()
+ ini.mutex.Unlock()
+ return nil
+}
+
+// GetLastModified returns the last modified timestamp of the
+// INI contents.
+func (ini *Ini) GetLastModified() time.Time {
+ return ini.lm
+}
+
+// GetSectionNames returns the names of all sections in this INI.
+// Note, the returned section names are a snapshot in time, meaning
+// other goroutines may change the contents of this INI as soon as
+// the method returns.
+func (ini *Ini) GetSectionNames() []string {
+ ini.mutex.RLock()
+ defer ini.mutex.RUnlock()
+
+ arr := make([]string, 0, len(ini.m))
+ for key := range ini.m {
+ arr = append(arr, key)
+ }
+ return arr
+}
+
+// GetKeys returns the names of all keys in the specified section.
+// Note, the returned key names are a snapshot in time, meaning other
+// goroutines may change the contents of this INI as soon as the
+// method returns.
+func (ini *Ini) GetKeys(sectionName string) ([]string, error) {
+ sec, err := ini.getSection(sectionName)
+ if err != nil {
+ return nil, err
+ }
+ return sec.getKeys(), nil
+}
+
+// getSection returns the named section.
+func (ini *Ini) getSection(sectionName string) (*Section, error) {
+ ini.mutex.RLock()
+ defer ini.mutex.RUnlock()
+
+ sec, ok := ini.m[sectionName]
+ if !ok {
+ return nil, fmt.Errorf("section '%s' not found", sectionName)
+ }
+ return sec, nil
+}
+
+// GetFlattenedKeys returns all section names plus keys as one
+// flattened array.
+func (ini *Ini) GetFlattenedKeys() []string {
+ ini.mutex.RLock()
+ defer ini.mutex.RUnlock()
+
+ arr := make([]string, 0, len(ini.m)*2)
+ for _, section := range ini.m {
+ keys := section.getKeys()
+ for _, key := range keys {
+ name := section.GetName()
+ if name != "" {
+ key = name + "." + key
+ }
+ arr = append(arr, key)
+ }
+ }
+ return arr
+}
+
+// GetProp returns the value of the specified key in the named section.
+func (ini *Ini) GetProp(section string, key string) (val string, ok bool) {
+ sec, err := ini.getSection(section)
+ if err != nil {
+ return val, false
+ }
+ return sec.GetProp(key)
+}
+
+// ToMap returns a flattened map of the section name plus keys mapped
+// to values.
+func (ini *Ini) ToMap() map[string]string {
+ m := make(map[string]string)
+
+ ini.mutex.RLock()
+ defer ini.mutex.RUnlock()
+
+ for _, section := range ini.m {
+ for _, key := range section.getKeys() {
+ val, ok := section.GetProp(key)
+ if ok {
+ name := section.GetName()
+ var mapkey string
+ if name != "" {
+ mapkey = name + "." + key
+ } else {
+ mapkey = key
+ }
+ m[mapkey] = val
+ }
+ }
+ }
+ return m
+}
diff --git a/vendor/github.com/wiggin77/cfg/ini/parser.go b/vendor/github.com/wiggin77/cfg/ini/parser.go
new file mode 100644
index 00000000..28916409
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/ini/parser.go
@@ -0,0 +1,142 @@
+package ini
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/wiggin77/merror"
+)
+
+// LF is linefeed
+const LF byte = 0x0A
+
+// CR is carriage return
+const CR byte = 0x0D
+
+// getSections parses an INI formatted string, or string containing just name/value pairs,
+// returns map of `Section`'s.
+//
+// Any name/value pairs appearing before a section name are added to the section named
+// with an empty string (""). Also true for Linux-style config files where all props
+// are outside a named section.
+//
+// Any errors encountered are aggregated and returned, along with the partially parsed
+// sections.
+func getSections(str string) (map[string]*Section, error) {
+ merr := merror.New()
+ mapSections := make(map[string]*Section)
+ lines := buildLineArray(str)
+ section := newSection("")
+
+ for _, line := range lines {
+ name, ok := parseSection(line)
+ if ok {
+ // A section name encountered. Stop processing the current one.
+ // Don't add the current section to the map if the section name is blank
+ // and the prop map is empty.
+ nameCurr := section.GetName()
+ if nameCurr != "" || section.hasKeys() {
+ mapSections[nameCurr] = section
+ }
+ // Start processing a new section.
+ section = newSection(name)
+ } else {
+ // Parse the property and add to the current section, or ignore if comment.
+ if k, v, comment, err := parseProp(line); !comment && err == nil {
+ section.setProp(k, v)
+ } else if err != nil {
+ merr.Append(err) // aggregate errors
+ }
+ }
+
+ }
+ // If the current section is not empty, add it.
+ if section.hasKeys() {
+ mapSections[section.GetName()] = section
+ }
+ return mapSections, merr.ErrorOrNil()
+}
+
+// buildLineArray parses the given string buffer and creates a list of strings,
+// one for each line in the string buffer.
+//
+// A line is considered to be terminated by any one of a line feed ('\n'),
+// a carriage return ('\r'), or a carriage return followed immediately by a
+// linefeed.
+//
+// Lines prefixed with ';' or '#' are considered comments and skipped.
+func buildLineArray(str string) []string {
+ arr := make([]string, 0, 10)
+ str = str + "\n"
+
+ iLen := len(str)
+ iPos, iBegin := 0, 0
+ var ch byte
+
+ for iPos < iLen {
+ ch = str[iPos]
+ if ch == LF || ch == CR {
+ sub := str[iBegin:iPos]
+ sub = strings.TrimSpace(sub)
+ if sub != "" && !strings.HasPrefix(sub, ";") && !strings.HasPrefix(sub, "#") {
+ arr = append(arr, sub)
+ }
+ iPos++
+ if ch == CR && iPos < iLen && str[iPos] == LF {
+ iPos++
+ }
+ iBegin = iPos
+ } else {
+ iPos++
+ }
+ }
+ return arr
+}
+
+// parseSection parses the specified string for a section name enclosed in square brackets.
+// Returns the section name found, or `ok=false` if `str` is not a section header.
+func parseSection(str string) (name string, ok bool) {
+ str = strings.TrimSpace(str)
+ if !strings.HasPrefix(str, "[") {
+ return "", false
+ }
+ iCloser := strings.Index(str, "]")
+ if iCloser == -1 {
+ return "", false
+ }
+ return strings.TrimSpace(str[1:iCloser]), true
+}
+
+// parseProp parses the specified string and extracts a key/value pair.
+//
+// If the string is a comment (prefixed with ';' or '#') then `comment=true`
+// and key will be empty.
+func parseProp(str string) (key string, val string, comment bool, err error) {
+ iLen := len(str)
+ iEqPos := strings.Index(str, "=")
+ if iEqPos == -1 {
+ return "", "", false, fmt.Errorf("not a key/value pair:'%s'", str)
+ }
+
+ key = str[0:iEqPos]
+ key = strings.TrimSpace(key)
+ if iEqPos+1 < iLen {
+ val = str[iEqPos+1:]
+ val = strings.TrimSpace(val)
+ }
+
+ // Check that the key has at least 1 char.
+ if key == "" {
+ return "", "", false, fmt.Errorf("key is empty for '%s'", str)
+ }
+
+ // Check if this line is a comment that just happens
+ // to have an equals sign in it. Not an error, but not a
+ // useable line either.
+ if strings.HasPrefix(key, ";") || strings.HasPrefix(key, "#") {
+ key = ""
+ val = ""
+ comment = true
+ }
+ return key, val, comment, err
+}
diff --git a/vendor/github.com/wiggin77/cfg/ini/section.go b/vendor/github.com/wiggin77/cfg/ini/section.go
new file mode 100644
index 00000000..18c4c254
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/ini/section.go
@@ -0,0 +1,109 @@
+package ini
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+)
+
+// Section represents a section in an INI file. The section has a name, which is
+// enclosed in square brackets in the file. The section also has an array of
+// key/value pairs.
+type Section struct {
+ name string
+ props map[string]string
+ mtx sync.RWMutex
+}
+
+func newSection(name string) *Section {
+ sec := &Section{}
+ sec.name = name
+ sec.props = make(map[string]string)
+ return sec
+}
+
+// addLines addes an array of strings containing name/value pairs
+// of the format `key=value`.
+//func addLines(lines []string) {
+// TODO
+//}
+
+// GetName returns the name of the section.
+func (sec *Section) GetName() (name string) {
+ sec.mtx.RLock()
+ name = sec.name
+ sec.mtx.RUnlock()
+ return
+}
+
+// GetProp returns the value associated with the given key, or
+// `ok=false` if key does not exist.
+func (sec *Section) GetProp(key string) (val string, ok bool) {
+ sec.mtx.RLock()
+ val, ok = sec.props[key]
+ sec.mtx.RUnlock()
+ return
+}
+
+// SetProp sets the value associated with the given key.
+func (sec *Section) setProp(key string, val string) {
+ sec.mtx.Lock()
+ sec.props[key] = val
+ sec.mtx.Unlock()
+}
+
+// hasKeys returns true if there are one or more properties in
+// this section.
+func (sec *Section) hasKeys() (b bool) {
+ sec.mtx.RLock()
+ b = len(sec.props) > 0
+ sec.mtx.RUnlock()
+ return
+}
+
+// getKeys returns an array containing all keys in this section.
+func (sec *Section) getKeys() []string {
+ sec.mtx.RLock()
+ defer sec.mtx.RUnlock()
+
+ arr := make([]string, len(sec.props))
+ idx := 0
+ for k := range sec.props {
+ arr[idx] = k
+ idx++
+ }
+ return arr
+}
+
+// combine the given section with this one.
+func (sec *Section) combine(sec2 *Section) {
+ sec.mtx.Lock()
+ sec2.mtx.RLock()
+ defer sec.mtx.Unlock()
+ defer sec2.mtx.RUnlock()
+
+ for k, v := range sec2.props {
+ sec.props[k] = v
+ }
+}
+
+// String returns a string representation of this section.
+func (sec *Section) String() string {
+ return fmt.Sprintf("[%s]\n%s", sec.GetName(), sec.StringPropsOnly())
+}
+
+// StringPropsOnly returns a string representation of this section
+// without the section header.
+func (sec *Section) StringPropsOnly() string {
+ sec.mtx.RLock()
+ defer sec.mtx.RUnlock()
+ sb := &strings.Builder{}
+
+ for k, v := range sec.props {
+ sb.WriteString(k)
+ sb.WriteString("=")
+ sb.WriteString(v)
+ sb.WriteString("\n")
+ }
+ return sb.String()
+}