1 // Package ini provides INI file read and write functionality in Go. 2 package ini 3 4 import ( 5 "os" 6 "regexp" 7 "runtime" 8 "strings" 9 ) 10 11 const ( 12 // Maximum allowed depth when recursively substituing variable names. 13 depthValues = 99 14 ) 15 16 var ( 17 // DefaultSection is the name of default section. You can use this var or the string literal. 18 // In most of cases, an empty string is all you need to access the section. 19 DefaultSection = "DEFAULT" 20 21 // LineBreak is the delimiter to determine or compose a new line. 22 // This variable will be changed to "\r\n" automatically on Windows at package init time. 23 LineBreak = "\n" 24 25 // Variable regexp pattern: %(variable)s 26 varPattern = regexp.MustCompile(`%\(([^)]+)\)s`) 27 28 // DefaultHeader explicitly writes default section header. 29 DefaultHeader = false 30 31 // PrettySection indicates whether to put a line between sections. 32 PrettySection = true 33 // PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output 34 // or reduce all possible spaces for compact format. 35 PrettyFormat = true 36 // PrettyEqual places spaces around "=" sign even when PrettyFormat is false. 37 PrettyEqual = false 38 // DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled. 39 DefaultFormatLeft = "" 40 // DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled. 41 DefaultFormatRight = "" 42 ) 43 44 var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test") 45 46 func init() { 47 if runtime.GOOS == "windows" && !inTest { 48 LineBreak = "\r\n" 49 } 50 } 51 52 // LoadOptions contains all customized options used for load data source(s). 53 type LoadOptions struct { 54 // Loose indicates whether the parser should ignore nonexistent files or return error. 55 Loose bool 56 // Insensitive indicates whether the parser forces all section and key names to lowercase. 57 Insensitive bool 58 // InsensitiveSections indicates whether the parser forces all section to lowercase. 59 InsensitiveSections bool 60 // InsensitiveKeys indicates whether the parser forces all key names to lowercase. 61 InsensitiveKeys bool 62 // IgnoreContinuation indicates whether to ignore continuation lines while parsing. 63 IgnoreContinuation bool 64 // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value. 65 IgnoreInlineComment bool 66 // SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs. 67 SkipUnrecognizableLines bool 68 // ShortCircuit indicates whether to ignore other configuration sources after loaded the first available configuration source. 69 ShortCircuit bool 70 // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. 71 // This type of keys are mostly used in my.cnf. 72 AllowBooleanKeys bool 73 // AllowShadows indicates whether to keep track of keys with same name under same section. 74 AllowShadows bool 75 // AllowNestedValues indicates whether to allow AWS-like nested values. 76 // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values 77 AllowNestedValues bool 78 // AllowPythonMultilineValues indicates whether to allow Python-like multi-line values. 79 // Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure 80 // Relevant quote: Values can also span multiple lines, as long as they are indented deeper 81 // than the first line of the value. 82 AllowPythonMultilineValues bool 83 // SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value. 84 // Docs: https://docs.python.org/2/library/configparser.html 85 // Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names. 86 // In the latter case, they need to be preceded by a whitespace character to be recognized as a comment. 87 SpaceBeforeInlineComment bool 88 // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format 89 // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" 90 UnescapeValueDoubleQuotes bool 91 // UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format 92 // when value is NOT surrounded by any quotes. 93 // Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all. 94 UnescapeValueCommentSymbols bool 95 // UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise 96 // conform to key/value pairs. Specify the names of those blocks here. 97 UnparseableSections []string 98 // KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:". 99 KeyValueDelimiters string 100 // KeyValueDelimiterOnWrite is the delimiter that are used to separate key and value output. By default, it is "=". 101 KeyValueDelimiterOnWrite string 102 // ChildSectionDelimiter is the delimiter that is used to separate child sections. By default, it is ".". 103 ChildSectionDelimiter string 104 // PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes). 105 PreserveSurroundedQuote bool 106 // DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values). 107 DebugFunc DebugFunc 108 // ReaderBufferSize is the buffer size of the reader in bytes. 109 ReaderBufferSize int 110 // AllowNonUniqueSections indicates whether to allow sections with the same name multiple times. 111 AllowNonUniqueSections bool 112 // AllowDuplicateShadowValues indicates whether values for shadowed keys should be deduplicated. 113 AllowDuplicateShadowValues bool 114 } 115 116 // DebugFunc is the type of function called to log parse events. 117 type DebugFunc func(message string) 118 119 // LoadSources allows caller to apply customized options for loading from data source(s). 120 func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) { 121 sources := make([]dataSource, len(others)+1) 122 sources[0], err = parseDataSource(source) 123 if err != nil { 124 return nil, err 125 } 126 for i := range others { 127 sources[i+1], err = parseDataSource(others[i]) 128 if err != nil { 129 return nil, err 130 } 131 } 132 f := newFile(sources, opts) 133 if err = f.Reload(); err != nil { 134 return nil, err 135 } 136 return f, nil 137 } 138 139 // Load loads and parses from INI data sources. 140 // Arguments can be mixed of file name with string type, or raw data in []byte. 141 // It will return error if list contains nonexistent files. 142 func Load(source interface{}, others ...interface{}) (*File, error) { 143 return LoadSources(LoadOptions{}, source, others...) 144 } 145 146 // LooseLoad has exactly same functionality as Load function 147 // except it ignores nonexistent files instead of returning error. 148 func LooseLoad(source interface{}, others ...interface{}) (*File, error) { 149 return LoadSources(LoadOptions{Loose: true}, source, others...) 150 } 151 152 // InsensitiveLoad has exactly same functionality as Load function 153 // except it forces all section and key names to be lowercased. 154 func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) { 155 return LoadSources(LoadOptions{Insensitive: true}, source, others...) 156 } 157 158 // ShadowLoad has exactly same functionality as Load function 159 // except it allows have shadow keys. 160 func ShadowLoad(source interface{}, others ...interface{}) (*File, error) { 161 return LoadSources(LoadOptions{AllowShadows: true}, source, others...) 162 } 163