123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- // Package ini provides functions for parsing INI configuration files.
- package ini
- import (
- "bufio"
- "fmt"
- "io"
- "os"
- "regexp"
- "strings"
- )
- var (
- sectionRegex = regexp.MustCompile(`^\[(.*)\]$`)
- assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
- )
- // ErrSyntax is returned when there is a syntax error in an INI file.
- type ErrSyntax struct {
- Line int
- Source string // The contents of the erroneous line, without leading or trailing whitespace
- }
- func (e ErrSyntax) Error() string {
- return fmt.Sprintf("invalid INI syntax on line %d: %s", e.Line, e.Source)
- }
- // A File represents a parsed INI file.
- type File map[string]Section
- // A Section represents a single section of an INI file.
- type Section map[string]string
- // Returns a named Section. A Section will be created if one does not already exist for the given name.
- func (f File) Section(name string) Section {
- section := f[name]
- if section == nil {
- section = make(Section)
- f[name] = section
- }
- return section
- }
- // Looks up a value for a key in a section and returns that value, along with a boolean result similar to a map lookup.
- func (f File) Get(section, key string) (value string, ok bool) {
- if s := f[section]; s != nil {
- value, ok = s[key]
- }
- return
- }
- // Loads INI data from a reader and stores the data in the File.
- func (f File) Load(in io.Reader) (err error) {
- bufin, ok := in.(*bufio.Reader)
- if !ok {
- bufin = bufio.NewReader(in)
- }
- return parseFile(bufin, f)
- }
- // Loads INI data from a named file and stores the data in the File.
- func (f File) LoadFile(file string) (err error) {
- in, err := os.Open(file)
- if err != nil {
- return
- }
- defer in.Close()
- return f.Load(in)
- }
- func parseFile(in *bufio.Reader, file File) (err error) {
- section := ""
- lineNum := 0
- for done := false; !done; {
- var line string
- if line, err = in.ReadString('\n'); err != nil {
- if err == io.EOF {
- done = true
- } else {
- return
- }
- }
- lineNum++
- line = strings.TrimSpace(line)
- if len(line) == 0 {
- // Skip blank lines
- continue
- }
- if line[0] == ';' || line[0] == '#' {
- // Skip comments
- continue
- }
- if groups := assignRegex.FindStringSubmatch(line); groups != nil {
- key, val := groups[1], groups[2]
- key, val = strings.TrimSpace(key), strings.TrimSpace(val)
- file.Section(section)[key] = val
- } else if groups := sectionRegex.FindStringSubmatch(line); groups != nil {
- name := strings.TrimSpace(groups[1])
- section = name
- // Create the section if it does not exist
- file.Section(section)
- } else {
- return ErrSyntax{lineNum, line}
- }
- }
- return nil
- }
- // Loads and returns a File from a reader.
- func Load(in io.Reader) (File, error) {
- file := make(File)
- err := file.Load(in)
- return file, err
- }
- // Loads and returns an INI File from a file on disk.
- func LoadFile(filename string) (File, error) {
- file := make(File)
- err := file.LoadFile(filename)
- return file, err
- }
|