...

Source file src/gopkg.in/ini.v1/file.go

Documentation: gopkg.in/ini.v1

     1  // Copyright 2017 Unknwon
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  package ini
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  	"io/ioutil"
    23  	"os"
    24  	"strings"
    25  	"sync"
    26  )
    27  
    28  // File represents a combination of one or more INI files in memory.
    29  type File struct {
    30  	options     LoadOptions
    31  	dataSources []dataSource
    32  
    33  	// Should make things safe, but sometimes doesn't matter.
    34  	BlockMode bool
    35  	lock      sync.RWMutex
    36  
    37  	// To keep data in order.
    38  	sectionList []string
    39  	// To keep track of the index of a section with same name.
    40  	// This meta list is only used with non-unique section names are allowed.
    41  	sectionIndexes []int
    42  
    43  	// Actual data is stored here.
    44  	sections map[string][]*Section
    45  
    46  	NameMapper
    47  	ValueMapper
    48  }
    49  
    50  // newFile initializes File object with given data sources.
    51  func newFile(dataSources []dataSource, opts LoadOptions) *File {
    52  	if len(opts.KeyValueDelimiters) == 0 {
    53  		opts.KeyValueDelimiters = "=:"
    54  	}
    55  	if len(opts.KeyValueDelimiterOnWrite) == 0 {
    56  		opts.KeyValueDelimiterOnWrite = "="
    57  	}
    58  	if len(opts.ChildSectionDelimiter) == 0 {
    59  		opts.ChildSectionDelimiter = "."
    60  	}
    61  
    62  	return &File{
    63  		BlockMode:   true,
    64  		dataSources: dataSources,
    65  		sections:    make(map[string][]*Section),
    66  		options:     opts,
    67  	}
    68  }
    69  
    70  // Empty returns an empty file object.
    71  func Empty(opts ...LoadOptions) *File {
    72  	var opt LoadOptions
    73  	if len(opts) > 0 {
    74  		opt = opts[0]
    75  	}
    76  
    77  	// Ignore error here, we are sure our data is good.
    78  	f, _ := LoadSources(opt, []byte(""))
    79  	return f
    80  }
    81  
    82  // NewSection creates a new section.
    83  func (f *File) NewSection(name string) (*Section, error) {
    84  	if len(name) == 0 {
    85  		return nil, errors.New("empty section name")
    86  	}
    87  
    88  	if (f.options.Insensitive || f.options.InsensitiveSections) && name != DefaultSection {
    89  		name = strings.ToLower(name)
    90  	}
    91  
    92  	if f.BlockMode {
    93  		f.lock.Lock()
    94  		defer f.lock.Unlock()
    95  	}
    96  
    97  	if !f.options.AllowNonUniqueSections && inSlice(name, f.sectionList) {
    98  		return f.sections[name][0], nil
    99  	}
   100  
   101  	f.sectionList = append(f.sectionList, name)
   102  
   103  	// NOTE: Append to indexes must happen before appending to sections,
   104  	// otherwise index will have off-by-one problem.
   105  	f.sectionIndexes = append(f.sectionIndexes, len(f.sections[name]))
   106  
   107  	sec := newSection(f, name)
   108  	f.sections[name] = append(f.sections[name], sec)
   109  
   110  	return sec, nil
   111  }
   112  
   113  // NewRawSection creates a new section with an unparseable body.
   114  func (f *File) NewRawSection(name, body string) (*Section, error) {
   115  	section, err := f.NewSection(name)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	section.isRawSection = true
   121  	section.rawBody = body
   122  	return section, nil
   123  }
   124  
   125  // NewSections creates a list of sections.
   126  func (f *File) NewSections(names ...string) (err error) {
   127  	for _, name := range names {
   128  		if _, err = f.NewSection(name); err != nil {
   129  			return err
   130  		}
   131  	}
   132  	return nil
   133  }
   134  
   135  // GetSection returns section by given name.
   136  func (f *File) GetSection(name string) (*Section, error) {
   137  	secs, err := f.SectionsByName(name)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	return secs[0], err
   143  }
   144  
   145  // HasSection returns true if the file contains a section with given name.
   146  func (f *File) HasSection(name string) bool {
   147  	section, _ := f.GetSection(name)
   148  	return section != nil
   149  }
   150  
   151  // SectionsByName returns all sections with given name.
   152  func (f *File) SectionsByName(name string) ([]*Section, error) {
   153  	if len(name) == 0 {
   154  		name = DefaultSection
   155  	}
   156  	if f.options.Insensitive || f.options.InsensitiveSections {
   157  		name = strings.ToLower(name)
   158  	}
   159  
   160  	if f.BlockMode {
   161  		f.lock.RLock()
   162  		defer f.lock.RUnlock()
   163  	}
   164  
   165  	secs := f.sections[name]
   166  	if len(secs) == 0 {
   167  		return nil, fmt.Errorf("section %q does not exist", name)
   168  	}
   169  
   170  	return secs, nil
   171  }
   172  
   173  // Section assumes named section exists and returns a zero-value when not.
   174  func (f *File) Section(name string) *Section {
   175  	sec, err := f.GetSection(name)
   176  	if err != nil {
   177  		if name == "" {
   178  			name = DefaultSection
   179  		}
   180  		sec, _ = f.NewSection(name)
   181  		return sec
   182  	}
   183  	return sec
   184  }
   185  
   186  // SectionWithIndex assumes named section exists and returns a new section when not.
   187  func (f *File) SectionWithIndex(name string, index int) *Section {
   188  	secs, err := f.SectionsByName(name)
   189  	if err != nil || len(secs) <= index {
   190  		// NOTE: It's OK here because the only possible error is empty section name,
   191  		// but if it's empty, this piece of code won't be executed.
   192  		newSec, _ := f.NewSection(name)
   193  		return newSec
   194  	}
   195  
   196  	return secs[index]
   197  }
   198  
   199  // Sections returns a list of Section stored in the current instance.
   200  func (f *File) Sections() []*Section {
   201  	if f.BlockMode {
   202  		f.lock.RLock()
   203  		defer f.lock.RUnlock()
   204  	}
   205  
   206  	sections := make([]*Section, len(f.sectionList))
   207  	for i, name := range f.sectionList {
   208  		sections[i] = f.sections[name][f.sectionIndexes[i]]
   209  	}
   210  	return sections
   211  }
   212  
   213  // ChildSections returns a list of child sections of given section name.
   214  func (f *File) ChildSections(name string) []*Section {
   215  	return f.Section(name).ChildSections()
   216  }
   217  
   218  // SectionStrings returns list of section names.
   219  func (f *File) SectionStrings() []string {
   220  	list := make([]string, len(f.sectionList))
   221  	copy(list, f.sectionList)
   222  	return list
   223  }
   224  
   225  // DeleteSection deletes a section or all sections with given name.
   226  func (f *File) DeleteSection(name string) {
   227  	secs, err := f.SectionsByName(name)
   228  	if err != nil {
   229  		return
   230  	}
   231  
   232  	for i := 0; i < len(secs); i++ {
   233  		// For non-unique sections, it is always needed to remove the first one so
   234  		// in the next iteration, the subsequent section continue having index 0.
   235  		// Ignoring the error as index 0 never returns an error.
   236  		_ = f.DeleteSectionWithIndex(name, 0)
   237  	}
   238  }
   239  
   240  // DeleteSectionWithIndex deletes a section with given name and index.
   241  func (f *File) DeleteSectionWithIndex(name string, index int) error {
   242  	if !f.options.AllowNonUniqueSections && index != 0 {
   243  		return fmt.Errorf("delete section with non-zero index is only allowed when non-unique sections is enabled")
   244  	}
   245  
   246  	if len(name) == 0 {
   247  		name = DefaultSection
   248  	}
   249  	if f.options.Insensitive || f.options.InsensitiveSections {
   250  		name = strings.ToLower(name)
   251  	}
   252  
   253  	if f.BlockMode {
   254  		f.lock.Lock()
   255  		defer f.lock.Unlock()
   256  	}
   257  
   258  	// Count occurrences of the sections
   259  	occurrences := 0
   260  
   261  	sectionListCopy := make([]string, len(f.sectionList))
   262  	copy(sectionListCopy, f.sectionList)
   263  
   264  	for i, s := range sectionListCopy {
   265  		if s != name {
   266  			continue
   267  		}
   268  
   269  		if occurrences == index {
   270  			if len(f.sections[name]) <= 1 {
   271  				delete(f.sections, name) // The last one in the map
   272  			} else {
   273  				f.sections[name] = append(f.sections[name][:index], f.sections[name][index+1:]...)
   274  			}
   275  
   276  			// Fix section lists
   277  			f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
   278  			f.sectionIndexes = append(f.sectionIndexes[:i], f.sectionIndexes[i+1:]...)
   279  
   280  		} else if occurrences > index {
   281  			// Fix the indices of all following sections with this name.
   282  			f.sectionIndexes[i-1]--
   283  		}
   284  
   285  		occurrences++
   286  	}
   287  
   288  	return nil
   289  }
   290  
   291  func (f *File) reload(s dataSource) error {
   292  	r, err := s.ReadCloser()
   293  	if err != nil {
   294  		return err
   295  	}
   296  	defer r.Close()
   297  
   298  	return f.parse(r)
   299  }
   300  
   301  // Reload reloads and parses all data sources.
   302  func (f *File) Reload() (err error) {
   303  	for _, s := range f.dataSources {
   304  		if err = f.reload(s); err != nil {
   305  			// In loose mode, we create an empty default section for nonexistent files.
   306  			if os.IsNotExist(err) && f.options.Loose {
   307  				_ = f.parse(bytes.NewBuffer(nil))
   308  				continue
   309  			}
   310  			return err
   311  		}
   312  		if f.options.ShortCircuit {
   313  			return nil
   314  		}
   315  	}
   316  	return nil
   317  }
   318  
   319  // Append appends one or more data sources and reloads automatically.
   320  func (f *File) Append(source interface{}, others ...interface{}) error {
   321  	ds, err := parseDataSource(source)
   322  	if err != nil {
   323  		return err
   324  	}
   325  	f.dataSources = append(f.dataSources, ds)
   326  	for _, s := range others {
   327  		ds, err = parseDataSource(s)
   328  		if err != nil {
   329  			return err
   330  		}
   331  		f.dataSources = append(f.dataSources, ds)
   332  	}
   333  	return f.Reload()
   334  }
   335  
   336  func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
   337  	equalSign := DefaultFormatLeft + f.options.KeyValueDelimiterOnWrite + DefaultFormatRight
   338  
   339  	if PrettyFormat || PrettyEqual {
   340  		equalSign = fmt.Sprintf(" %s ", f.options.KeyValueDelimiterOnWrite)
   341  	}
   342  
   343  	// Use buffer to make sure target is safe until finish encoding.
   344  	buf := bytes.NewBuffer(nil)
   345  	lastSectionIdx := len(f.sectionList) - 1
   346  	for i, sname := range f.sectionList {
   347  		sec := f.SectionWithIndex(sname, f.sectionIndexes[i])
   348  		if len(sec.Comment) > 0 {
   349  			// Support multiline comments
   350  			lines := strings.Split(sec.Comment, LineBreak)
   351  			for i := range lines {
   352  				if lines[i][0] != '#' && lines[i][0] != ';' {
   353  					lines[i] = "; " + lines[i]
   354  				} else {
   355  					lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:])
   356  				}
   357  
   358  				if _, err := buf.WriteString(lines[i] + LineBreak); err != nil {
   359  					return nil, err
   360  				}
   361  			}
   362  		}
   363  
   364  		if i > 0 || DefaultHeader || (i == 0 && strings.ToUpper(sec.name) != DefaultSection) {
   365  			if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
   366  				return nil, err
   367  			}
   368  		} else {
   369  			// Write nothing if default section is empty
   370  			if len(sec.keyList) == 0 {
   371  				continue
   372  			}
   373  		}
   374  
   375  		isLastSection := i == lastSectionIdx
   376  		if sec.isRawSection {
   377  			if _, err := buf.WriteString(sec.rawBody); err != nil {
   378  				return nil, err
   379  			}
   380  
   381  			if PrettySection && !isLastSection {
   382  				// Put a line between sections
   383  				if _, err := buf.WriteString(LineBreak); err != nil {
   384  					return nil, err
   385  				}
   386  			}
   387  			continue
   388  		}
   389  
   390  		// Count and generate alignment length and buffer spaces using the
   391  		// longest key. Keys may be modified if they contain certain characters so
   392  		// we need to take that into account in our calculation.
   393  		alignLength := 0
   394  		if PrettyFormat {
   395  			for _, kname := range sec.keyList {
   396  				keyLength := len(kname)
   397  				// First case will surround key by ` and second by """
   398  				if strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters) {
   399  					keyLength += 2
   400  				} else if strings.Contains(kname, "`") {
   401  					keyLength += 6
   402  				}
   403  
   404  				if keyLength > alignLength {
   405  					alignLength = keyLength
   406  				}
   407  			}
   408  		}
   409  		alignSpaces := bytes.Repeat([]byte(" "), alignLength)
   410  
   411  	KeyList:
   412  		for _, kname := range sec.keyList {
   413  			key := sec.Key(kname)
   414  			if len(key.Comment) > 0 {
   415  				if len(indent) > 0 && sname != DefaultSection {
   416  					buf.WriteString(indent)
   417  				}
   418  
   419  				// Support multiline comments
   420  				lines := strings.Split(key.Comment, LineBreak)
   421  				for i := range lines {
   422  					if lines[i][0] != '#' && lines[i][0] != ';' {
   423  						lines[i] = "; " + strings.TrimSpace(lines[i])
   424  					} else {
   425  						lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:])
   426  					}
   427  
   428  					if _, err := buf.WriteString(lines[i] + LineBreak); err != nil {
   429  						return nil, err
   430  					}
   431  				}
   432  			}
   433  
   434  			if len(indent) > 0 && sname != DefaultSection {
   435  				buf.WriteString(indent)
   436  			}
   437  
   438  			switch {
   439  			case key.isAutoIncrement:
   440  				kname = "-"
   441  			case strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters):
   442  				kname = "`" + kname + "`"
   443  			case strings.Contains(kname, "`"):
   444  				kname = `"""` + kname + `"""`
   445  			}
   446  
   447  			writeKeyValue := func(val string) (bool, error) {
   448  				if _, err := buf.WriteString(kname); err != nil {
   449  					return false, err
   450  				}
   451  
   452  				if key.isBooleanType {
   453  					buf.WriteString(LineBreak)
   454  					return true, nil
   455  				}
   456  
   457  				// Write out alignment spaces before "=" sign
   458  				if PrettyFormat {
   459  					buf.Write(alignSpaces[:alignLength-len(kname)])
   460  				}
   461  
   462  				// In case key value contains "\n", "`", "\"", "#" or ";"
   463  				if strings.ContainsAny(val, "\n`") {
   464  					val = `"""` + val + `"""`
   465  				} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
   466  					val = "`" + val + "`"
   467  				} else if len(strings.TrimSpace(val)) != len(val) {
   468  					val = `"` + val + `"`
   469  				}
   470  				if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
   471  					return false, err
   472  				}
   473  				return false, nil
   474  			}
   475  
   476  			shadows := key.ValueWithShadows()
   477  			if len(shadows) == 0 {
   478  				if _, err := writeKeyValue(""); err != nil {
   479  					return nil, err
   480  				}
   481  			}
   482  
   483  			for _, val := range shadows {
   484  				exitLoop, err := writeKeyValue(val)
   485  				if err != nil {
   486  					return nil, err
   487  				} else if exitLoop {
   488  					continue KeyList
   489  				}
   490  			}
   491  
   492  			for _, val := range key.nestedValues {
   493  				if _, err := buf.WriteString(indent + "  " + val + LineBreak); err != nil {
   494  					return nil, err
   495  				}
   496  			}
   497  		}
   498  
   499  		if PrettySection && !isLastSection {
   500  			// Put a line between sections
   501  			if _, err := buf.WriteString(LineBreak); err != nil {
   502  				return nil, err
   503  			}
   504  		}
   505  	}
   506  
   507  	return buf, nil
   508  }
   509  
   510  // WriteToIndent writes content into io.Writer with given indention.
   511  // If PrettyFormat has been set to be true,
   512  // it will align "=" sign with spaces under each section.
   513  func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) {
   514  	buf, err := f.writeToBuffer(indent)
   515  	if err != nil {
   516  		return 0, err
   517  	}
   518  	return buf.WriteTo(w)
   519  }
   520  
   521  // WriteTo writes file content into io.Writer.
   522  func (f *File) WriteTo(w io.Writer) (int64, error) {
   523  	return f.WriteToIndent(w, "")
   524  }
   525  
   526  // SaveToIndent writes content to file system with given value indention.
   527  func (f *File) SaveToIndent(filename, indent string) error {
   528  	// Note: Because we are truncating with os.Create,
   529  	// 	so it's safer to save to a temporary file location and rename after done.
   530  	buf, err := f.writeToBuffer(indent)
   531  	if err != nil {
   532  		return err
   533  	}
   534  
   535  	return ioutil.WriteFile(filename, buf.Bytes(), 0666)
   536  }
   537  
   538  // SaveTo writes content to file system.
   539  func (f *File) SaveTo(filename string) error {
   540  	return f.SaveToIndent(filename, "")
   541  }
   542  

View as plain text