func (c *ConfigFile) MustValue(section, key string, defaultVal ...string) string {
	val, err := c.GetValue(section, key)
	if len(defaultVal) > 0 && (err != nil || len(val) == 0) {
		return defaultVal[0]
	}
	return val
}
MustValue always returns value without error.
It returns empty string if error occurs, or the default value if given.
package goconfig
import (
	"fmt"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
)
const (
	DEFAULT_SECTION = "DEFAULT"
	_DEPTH_VALUES = 200
)
type ParseError int
const (
	ERR_SECTION_NOT_FOUND ParseError = iota + 1
	ERR_KEY_NOT_FOUND
	ERR_BLANK_SECTION_NAME
	ERR_COULD_NOT_PARSE
)
var LineBreak = "\n"
var varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
func init() {
	if runtime.GOOS == "windows" {
		LineBreak = "\r\n"
	}
}
type ConfigFile struct {
	lock      sync.RWMutex                  
	fileNames []string                      
	data      map[string]map[string]string  
	sectionList []string             
	keyList     map[string][]string  
	sectionComments map[string]string             
	keyComments     map[string]map[string]string  
	BlockMode       bool                          
}
func newConfigFile(fileNames []string) *ConfigFile {
	c := new(ConfigFile)
	c.fileNames = fileNames
	c.data = make(map[string]map[string]string)
	c.keyList = make(map[string][]string)
	c.sectionComments = make(map[string]string)
	c.keyComments = make(map[string]map[string]string)
	c.BlockMode = true
	return c
}
func (c *ConfigFile) SetValue(section, key, value string) bool {
	if len(section) == 0 {
		section = DEFAULT_SECTION
	}
	if len(key) == 0 {
		return false
	}
	if c.BlockMode {
		c.lock.Lock()
		defer c.lock.Unlock()
	}
	if _, ok := c.data[section]; !ok {
		c.data[section] = make(map[string]string)
		c.sectionList = append(c.sectionList, section)
	}
	_, ok := c.data[section][key]
	c.data[section][key] = value
	if !ok {
		c.keyList[section] = append(c.keyList[section], key)
	}
	return !ok
}
func (c *ConfigFile) DeleteKey(section, key string) bool {
	if len(section) == 0 {
		section = DEFAULT_SECTION
	}
	if c.BlockMode {
		c.lock.Lock()
		defer c.lock.Unlock()
	}
	if _, ok := c.data[section]; !ok {
		return false
	}
	if _, ok := c.data[section][key]; ok {
		delete(c.data[section], key)
		c.SetKeyComments(section, key, "")
		i := 0
		for _, keyName := range c.keyList[section] {
			if keyName == key {
				break
			}
			i++
		}
		c.keyList[section] = append(c.keyList[section][:i], c.keyList[section][i+1:]...)
		return true
	}
	return false
}
func (c *ConfigFile) GetValue(section, key string) (string, error) {
	if c.BlockMode {
		c.lock.RLock()
		defer c.lock.RUnlock()
	}
	if len(section) == 0 {
		section = DEFAULT_SECTION
	}
	if _, ok := c.data[section]; !ok {
		return "", getError{ERR_SECTION_NOT_FOUND, section}
	}
	value, ok := c.data[section][key]
	if !ok {
		if i := strings.LastIndex(section, "."); i > -1 {
			return c.GetValue(section[:i], key)
		}
		return "", getError{ERR_KEY_NOT_FOUND, key}
	}
	var i int
	for i = 0; i < _DEPTH_VALUES; i++ {
		vr := varPattern.FindString(value)
		if len(vr) == 0 {
			break
		}
		noption := strings.TrimLeft(vr, "%(")
		noption = strings.TrimRight(noption, ")s")
		nvalue, err := c.GetValue(DEFAULT_SECTION, noption)
		if err != nil && section != DEFAULT_SECTION {
			if _, ok := c.data[section][noption]; ok {
				nvalue = c.data[section][noption]
			}
		}
		value = strings.Replace(value, vr, nvalue, -1)
	}
	return value, nil
}
func (c *ConfigFile) Bool(section, key string) (bool, error) {
	value, err := c.GetValue(section, key)
	if err != nil {
		return false, err
	}
	return strconv.ParseBool(value)
}
func (c *ConfigFile) Float64(section, key string) (float64, error) {
	value, err := c.GetValue(section, key)
	if err != nil {
		return 0.0, err
	}
	return strconv.ParseFloat(value, 64)
}
func (c *ConfigFile) Int(section, key string) (int, error) {
	value, err := c.GetValue(section, key)
	if err != nil {
		return 0, err
	}
	return strconv.Atoi(value)
}
func (c *ConfigFile) Int64(section, key string) (int64, error) {
	value, err := c.GetValue(section, key)
	if err != nil {
		return 0, err
	}
	return strconv.ParseInt(value, 10, 64)
}