135 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package config
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"unicode"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	bComment = []byte{'#'}
 | |
| 	bEmpty   = []byte{}
 | |
| 	bEqual   = []byte{'='}
 | |
| 	bDQuote  = []byte{'"'}
 | |
| )
 | |
| 
 | |
| type IniConfig struct {
 | |
| }
 | |
| 
 | |
| // ParseFile creates a new Config and parses the file configuration from the
 | |
| // named file.
 | |
| func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
 | |
| 	file, err := os.Open(name)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	cfg := &IniConfigContainer{
 | |
| 		file.Name(),
 | |
| 		make(map[int][]string),
 | |
| 		make(map[string]string),
 | |
| 		make(map[string]int64),
 | |
| 		sync.RWMutex{},
 | |
| 	}
 | |
| 	cfg.Lock()
 | |
| 	defer cfg.Unlock()
 | |
| 	defer file.Close()
 | |
| 
 | |
| 	var comment bytes.Buffer
 | |
| 	buf := bufio.NewReader(file)
 | |
| 
 | |
| 	for nComment, off := 0, int64(1); ; {
 | |
| 		line, _, err := buf.ReadLine()
 | |
| 		if err == io.EOF {
 | |
| 			break
 | |
| 		}
 | |
| 		if bytes.Equal(line, bEmpty) {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		off += int64(len(line))
 | |
| 
 | |
| 		if bytes.HasPrefix(line, bComment) {
 | |
| 			line = bytes.TrimLeft(line, "#")
 | |
| 			line = bytes.TrimLeftFunc(line, unicode.IsSpace)
 | |
| 			comment.Write(line)
 | |
| 			comment.WriteByte('\n')
 | |
| 			continue
 | |
| 		}
 | |
| 		if comment.Len() != 0 {
 | |
| 			cfg.comment[nComment] = []string{comment.String()}
 | |
| 			comment.Reset()
 | |
| 			nComment++
 | |
| 		}
 | |
| 
 | |
| 		val := bytes.SplitN(line, bEqual, 2)
 | |
| 		if bytes.HasPrefix([]byte(strings.TrimSpace(string(val[1]))), bDQuote) {
 | |
| 			val[1] = bytes.Trim([]byte(strings.TrimSpace(string(val[1]))), `"`)
 | |
| 		}
 | |
| 
 | |
| 		key := strings.TrimSpace(string(val[0]))
 | |
| 		cfg.comment[nComment-1] = append(cfg.comment[nComment-1], key)
 | |
| 		cfg.data[key] = strings.TrimSpace(string(val[1]))
 | |
| 		cfg.offset[key] = off
 | |
| 	}
 | |
| 	return cfg, nil
 | |
| }
 | |
| 
 | |
| // A Config represents the configuration.
 | |
| type IniConfigContainer struct {
 | |
| 	filename string
 | |
| 	comment  map[int][]string  // id: []{comment, key...}; id 1 is for main comment.
 | |
| 	data     map[string]string // key: value
 | |
| 	offset   map[string]int64  // key: offset; for editing.
 | |
| 	sync.RWMutex
 | |
| }
 | |
| 
 | |
| // Bool returns the boolean value for a given key.
 | |
| func (c *IniConfigContainer) Bool(key string) (bool, error) {
 | |
| 	return strconv.ParseBool(c.data[key])
 | |
| }
 | |
| 
 | |
| // Int returns the integer value for a given key.
 | |
| func (c *IniConfigContainer) Int(key string) (int, error) {
 | |
| 	return strconv.Atoi(c.data[key])
 | |
| }
 | |
| 
 | |
| func (c *IniConfigContainer) Int64(key string) (int64, error) {
 | |
| 	return strconv.ParseInt(c.data[key], 10, 64)
 | |
| }
 | |
| 
 | |
| // Float returns the float value for a given key.
 | |
| func (c *IniConfigContainer) Float(key string) (float64, error) {
 | |
| 	return strconv.ParseFloat(c.data[key], 64)
 | |
| }
 | |
| 
 | |
| // String returns the string value for a given key.
 | |
| func (c *IniConfigContainer) String(key string) string {
 | |
| 	return c.data[key]
 | |
| }
 | |
| 
 | |
| // WriteValue writes a new value for key.
 | |
| func (c *IniConfigContainer) Set(key, value string) error {
 | |
| 	c.Lock()
 | |
| 	defer c.Unlock()
 | |
| 	c.data[key] = value
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
 | |
| 	if v, ok := c.data[key]; ok {
 | |
| 		return v, nil
 | |
| 	}
 | |
| 	return v, errors.New("key not find")
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	Register("ini", &IniConfig{})
 | |
| }
 |