func (c *Config) HasSection(section string) bool {
	if c == nil || c.mx == nil {
		return false
	}
	c.mx.RLock()
	defer c.mx.RUnlock()
	return c.data[strings.ToLower(section)] == "!"
}
HasSection checks if section exist
package knf
import (
	"errors"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)
type Config struct {
	sections []string
	props    []string
	data     map[string]string
	file     string
	mx *sync.RWMutex
}
type Validator struct {
	Property string             
	Func     PropertyValidator  
	Value    interface{}        
}
type PropertyValidator func(config *Config, prop string, value interface{}) error
var (
	ErrConfigIsNil = errors.New("Config struct is nil")
	ErrFileNotSet  = errors.New("Path to config file is empty (non initialized struct?)")
)
var global *Config
func Global(file string) error {
	config, err := Read(file)
	if err != nil {
		return err
	}
	global = config
	return nil
}
func Read(file string) (*Config, error) {
	return readKNFFile(file)
}
func Reload() (map[string]bool, error) {
	if global == nil {
		return nil, ErrConfigIsNil
	}
	return global.Reload()
}
func GetS(name string, defvals ...string) string {
	if global == nil {
		if len(defvals) == 0 {
			return ""
		}
		return defvals[0]
	}
	return global.GetS(name, defvals...)
}
func GetI(name string, defvals ...int) int {
	if global == nil {
		if len(defvals) == 0 {
			return 0
		}
		return defvals[0]
	}
	return global.GetI(name, defvals...)
}
func GetI64(name string, defvals ...int64) int64 {
	if global == nil {
		if len(defvals) == 0 {
			return 0
		}
		return defvals[0]
	}
	return global.GetI64(name, defvals...)
}
func GetU(name string, defvals ...uint) uint {
	if global == nil {
		if len(defvals) == 0 {
			return 0
		}
		return defvals[0]
	}
	return global.GetU(name, defvals...)
}
func GetU64(name string, defvals ...uint64) uint64 {
	if global == nil {
		if len(defvals) == 0 {
			return 0
		}
		return defvals[0]
	}
	return global.GetU64(name, defvals...)
}
func GetF(name string, defvals ...float64) float64 {
	if global == nil {
		if len(defvals) == 0 {
			return 0.0
		}
		return defvals[0]
	}
	return global.GetF(name, defvals...)
}
func GetB(name string, defvals ...bool) bool {
	if global == nil {
		if len(defvals) == 0 {
			return false
		}
		return defvals[0]
	}
	return global.GetB(name, defvals...)
}
func GetM(name string, defvals ...os.FileMode) os.FileMode {
	if global == nil {
		if len(defvals) == 0 {
			return os.FileMode(0)
		}
		return defvals[0]
	}
	return global.GetM(name, defvals...)
}
func GetD(name string, defvals ...time.Duration) time.Duration {
	if global == nil {
		if len(defvals) == 0 {
			return time.Duration(0)
		}
		return defvals[0]
	}
	return global.GetD(name, defvals...)
}
func HasSection(section string) bool {
	if global == nil {
		return false
	}
	return global.HasSection(section)
}
func HasProp(name string) bool {
	if global == nil {
		return false
	}
	return global.HasProp(name)
}
func Sections() []string {
	if global == nil {
		return nil
	}
	return global.Sections()
}
func Props(section string) []string {
	if global == nil {
		return nil
	}
	return global.Props(section)
}
func Validate(validators []*Validator) []error {
	if global == nil {
		return []error{ErrConfigIsNil}
	}
	return global.Validate(validators)
}
func (c *Config) Reload() (map[string]bool, error) {
	if c == nil || c.mx == nil {
		return nil, ErrConfigIsNil
	}
	if c.file == "" {
		return nil, ErrFileNotSet
	}
	nc, err := Read(c.file)
	if err != nil {
		return nil, err
	}
	changes := make(map[string]bool)
	c.mx.RLock()
	for _, prop := range c.props {
		changes[prop] = c.GetS(prop) != nc.GetS(prop)
	}
	c.mx.RUnlock()
	c.mx.Lock()
	c.data, c.sections, c.props = nc.data, nc.sections, nc.props
	c.mx.Unlock()
	return changes, nil
}
func (c *Config) GetS(name string, defvals ...string) string {
	if c == nil || c.mx == nil {
		if len(defvals) == 0 {
			return ""
		}
		return defvals[0]
	}
	c.mx.RLock()
	val := c.data[strings.ToLower(name)]
	c.mx.RUnlock()
	if val == "" {
		if len(defvals) == 0 {
			return ""
		}
		return defvals[0]
	}
	return val
}
func (c *Config) GetI64(name string, defvals ...int64) int64 {
	if c == nil || c.mx == nil {
		if len(defvals) == 0 {
			return 0
		}
		return defvals[0]
	}
	c.mx.RLock()
	val := c.data[strings.ToLower(name)]
	c.mx.RUnlock()
	if val == "" {
		if len(defvals) == 0 {
			return 0
		}
		return defvals[0]
	}
	if len(val) >= 3 && val[0:2] == "0x" {
		valHex, err := strconv.ParseInt(val[2:], 16, 0)
		if err != nil {
			return 0
		}
		return valHex
	}
	valInt, err := strconv.ParseInt(val, 10, 0)
	if err != nil {
		return 0
	}
	return valInt
}
func (c *Config) GetI(name string, defvals ...int) int {
	if len(defvals) != 0 {
		return int(c.GetI64(name, int64(defvals[0])))
	}
	return int(c.GetI64(name))
}
func (c *Config) GetU(name string, defvals ...uint) uint {
	if len(defvals) != 0 {
		return uint(c.GetI64(name, int64(defvals[0])))
	}
	return uint(c.GetI64(name))
}
func (c *Config) GetU64(name string, defvals ...uint64) uint64 {
	if len(defvals) != 0 {
		return uint64(c.GetI64(name, int64(defvals[0])))
	}
	return uint64(c.GetI64(name))
}
func (c *Config) GetF(name string, defvals ...float64) float64 {
	if c == nil || c.mx == nil {
		if len(defvals) == 0 {
			return 0.0
		}
		return defvals[0]
	}
	c.mx.RLock()
	val := c.data[strings.ToLower(name)]
	c.mx.RUnlock()
	if val == "" {
		if len(defvals) == 0 {
			return 0.0
		}
		return defvals[0]
	}
	valFl, err := strconv.ParseFloat(val, 64)
	if err != nil {
		return 0.0
	}
	return valFl
}
func (c *Config) GetB(name string, defvals ...bool) bool {
	if c == nil || c.mx == nil {
		if len(defvals) == 0 {
			return false
		}
		return defvals[0]
	}
	c.mx.RLock()
	val := c.data[strings.ToLower(name)]
	c.mx.RUnlock()
	if val == "" {
		if len(defvals) == 0 {
			return false
		}
		return defvals[0]
	}
	switch val {
	case "", "0", "false", "no":
		return false
	default:
		return true
	}
}
func (c *Config) GetM(name string, defvals ...os.FileMode) os.FileMode {
	if c == nil || c.mx == nil {
		if len(defvals) == 0 {
			return os.FileMode(0)
		}
		return defvals[0]
	}
	c.mx.RLock()
	val := c.data[strings.ToLower(name)]
	c.mx.RUnlock()
	if val == "" {
		if len(defvals) == 0 {
			return os.FileMode(0)
		}
		return defvals[0]
	}
	valM, err := strconv.ParseUint(val, 8, 32)
	if err != nil {
		return 0
	}
	return os.FileMode(valM)
}
func (c *Config) GetD(name string, defvals ...time.Duration) time.Duration {
	if c == nil || c.mx == nil {
		if len(defvals) == 0 {
			return time.Duration(0)
		}
		return defvals[0]
	}
	c.mx.RLock()
	val := c.data[strings.ToLower(name)]
	c.mx.RUnlock()
	if val == "" {
		if len(defvals) == 0 {
			return time.Duration(0)
		}
		return defvals[0]
	}
	return time.Duration(c.GetI64(name)) * time.Second
}