1// +build go1.6 2 3// Copyright 2014 Unknwon 4// 5// Licensed under the Apache License, Version 2.0 (the "License"): you may 6// not use this file except in compliance with the License. You may obtain 7// a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14// License for the specific language governing permissions and limitations 15// under the License. 16 17// Package ini provides INI file read and write functionality in Go. 18package ini 19 20import ( 21 "bytes" 22 "fmt" 23 "io" 24 "io/ioutil" 25 "os" 26 "regexp" 27 "runtime" 28) 29 30const ( 31 // Name for default section. You can use this constant or the string literal. 32 // In most of cases, an empty string is all you need to access the section. 33 DEFAULT_SECTION = "DEFAULT" 34 35 // Maximum allowed depth when recursively substituing variable names. 36 _DEPTH_VALUES = 99 37 _VERSION = "1.42.0" 38) 39 40// Version returns current package version literal. 41func Version() string { 42 return _VERSION 43} 44 45var ( 46 // Delimiter to determine or compose a new line. 47 // This variable will be changed to "\r\n" automatically on Windows 48 // at package init time. 49 LineBreak = "\n" 50 51 // Place custom spaces when PrettyFormat and PrettyEqual are both disabled 52 DefaultFormatLeft = "" 53 DefaultFormatRight = "" 54 55 // Variable regexp pattern: %(variable)s 56 varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`) 57 58 // Indicate whether to align "=" sign with spaces to produce pretty output 59 // or reduce all possible spaces for compact format. 60 PrettyFormat = true 61 62 // Place spaces around "=" sign even when PrettyFormat is false 63 PrettyEqual = false 64 65 // Explicitly write DEFAULT section header 66 DefaultHeader = false 67 68 // Indicate whether to put a line between sections 69 PrettySection = true 70) 71 72func init() { 73 if runtime.GOOS == "windows" { 74 LineBreak = "\r\n" 75 } 76} 77 78func inSlice(str string, s []string) bool { 79 for _, v := range s { 80 if str == v { 81 return true 82 } 83 } 84 return false 85} 86 87// dataSource is an interface that returns object which can be read and closed. 88type dataSource interface { 89 ReadCloser() (io.ReadCloser, error) 90} 91 92// sourceFile represents an object that contains content on the local file system. 93type sourceFile struct { 94 name string 95} 96 97func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) { 98 return os.Open(s.name) 99} 100 101// sourceData represents an object that contains content in memory. 102type sourceData struct { 103 data []byte 104} 105 106func (s *sourceData) ReadCloser() (io.ReadCloser, error) { 107 return ioutil.NopCloser(bytes.NewReader(s.data)), nil 108} 109 110// sourceReadCloser represents an input stream with Close method. 111type sourceReadCloser struct { 112 reader io.ReadCloser 113} 114 115func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) { 116 return s.reader, nil 117} 118 119func parseDataSource(source interface{}) (dataSource, error) { 120 switch s := source.(type) { 121 case string: 122 return sourceFile{s}, nil 123 case []byte: 124 return &sourceData{s}, nil 125 case io.ReadCloser: 126 return &sourceReadCloser{s}, nil 127 default: 128 return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s) 129 } 130} 131 132type LoadOptions struct { 133 // Loose indicates whether the parser should ignore nonexistent files or return error. 134 Loose bool 135 // Insensitive indicates whether the parser forces all section and key names to lowercase. 136 Insensitive bool 137 // IgnoreContinuation indicates whether to ignore continuation lines while parsing. 138 IgnoreContinuation bool 139 // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value. 140 IgnoreInlineComment bool 141 // SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs. 142 SkipUnrecognizableLines bool 143 // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. 144 // This type of keys are mostly used in my.cnf. 145 AllowBooleanKeys bool 146 // AllowShadows indicates whether to keep track of keys with same name under same section. 147 AllowShadows bool 148 // AllowNestedValues indicates whether to allow AWS-like nested values. 149 // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values 150 AllowNestedValues bool 151 // AllowPythonMultilineValues indicates whether to allow Python-like multi-line values. 152 // Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure 153 // Relevant quote: Values can also span multiple lines, as long as they are indented deeper 154 // than the first line of the value. 155 AllowPythonMultilineValues bool 156 // SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value. 157 // Docs: https://docs.python.org/2/library/configparser.html 158 // Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names. 159 // In the latter case, they need to be preceded by a whitespace character to be recognized as a comment. 160 SpaceBeforeInlineComment bool 161 // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format 162 // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" 163 UnescapeValueDoubleQuotes bool 164 // UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format 165 // when value is NOT surrounded by any quotes. 166 // Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all. 167 UnescapeValueCommentSymbols bool 168 // UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise 169 // conform to key/value pairs. Specify the names of those blocks here. 170 UnparseableSections []string 171 // KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:". 172 KeyValueDelimiters string 173 // PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes). 174 PreserveSurroundedQuote bool 175} 176 177func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) { 178 sources := make([]dataSource, len(others)+1) 179 sources[0], err = parseDataSource(source) 180 if err != nil { 181 return nil, err 182 } 183 for i := range others { 184 sources[i+1], err = parseDataSource(others[i]) 185 if err != nil { 186 return nil, err 187 } 188 } 189 f := newFile(sources, opts) 190 if err = f.Reload(); err != nil { 191 return nil, err 192 } 193 return f, nil 194} 195 196// Load loads and parses from INI data sources. 197// Arguments can be mixed of file name with string type, or raw data in []byte. 198// It will return error if list contains nonexistent files. 199func Load(source interface{}, others ...interface{}) (*File, error) { 200 return LoadSources(LoadOptions{}, source, others...) 201} 202 203// LooseLoad has exactly same functionality as Load function 204// except it ignores nonexistent files instead of returning error. 205func LooseLoad(source interface{}, others ...interface{}) (*File, error) { 206 return LoadSources(LoadOptions{Loose: true}, source, others...) 207} 208 209// InsensitiveLoad has exactly same functionality as Load function 210// except it forces all section and key names to be lowercased. 211func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) { 212 return LoadSources(LoadOptions{Insensitive: true}, source, others...) 213} 214 215// ShadowLoad has exactly same functionality as Load function 216// except it allows have shadow keys. 217func ShadowLoad(source interface{}, others ...interface{}) (*File, error) { 218 return LoadSources(LoadOptions{AllowShadows: true}, source, others...) 219} 220