12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226 |
- // Copyright 2014 Unknwon
- //
- // Licensed under the Apache License, Version 2.0 (the "License"): you may
- // not use this file except in compliance with the License. You may obtain
- // a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- // License for the specific language governing permissions and limitations
- // under the License.
- // Package ini provides INI file read and write functionality in Go.
- package ini
- import (
- "bufio"
- "bytes"
- "errors"
- "fmt"
- "io"
- "os"
- "regexp"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "time"
- )
- const (
- DEFAULT_SECTION = "DEFAULT"
- // Maximum allowed depth when recursively substituing variable names.
- _DEPTH_VALUES = 99
- _VERSION = "1.6.0"
- )
- func Version() string {
- return _VERSION
- }
- var (
- LineBreak = "\n"
- // Variable regexp pattern: %(variable)s
- varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
- // Write spaces around "=" to look better.
- PrettyFormat = true
- )
- func init() {
- if runtime.GOOS == "windows" {
- LineBreak = "\r\n"
- }
- }
- func inSlice(str string, s []string) bool {
- for _, v := range s {
- if str == v {
- return true
- }
- }
- return false
- }
- // dataSource is a interface that returns file content.
- type dataSource interface {
- ReadCloser() (io.ReadCloser, error)
- }
- type sourceFile struct {
- name string
- }
- func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
- return os.Open(s.name)
- }
- type bytesReadCloser struct {
- reader io.Reader
- }
- func (rc *bytesReadCloser) Read(p []byte) (n int, err error) {
- return rc.reader.Read(p)
- }
- func (rc *bytesReadCloser) Close() error {
- return nil
- }
- type sourceData struct {
- data []byte
- }
- func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
- return &bytesReadCloser{bytes.NewReader(s.data)}, nil
- }
- // ____ __.
- // | |/ _|____ ___.__.
- // | <_/ __ < | |
- // | | \ ___/\___ |
- // |____|__ \___ > ____|
- // \/ \/\/
- // Key represents a key under a section.
- type Key struct {
- s *Section
- Comment string
- name string
- value string
- isAutoIncr bool
- }
- // Name returns name of key.
- func (k *Key) Name() string {
- return k.name
- }
- // Value returns raw value of key for performance purpose.
- func (k *Key) Value() string {
- return k.value
- }
- // String returns string representation of value.
- func (k *Key) String() string {
- val := k.value
- if strings.Index(val, "%") == -1 {
- return val
- }
- for i := 0; i < _DEPTH_VALUES; i++ {
- vr := varPattern.FindString(val)
- if len(vr) == 0 {
- break
- }
- // Take off leading '%(' and trailing ')s'.
- noption := strings.TrimLeft(vr, "%(")
- noption = strings.TrimRight(noption, ")s")
- // Search in the same section.
- nk, err := k.s.GetKey(noption)
- if err != nil {
- // Search again in default section.
- nk, _ = k.s.f.Section("").GetKey(noption)
- }
- // Substitute by new value and take off leading '%(' and trailing ')s'.
- val = strings.Replace(val, vr, nk.value, -1)
- }
- return val
- }
- // Validate accepts a validate function which can
- // return modifed result as key value.
- func (k *Key) Validate(fn func(string) string) string {
- return fn(k.String())
- }
- // parseBool returns the boolean value represented by the string.
- //
- // It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On,
- // 0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off.
- // Any other value returns an error.
- func parseBool(str string) (value bool, err error) {
- switch str {
- case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "ON", "on", "On":
- return true, nil
- case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "OFF", "off", "Off":
- return false, nil
- }
- return false, fmt.Errorf("parsing \"%s\": invalid syntax", str)
- }
- // Bool returns bool type value.
- func (k *Key) Bool() (bool, error) {
- return parseBool(k.String())
- }
- // Float64 returns float64 type value.
- func (k *Key) Float64() (float64, error) {
- return strconv.ParseFloat(k.String(), 64)
- }
- // Int returns int type value.
- func (k *Key) Int() (int, error) {
- return strconv.Atoi(k.String())
- }
- // Int64 returns int64 type value.
- func (k *Key) Int64() (int64, error) {
- return strconv.ParseInt(k.String(), 10, 64)
- }
- // Uint returns uint type valued.
- func (k *Key) Uint() (uint, error) {
- u, e := strconv.ParseUint(k.String(), 10, 64)
- return uint(u), e
- }
- // Uint64 returns uint64 type value.
- func (k *Key) Uint64() (uint64, error) {
- return strconv.ParseUint(k.String(), 10, 64)
- }
- // Duration returns time.Duration type value.
- func (k *Key) Duration() (time.Duration, error) {
- return time.ParseDuration(k.String())
- }
- // TimeFormat parses with given format and returns time.Time type value.
- func (k *Key) TimeFormat(format string) (time.Time, error) {
- return time.Parse(format, k.String())
- }
- // Time parses with RFC3339 format and returns time.Time type value.
- func (k *Key) Time() (time.Time, error) {
- return k.TimeFormat(time.RFC3339)
- }
- // MustString returns default value if key value is empty.
- func (k *Key) MustString(defaultVal string) string {
- val := k.String()
- if len(val) == 0 {
- return defaultVal
- }
- return val
- }
- // MustBool always returns value without error,
- // it returns false if error occurs.
- func (k *Key) MustBool(defaultVal ...bool) bool {
- val, err := k.Bool()
- if len(defaultVal) > 0 && err != nil {
- return defaultVal[0]
- }
- return val
- }
- // MustFloat64 always returns value without error,
- // it returns 0.0 if error occurs.
- func (k *Key) MustFloat64(defaultVal ...float64) float64 {
- val, err := k.Float64()
- if len(defaultVal) > 0 && err != nil {
- return defaultVal[0]
- }
- return val
- }
- // MustInt always returns value without error,
- // it returns 0 if error occurs.
- func (k *Key) MustInt(defaultVal ...int) int {
- val, err := k.Int()
- if len(defaultVal) > 0 && err != nil {
- return defaultVal[0]
- }
- return val
- }
- // MustInt64 always returns value without error,
- // it returns 0 if error occurs.
- func (k *Key) MustInt64(defaultVal ...int64) int64 {
- val, err := k.Int64()
- if len(defaultVal) > 0 && err != nil {
- return defaultVal[0]
- }
- return val
- }
- // MustUint always returns value without error,
- // it returns 0 if error occurs.
- func (k *Key) MustUint(defaultVal ...uint) uint {
- val, err := k.Uint()
- if len(defaultVal) > 0 && err != nil {
- return defaultVal[0]
- }
- return val
- }
- // MustUint64 always returns value without error,
- // it returns 0 if error occurs.
- func (k *Key) MustUint64(defaultVal ...uint64) uint64 {
- val, err := k.Uint64()
- if len(defaultVal) > 0 && err != nil {
- return defaultVal[0]
- }
- return val
- }
- // MustDuration always returns value without error,
- // it returns zero value if error occurs.
- func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration {
- val, err := k.Duration()
- if len(defaultVal) > 0 && err != nil {
- return defaultVal[0]
- }
- return val
- }
- // MustTimeFormat always parses with given format and returns value without error,
- // it returns zero value if error occurs.
- func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time {
- val, err := k.TimeFormat(format)
- if len(defaultVal) > 0 && err != nil {
- return defaultVal[0]
- }
- return val
- }
- // MustTime always parses with RFC3339 format and returns value without error,
- // it returns zero value if error occurs.
- func (k *Key) MustTime(defaultVal ...time.Time) time.Time {
- return k.MustTimeFormat(time.RFC3339, defaultVal...)
- }
- // In always returns value without error,
- // it returns default value if error occurs or doesn't fit into candidates.
- func (k *Key) In(defaultVal string, candidates []string) string {
- val := k.String()
- for _, cand := range candidates {
- if val == cand {
- return val
- }
- }
- return defaultVal
- }
- // InFloat64 always returns value without error,
- // it returns default value if error occurs or doesn't fit into candidates.
- func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 {
- val := k.MustFloat64()
- for _, cand := range candidates {
- if val == cand {
- return val
- }
- }
- return defaultVal
- }
- // InInt always returns value without error,
- // it returns default value if error occurs or doesn't fit into candidates.
- func (k *Key) InInt(defaultVal int, candidates []int) int {
- val := k.MustInt()
- for _, cand := range candidates {
- if val == cand {
- return val
- }
- }
- return defaultVal
- }
- // InInt64 always returns value without error,
- // it returns default value if error occurs or doesn't fit into candidates.
- func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 {
- val := k.MustInt64()
- for _, cand := range candidates {
- if val == cand {
- return val
- }
- }
- return defaultVal
- }
- // InUint always returns value without error,
- // it returns default value if error occurs or doesn't fit into candidates.
- func (k *Key) InUint(defaultVal uint, candidates []uint) uint {
- val := k.MustUint()
- for _, cand := range candidates {
- if val == cand {
- return val
- }
- }
- return defaultVal
- }
- // InUint64 always returns value without error,
- // it returns default value if error occurs or doesn't fit into candidates.
- func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 {
- val := k.MustUint64()
- for _, cand := range candidates {
- if val == cand {
- return val
- }
- }
- return defaultVal
- }
- // InTimeFormat always parses with given format and returns value without error,
- // it returns default value if error occurs or doesn't fit into candidates.
- func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time {
- val := k.MustTimeFormat(format)
- for _, cand := range candidates {
- if val == cand {
- return val
- }
- }
- return defaultVal
- }
- // InTime always parses with RFC3339 format and returns value without error,
- // it returns default value if error occurs or doesn't fit into candidates.
- func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time {
- return k.InTimeFormat(time.RFC3339, defaultVal, candidates)
- }
- // RangeFloat64 checks if value is in given range inclusively,
- // and returns default value if it's not.
- func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 {
- val := k.MustFloat64()
- if val < min || val > max {
- return defaultVal
- }
- return val
- }
- // RangeInt checks if value is in given range inclusively,
- // and returns default value if it's not.
- func (k *Key) RangeInt(defaultVal, min, max int) int {
- val := k.MustInt()
- if val < min || val > max {
- return defaultVal
- }
- return val
- }
- // RangeInt64 checks if value is in given range inclusively,
- // and returns default value if it's not.
- func (k *Key) RangeInt64(defaultVal, min, max int64) int64 {
- val := k.MustInt64()
- if val < min || val > max {
- return defaultVal
- }
- return val
- }
- // RangeTimeFormat checks if value with given format is in given range inclusively,
- // and returns default value if it's not.
- func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time {
- val := k.MustTimeFormat(format)
- if val.Unix() < min.Unix() || val.Unix() > max.Unix() {
- return defaultVal
- }
- return val
- }
- // RangeTime checks if value with RFC3339 format is in given range inclusively,
- // and returns default value if it's not.
- func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time {
- return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max)
- }
- // Strings returns list of string devide by given delimiter.
- func (k *Key) Strings(delim string) []string {
- str := k.String()
- if len(str) == 0 {
- return []string{}
- }
- vals := strings.Split(str, delim)
- for i := range vals {
- vals[i] = strings.TrimSpace(vals[i])
- }
- return vals
- }
- // Float64s returns list of float64 devide by given delimiter.
- func (k *Key) Float64s(delim string) []float64 {
- strs := k.Strings(delim)
- vals := make([]float64, len(strs))
- for i := range strs {
- vals[i], _ = strconv.ParseFloat(strs[i], 64)
- }
- return vals
- }
- // Ints returns list of int devide by given delimiter.
- func (k *Key) Ints(delim string) []int {
- strs := k.Strings(delim)
- vals := make([]int, len(strs))
- for i := range strs {
- vals[i], _ = strconv.Atoi(strs[i])
- }
- return vals
- }
- // Int64s returns list of int64 devide by given delimiter.
- func (k *Key) Int64s(delim string) []int64 {
- strs := k.Strings(delim)
- vals := make([]int64, len(strs))
- for i := range strs {
- vals[i], _ = strconv.ParseInt(strs[i], 10, 64)
- }
- return vals
- }
- // Uints returns list of uint devide by given delimiter.
- func (k *Key) Uints(delim string) []uint {
- strs := k.Strings(delim)
- vals := make([]uint, len(strs))
- for i := range strs {
- u, _ := strconv.ParseUint(strs[i], 10, 64)
- vals[i] = uint(u)
- }
- return vals
- }
- // Uint64s returns list of uint64 devide by given delimiter.
- func (k *Key) Uint64s(delim string) []uint64 {
- strs := k.Strings(delim)
- vals := make([]uint64, len(strs))
- for i := range strs {
- vals[i], _ = strconv.ParseUint(strs[i], 10, 64)
- }
- return vals
- }
- // TimesFormat parses with given format and returns list of time.Time devide by given delimiter.
- func (k *Key) TimesFormat(format, delim string) []time.Time {
- strs := k.Strings(delim)
- vals := make([]time.Time, len(strs))
- for i := range strs {
- vals[i], _ = time.Parse(format, strs[i])
- }
- return vals
- }
- // Times parses with RFC3339 format and returns list of time.Time devide by given delimiter.
- func (k *Key) Times(delim string) []time.Time {
- return k.TimesFormat(time.RFC3339, delim)
- }
- // SetValue changes key value.
- func (k *Key) SetValue(v string) {
- k.value = v
- }
- // _________ __ .__
- // / _____/ ____ _____/ |_|__| ____ ____
- // \_____ \_/ __ \_/ ___\ __\ |/ _ \ / \
- // / \ ___/\ \___| | | ( <_> ) | \
- // /_______ /\___ >\___ >__| |__|\____/|___| /
- // \/ \/ \/ \/
- // Section represents a config section.
- type Section struct {
- f *File
- Comment string
- name string
- keys map[string]*Key
- keyList []string
- keysHash map[string]string
- }
- func newSection(f *File, name string) *Section {
- return &Section{f, "", name, make(map[string]*Key), make([]string, 0, 10), make(map[string]string)}
- }
- // Name returns name of Section.
- func (s *Section) Name() string {
- return s.name
- }
- // NewKey creates a new key to given section.
- func (s *Section) NewKey(name, val string) (*Key, error) {
- if len(name) == 0 {
- return nil, errors.New("error creating new key: empty key name")
- }
- if s.f.BlockMode {
- s.f.lock.Lock()
- defer s.f.lock.Unlock()
- }
- if inSlice(name, s.keyList) {
- s.keys[name].value = val
- return s.keys[name], nil
- }
- s.keyList = append(s.keyList, name)
- s.keys[name] = &Key{s, "", name, val, false}
- s.keysHash[name] = val
- return s.keys[name], nil
- }
- // GetKey returns key in section by given name.
- func (s *Section) GetKey(name string) (*Key, error) {
- // FIXME: change to section level lock?
- if s.f.BlockMode {
- s.f.lock.RLock()
- }
- key := s.keys[name]
- if s.f.BlockMode {
- s.f.lock.RUnlock()
- }
- if key == nil {
- // Check if it is a child-section.
- sname := s.name
- for {
- if i := strings.LastIndex(sname, "."); i > -1 {
- sname = sname[:i]
- sec, err := s.f.GetSection(sname)
- if err != nil {
- continue
- }
- return sec.GetKey(name)
- } else {
- break
- }
- }
- return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name)
- }
- return key, nil
- }
- // Key assumes named Key exists in section and returns a zero-value when not.
- func (s *Section) Key(name string) *Key {
- key, err := s.GetKey(name)
- if err != nil {
- // It's OK here because the only possible error is empty key name,
- // but if it's empty, this piece of code won't be executed.
- key, _ = s.NewKey(name, "")
- return key
- }
- return key
- }
- // Keys returns list of keys of section.
- func (s *Section) Keys() []*Key {
- keys := make([]*Key, len(s.keyList))
- for i := range s.keyList {
- keys[i] = s.Key(s.keyList[i])
- }
- return keys
- }
- // KeyStrings returns list of key names of section.
- func (s *Section) KeyStrings() []string {
- list := make([]string, len(s.keyList))
- copy(list, s.keyList)
- return list
- }
- // KeysHash returns keys hash consisting of names and values.
- func (s *Section) KeysHash() map[string]string {
- if s.f.BlockMode {
- s.f.lock.RLock()
- defer s.f.lock.RUnlock()
- }
- hash := map[string]string{}
- for key, value := range s.keysHash {
- hash[key] = value
- }
- return hash
- }
- // DeleteKey deletes a key from section.
- func (s *Section) DeleteKey(name string) {
- if s.f.BlockMode {
- s.f.lock.Lock()
- defer s.f.lock.Unlock()
- }
- for i, k := range s.keyList {
- if k == name {
- s.keyList = append(s.keyList[:i], s.keyList[i+1:]...)
- delete(s.keys, name)
- return
- }
- }
- }
- // ___________.__.__
- // \_ _____/|__| | ____
- // | __) | | | _/ __ \
- // | \ | | |_\ ___/
- // \___ / |__|____/\___ >
- // \/ \/
- // File represents a combination of a or more INI file(s) in memory.
- type File struct {
- // Should make things safe, but sometimes doesn't matter.
- BlockMode bool
- // Make sure data is safe in multiple goroutines.
- lock sync.RWMutex
- // Allow combination of multiple data sources.
- dataSources []dataSource
- // Actual data is stored here.
- sections map[string]*Section
- // To keep data in order.
- sectionList []string
- NameMapper
- }
- // newFile initializes File object with given data sources.
- func newFile(dataSources []dataSource) *File {
- return &File{
- BlockMode: true,
- dataSources: dataSources,
- sections: make(map[string]*Section),
- sectionList: make([]string, 0, 10),
- }
- }
- func parseDataSource(source interface{}) (dataSource, error) {
- switch s := source.(type) {
- case string:
- return sourceFile{s}, nil
- case []byte:
- return &sourceData{s}, nil
- default:
- return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s)
- }
- }
- // Load loads and parses from INI data sources.
- // Arguments can be mixed of file name with string type, or raw data in []byte.
- func Load(source interface{}, others ...interface{}) (_ *File, err error) {
- sources := make([]dataSource, len(others)+1)
- sources[0], err = parseDataSource(source)
- if err != nil {
- return nil, err
- }
- for i := range others {
- sources[i+1], err = parseDataSource(others[i])
- if err != nil {
- return nil, err
- }
- }
- f := newFile(sources)
- return f, f.Reload()
- }
- // Empty returns an empty file object.
- func Empty() *File {
- // Ignore error here, we sure our data is good.
- f, _ := Load([]byte(""))
- return f
- }
- // NewSection creates a new section.
- func (f *File) NewSection(name string) (*Section, error) {
- if len(name) == 0 {
- return nil, errors.New("error creating new section: empty section name")
- }
- if f.BlockMode {
- f.lock.Lock()
- defer f.lock.Unlock()
- }
- if inSlice(name, f.sectionList) {
- return f.sections[name], nil
- }
- f.sectionList = append(f.sectionList, name)
- f.sections[name] = newSection(f, name)
- return f.sections[name], nil
- }
- // NewSections creates a list of sections.
- func (f *File) NewSections(names ...string) (err error) {
- for _, name := range names {
- if _, err = f.NewSection(name); err != nil {
- return err
- }
- }
- return nil
- }
- // GetSection returns section by given name.
- func (f *File) GetSection(name string) (*Section, error) {
- if len(name) == 0 {
- name = DEFAULT_SECTION
- }
- if f.BlockMode {
- f.lock.RLock()
- defer f.lock.RUnlock()
- }
- sec := f.sections[name]
- if sec == nil {
- return nil, fmt.Errorf("error when getting section: section '%s' not exists", name)
- }
- return sec, nil
- }
- // Section assumes named section exists and returns a zero-value when not.
- func (f *File) Section(name string) *Section {
- sec, err := f.GetSection(name)
- if err != nil {
- // Note: It's OK here because the only possible error is empty section name,
- // but if it's empty, this piece of code won't be executed.
- sec, _ = f.NewSection(name)
- return sec
- }
- return sec
- }
- // Section returns list of Section.
- func (f *File) Sections() []*Section {
- sections := make([]*Section, len(f.sectionList))
- for i := range f.sectionList {
- sections[i] = f.Section(f.sectionList[i])
- }
- return sections
- }
- // SectionStrings returns list of section names.
- func (f *File) SectionStrings() []string {
- list := make([]string, len(f.sectionList))
- copy(list, f.sectionList)
- return list
- }
- // DeleteSection deletes a section.
- func (f *File) DeleteSection(name string) {
- if f.BlockMode {
- f.lock.Lock()
- defer f.lock.Unlock()
- }
- if len(name) == 0 {
- name = DEFAULT_SECTION
- }
- for i, s := range f.sectionList {
- if s == name {
- f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
- delete(f.sections, name)
- return
- }
- }
- }
- func cutComment(str string) string {
- i := strings.Index(str, "#")
- if i == -1 {
- return str
- }
- return str[:i]
- }
- func checkMultipleLines(buf *bufio.Reader, line, val, valQuote string) (string, error) {
- isEnd := false
- for {
- next, err := buf.ReadString('\n')
- if err != nil {
- if err != io.EOF {
- return "", err
- }
- isEnd = true
- }
- pos := strings.LastIndex(next, valQuote)
- if pos > -1 {
- val += next[:pos]
- break
- }
- val += next
- if isEnd {
- return "", fmt.Errorf("error parsing line: missing closing key quote from '%s' to '%s'", line, next)
- }
- }
- return val, nil
- }
- func checkContinuationLines(buf *bufio.Reader, val string) (string, bool, error) {
- isEnd := false
- for {
- valLen := len(val)
- if valLen == 0 || val[valLen-1] != '\\' {
- break
- }
- val = val[:valLen-1]
- next, err := buf.ReadString('\n')
- if err != nil {
- if err != io.EOF {
- return "", isEnd, err
- }
- isEnd = true
- }
- next = strings.TrimSpace(next)
- if len(next) == 0 {
- break
- }
- val += next
- }
- return val, isEnd, nil
- }
- // parse parses data through an io.Reader.
- func (f *File) parse(reader io.Reader) error {
- buf := bufio.NewReader(reader)
- // Handle BOM-UTF8.
- // http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
- mask, err := buf.Peek(3)
- if err == nil && len(mask) >= 3 && mask[0] == 239 && mask[1] == 187 && mask[2] == 191 {
- buf.Read(mask)
- }
- count := 1
- comments := ""
- isEnd := false
- section, err := f.NewSection(DEFAULT_SECTION)
- if err != nil {
- return err
- }
- for {
- line, err := buf.ReadString('\n')
- line = strings.TrimSpace(line)
- length := len(line)
- // Check error and ignore io.EOF just for a moment.
- if err != nil {
- if err != io.EOF {
- return fmt.Errorf("error reading next line: %v", err)
- }
- // The last line of file could be an empty line.
- if length == 0 {
- break
- }
- isEnd = true
- }
- // Skip empty lines.
- if length == 0 {
- continue
- }
- switch {
- case line[0] == '#' || line[0] == ';': // Comments.
- if len(comments) == 0 {
- comments = line
- } else {
- comments += LineBreak + line
- }
- continue
- case line[0] == '[' && line[length-1] == ']': // New sction.
- section, err = f.NewSection(strings.TrimSpace(line[1 : length-1]))
- if err != nil {
- return err
- }
- if len(comments) > 0 {
- section.Comment = comments
- comments = ""
- }
- // Reset counter.
- count = 1
- continue
- }
- // Other possibilities.
- var (
- i int
- keyQuote string
- kname string
- valQuote string
- val string
- )
- // Key name surrounded by quotes.
- if line[0] == '"' {
- if length > 6 && line[0:3] == `"""` {
- keyQuote = `"""`
- } else {
- keyQuote = `"`
- }
- } else if line[0] == '`' {
- keyQuote = "`"
- }
- if len(keyQuote) > 0 {
- qLen := len(keyQuote)
- pos := strings.Index(line[qLen:], keyQuote)
- if pos == -1 {
- return fmt.Errorf("error parsing line: missing closing key quote: %s", line)
- }
- pos = pos + qLen
- i = strings.IndexAny(line[pos:], "=:")
- if i < 0 {
- return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line)
- } else if i == pos {
- return fmt.Errorf("error parsing line: key is empty: %s", line)
- }
- i = i + pos
- kname = line[qLen:pos] // Just keep spaces inside quotes.
- } else {
- i = strings.IndexAny(line, "=:")
- if i < 0 {
- return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line)
- } else if i == 0 {
- return fmt.Errorf("error parsing line: key is empty: %s", line)
- }
- kname = strings.TrimSpace(line[0:i])
- }
- isAutoIncr := false
- // Auto increment.
- if kname == "-" {
- isAutoIncr = true
- kname = "#" + fmt.Sprint(count)
- count++
- }
- lineRight := strings.TrimSpace(line[i+1:])
- lineRightLength := len(lineRight)
- firstChar := ""
- if lineRightLength >= 2 {
- firstChar = lineRight[0:1]
- }
- if firstChar == "`" {
- valQuote = "`"
- } else if firstChar == `"` {
- if lineRightLength >= 3 && lineRight[0:3] == `"""` {
- valQuote = `"""`
- } else {
- valQuote = `"`
- }
- } else if firstChar == `'` {
- valQuote = `'`
- }
- if len(valQuote) > 0 {
- qLen := len(valQuote)
- pos := strings.LastIndex(lineRight[qLen:], valQuote)
- // For multiple-line value check.
- if pos == -1 {
- if valQuote == `"` || valQuote == `'` {
- return fmt.Errorf("error parsing line: single quote does not allow multiple-line value: %s", line)
- }
- val = lineRight[qLen:] + "\n"
- val, err = checkMultipleLines(buf, line, val, valQuote)
- if err != nil {
- return err
- }
- } else {
- val = lineRight[qLen : pos+qLen]
- }
- } else {
- val = strings.TrimSpace(cutComment(lineRight))
- val, isEnd, err = checkContinuationLines(buf, val)
- if err != nil {
- return err
- }
- }
- k, err := section.NewKey(kname, val)
- if err != nil {
- return err
- }
- k.isAutoIncr = isAutoIncr
- if len(comments) > 0 {
- k.Comment = comments
- comments = ""
- }
- if isEnd {
- break
- }
- }
- return nil
- }
- func (f *File) reload(s dataSource) error {
- r, err := s.ReadCloser()
- if err != nil {
- return err
- }
- defer r.Close()
- return f.parse(r)
- }
- // Reload reloads and parses all data sources.
- func (f *File) Reload() (err error) {
- for _, s := range f.dataSources {
- if err = f.reload(s); err != nil {
- return err
- }
- }
- return nil
- }
- // Append appends one or more data sources and reloads automatically.
- func (f *File) Append(source interface{}, others ...interface{}) error {
- ds, err := parseDataSource(source)
- if err != nil {
- return err
- }
- f.dataSources = append(f.dataSources, ds)
- for _, s := range others {
- ds, err = parseDataSource(s)
- if err != nil {
- return err
- }
- f.dataSources = append(f.dataSources, ds)
- }
- return f.Reload()
- }
- // WriteToIndent writes file content into io.Writer with given value indention.
- func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
- equalSign := "="
- if PrettyFormat {
- equalSign = " = "
- }
- // Use buffer to make sure target is safe until finish encoding.
- buf := bytes.NewBuffer(nil)
- for i, sname := range f.sectionList {
- sec := f.Section(sname)
- if len(sec.Comment) > 0 {
- if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
- sec.Comment = "; " + sec.Comment
- }
- if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil {
- return 0, err
- }
- }
- if i > 0 {
- if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
- return 0, err
- }
- } else {
- // Write nothing if default section is empty.
- if len(sec.keyList) == 0 {
- continue
- }
- }
- for _, kname := range sec.keyList {
- key := sec.Key(kname)
- if len(key.Comment) > 0 {
- if len(indent) > 0 && sname != DEFAULT_SECTION {
- buf.WriteString(indent)
- }
- if key.Comment[0] != '#' && key.Comment[0] != ';' {
- key.Comment = "; " + key.Comment
- }
- if _, err = buf.WriteString(key.Comment + LineBreak); err != nil {
- return 0, err
- }
- }
- if len(indent) > 0 && sname != DEFAULT_SECTION {
- buf.WriteString(indent)
- }
- switch {
- case key.isAutoIncr:
- kname = "-"
- case strings.Contains(kname, "`") || strings.Contains(kname, `"`):
- kname = `"""` + kname + `"""`
- case strings.Contains(kname, `=`) || strings.Contains(kname, `:`):
- kname = "`" + kname + "`"
- }
- val := key.value
- // In case key value contains "\n", "`" or "\"".
- if strings.Contains(val, "\n") || strings.Contains(val, "`") || strings.Contains(val, `"`) ||
- strings.Contains(val, "#") {
- val = `"""` + val + `"""`
- }
- if _, err = buf.WriteString(kname + equalSign + val + LineBreak); err != nil {
- return 0, err
- }
- }
- // Put a line between sections.
- if _, err = buf.WriteString(LineBreak); err != nil {
- return 0, err
- }
- }
- return buf.WriteTo(w)
- }
- // WriteTo writes file content into io.Writer.
- func (f *File) WriteTo(w io.Writer) (int64, error) {
- return f.WriteToIndent(w, "")
- }
- // SaveToIndent writes content to file system with given value indention.
- func (f *File) SaveToIndent(filename, indent string) error {
- // Note: Because we are truncating with os.Create,
- // so it's safer to save to a temporary file location and rename afte done.
- tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp"
- defer os.Remove(tmpPath)
- fw, err := os.Create(tmpPath)
- if err != nil {
- return err
- }
- if _, err = f.WriteToIndent(fw, indent); err != nil {
- fw.Close()
- return err
- }
- fw.Close()
- // Remove old file and rename the new one.
- os.Remove(filename)
- return os.Rename(tmpPath, filename)
- }
- // SaveTo writes content to file system.
- func (f *File) SaveTo(filename string) error {
- return f.SaveToIndent(filename, "")
- }
|