aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/go-ini/ini/ini.go
diff options
context:
space:
mode:
authorJake Champlin <jake.champlin.27@gmail.com>2017-06-06 12:40:07 -0400
committerJake Champlin <jake.champlin.27@gmail.com>2017-06-06 12:40:07 -0400
commitbae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch)
treeca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/go-ini/ini/ini.go
parent254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff)
downloadterraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz
terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst
terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip
Initial transfer of provider code
Diffstat (limited to 'vendor/github.com/go-ini/ini/ini.go')
-rw-r--r--vendor/github.com/go-ini/ini/ini.go535
1 files changed, 535 insertions, 0 deletions
diff --git a/vendor/github.com/go-ini/ini/ini.go b/vendor/github.com/go-ini/ini/ini.go
new file mode 100644
index 0000000..77e0dbd
--- /dev/null
+++ b/vendor/github.com/go-ini/ini/ini.go
@@ -0,0 +1,535 @@
1// Copyright 2014 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 provides INI file read and write functionality in Go.
16package ini
17
18import (
19 "bytes"
20 "errors"
21 "fmt"
22 "io"
23 "io/ioutil"
24 "os"
25 "regexp"
26 "runtime"
27 "strconv"
28 "strings"
29 "sync"
30 "time"
31)
32
33const (
34 // Name for default section. You can use this constant or the string literal.
35 // In most of cases, an empty string is all you need to access the section.
36 DEFAULT_SECTION = "DEFAULT"
37
38 // Maximum allowed depth when recursively substituing variable names.
39 _DEPTH_VALUES = 99
40 _VERSION = "1.23.1"
41)
42
43// Version returns current package version literal.
44func Version() string {
45 return _VERSION
46}
47
48var (
49 // Delimiter to determine or compose a new line.
50 // This variable will be changed to "\r\n" automatically on Windows
51 // at package init time.
52 LineBreak = "\n"
53
54 // Variable regexp pattern: %(variable)s
55 varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
56
57 // Indicate whether to align "=" sign with spaces to produce pretty output
58 // or reduce all possible spaces for compact format.
59 PrettyFormat = true
60
61 // Explicitly write DEFAULT section header
62 DefaultHeader = false
63)
64
65func init() {
66 if runtime.GOOS == "windows" {
67 LineBreak = "\r\n"
68 }
69}
70
71func inSlice(str string, s []string) bool {
72 for _, v := range s {
73 if str == v {
74 return true
75 }
76 }
77 return false
78}
79
80// dataSource is an interface that returns object which can be read and closed.
81type dataSource interface {
82 ReadCloser() (io.ReadCloser, error)
83}
84
85// sourceFile represents an object that contains content on the local file system.
86type sourceFile struct {
87 name string
88}
89
90func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
91 return os.Open(s.name)
92}
93
94type bytesReadCloser struct {
95 reader io.Reader
96}
97
98func (rc *bytesReadCloser) Read(p []byte) (n int, err error) {
99 return rc.reader.Read(p)
100}
101
102func (rc *bytesReadCloser) Close() error {
103 return nil
104}
105
106// sourceData represents an object that contains content in memory.
107type sourceData struct {
108 data []byte
109}
110
111func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
112 return ioutil.NopCloser(bytes.NewReader(s.data)), nil
113}
114
115// sourceReadCloser represents an input stream with Close method.
116type sourceReadCloser struct {
117 reader io.ReadCloser
118}
119
120func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
121 return s.reader, nil
122}
123
124// File represents a combination of a or more INI file(s) in memory.
125type File struct {
126 // Should make things safe, but sometimes doesn't matter.
127 BlockMode bool
128 // Make sure data is safe in multiple goroutines.
129 lock sync.RWMutex
130
131 // Allow combination of multiple data sources.
132 dataSources []dataSource
133 // Actual data is stored here.
134 sections map[string]*Section
135
136 // To keep data in order.
137 sectionList []string
138
139 options LoadOptions
140
141 NameMapper
142 ValueMapper
143}
144
145// newFile initializes File object with given data sources.
146func newFile(dataSources []dataSource, opts LoadOptions) *File {
147 return &File{
148 BlockMode: true,
149 dataSources: dataSources,
150 sections: make(map[string]*Section),
151 sectionList: make([]string, 0, 10),
152 options: opts,
153 }
154}
155
156func parseDataSource(source interface{}) (dataSource, error) {
157 switch s := source.(type) {
158 case string:
159 return sourceFile{s}, nil
160 case []byte:
161 return &sourceData{s}, nil
162 case io.ReadCloser:
163 return &sourceReadCloser{s}, nil
164 default:
165 return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s)
166 }
167}
168
169type LoadOptions struct {
170 // Loose indicates whether the parser should ignore nonexistent files or return error.
171 Loose bool
172 // Insensitive indicates whether the parser forces all section and key names to lowercase.
173 Insensitive bool
174 // IgnoreContinuation indicates whether to ignore continuation lines while parsing.
175 IgnoreContinuation bool
176 // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
177 // This type of keys are mostly used in my.cnf.
178 AllowBooleanKeys bool
179 // Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
180 // conform to key/value pairs. Specify the names of those blocks here.
181 UnparseableSections []string
182}
183
184func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
185 sources := make([]dataSource, len(others)+1)
186 sources[0], err = parseDataSource(source)
187 if err != nil {
188 return nil, err
189 }
190 for i := range others {
191 sources[i+1], err = parseDataSource(others[i])
192 if err != nil {
193 return nil, err
194 }
195 }
196 f := newFile(sources, opts)
197 if err = f.Reload(); err != nil {
198 return nil, err
199 }
200 return f, nil
201}
202
203// Load loads and parses from INI data sources.
204// Arguments can be mixed of file name with string type, or raw data in []byte.
205// It will return error if list contains nonexistent files.
206func Load(source interface{}, others ...interface{}) (*File, error) {
207 return LoadSources(LoadOptions{}, source, others...)
208}
209
210// LooseLoad has exactly same functionality as Load function
211// except it ignores nonexistent files instead of returning error.
212func LooseLoad(source interface{}, others ...interface{}) (*File, error) {
213 return LoadSources(LoadOptions{Loose: true}, source, others...)
214}
215
216// InsensitiveLoad has exactly same functionality as Load function
217// except it forces all section and key names to be lowercased.
218func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
219 return LoadSources(LoadOptions{Insensitive: true}, source, others...)
220}
221
222// Empty returns an empty file object.
223func Empty() *File {
224 // Ignore error here, we sure our data is good.
225 f, _ := Load([]byte(""))
226 return f
227}
228
229// NewSection creates a new section.
230func (f *File) NewSection(name string) (*Section, error) {
231 if len(name) == 0 {
232 return nil, errors.New("error creating new section: empty section name")
233 } else if f.options.Insensitive && name != DEFAULT_SECTION {
234 name = strings.ToLower(name)
235 }
236
237 if f.BlockMode {
238 f.lock.Lock()
239 defer f.lock.Unlock()
240 }
241
242 if inSlice(name, f.sectionList) {
243 return f.sections[name], nil
244 }
245
246 f.sectionList = append(f.sectionList, name)
247 f.sections[name] = newSection(f, name)
248 return f.sections[name], nil
249}
250
251// NewRawSection creates a new section with an unparseable body.
252func (f *File) NewRawSection(name, body string) (*Section, error) {
253 section, err := f.NewSection(name)
254 if err != nil {
255 return nil, err
256 }
257
258 section.isRawSection = true
259 section.rawBody = body
260 return section, nil
261}
262
263// NewSections creates a list of sections.
264func (f *File) NewSections(names ...string) (err error) {
265 for _, name := range names {
266 if _, err = f.NewSection(name); err != nil {
267 return err
268 }
269 }
270 return nil
271}
272
273// GetSection returns section by given name.
274func (f *File) GetSection(name string) (*Section, error) {
275 if len(name) == 0 {
276 name = DEFAULT_SECTION
277 } else if f.options.Insensitive {
278 name = strings.ToLower(name)
279 }
280
281 if f.BlockMode {
282 f.lock.RLock()
283 defer f.lock.RUnlock()
284 }
285
286 sec := f.sections[name]
287 if sec == nil {
288 return nil, fmt.Errorf("section '%s' does not exist", name)
289 }
290 return sec, nil
291}
292
293// Section assumes named section exists and returns a zero-value when not.
294func (f *File) Section(name string) *Section {
295 sec, err := f.GetSection(name)
296 if err != nil {
297 // Note: It's OK here because the only possible error is empty section name,
298 // but if it's empty, this piece of code won't be executed.
299 sec, _ = f.NewSection(name)
300 return sec
301 }
302 return sec
303}
304
305// Section returns list of Section.
306func (f *File) Sections() []*Section {
307 sections := make([]*Section, len(f.sectionList))
308 for i := range f.sectionList {
309 sections[i] = f.Section(f.sectionList[i])
310 }
311 return sections
312}
313
314// SectionStrings returns list of section names.
315func (f *File) SectionStrings() []string {
316 list := make([]string, len(f.sectionList))
317 copy(list, f.sectionList)
318 return list
319}
320
321// DeleteSection deletes a section.
322func (f *File) DeleteSection(name string) {
323 if f.BlockMode {
324 f.lock.Lock()
325 defer f.lock.Unlock()
326 }
327
328 if len(name) == 0 {
329 name = DEFAULT_SECTION
330 }
331
332 for i, s := range f.sectionList {
333 if s == name {
334 f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
335 delete(f.sections, name)
336 return
337 }
338 }
339}
340
341func (f *File) reload(s dataSource) error {
342 r, err := s.ReadCloser()
343 if err != nil {
344 return err
345 }
346 defer r.Close()
347
348 return f.parse(r)
349}
350
351// Reload reloads and parses all data sources.
352func (f *File) Reload() (err error) {
353 for _, s := range f.dataSources {
354 if err = f.reload(s); err != nil {
355 // In loose mode, we create an empty default section for nonexistent files.
356 if os.IsNotExist(err) && f.options.Loose {
357 f.parse(bytes.NewBuffer(nil))
358 continue
359 }
360 return err
361 }
362 }
363 return nil
364}
365
366// Append appends one or more data sources and reloads automatically.
367func (f *File) Append(source interface{}, others ...interface{}) error {
368 ds, err := parseDataSource(source)
369 if err != nil {
370 return err
371 }
372 f.dataSources = append(f.dataSources, ds)
373 for _, s := range others {
374 ds, err = parseDataSource(s)
375 if err != nil {
376 return err
377 }
378 f.dataSources = append(f.dataSources, ds)
379 }
380 return f.Reload()
381}
382
383// WriteToIndent writes content into io.Writer with given indention.
384// If PrettyFormat has been set to be true,
385// it will align "=" sign with spaces under each section.
386func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
387 equalSign := "="
388 if PrettyFormat {
389 equalSign = " = "
390 }
391
392 // Use buffer to make sure target is safe until finish encoding.
393 buf := bytes.NewBuffer(nil)
394 for i, sname := range f.sectionList {
395 sec := f.Section(sname)
396 if len(sec.Comment) > 0 {
397 if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
398 sec.Comment = "; " + sec.Comment
399 }
400 if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil {
401 return 0, err
402 }
403 }
404
405 if i > 0 || DefaultHeader {
406 if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
407 return 0, err
408 }
409 } else {
410 // Write nothing if default section is empty
411 if len(sec.keyList) == 0 {
412 continue
413 }
414 }
415
416 if sec.isRawSection {
417 if _, err = buf.WriteString(sec.rawBody); err != nil {
418 return 0, err
419 }
420 continue
421 }
422
423 // Count and generate alignment length and buffer spaces using the
424 // longest key. Keys may be modifed if they contain certain characters so
425 // we need to take that into account in our calculation.
426 alignLength := 0
427 if PrettyFormat {
428 for _, kname := range sec.keyList {
429 keyLength := len(kname)
430 // First case will surround key by ` and second by """
431 if strings.ContainsAny(kname, "\"=:") {
432 keyLength += 2
433 } else if strings.Contains(kname, "`") {
434 keyLength += 6
435 }
436
437 if keyLength > alignLength {
438 alignLength = keyLength
439 }
440 }
441 }
442 alignSpaces := bytes.Repeat([]byte(" "), alignLength)
443
444 for _, kname := range sec.keyList {
445 key := sec.Key(kname)
446 if len(key.Comment) > 0 {
447 if len(indent) > 0 && sname != DEFAULT_SECTION {
448 buf.WriteString(indent)
449 }
450 if key.Comment[0] != '#' && key.Comment[0] != ';' {
451 key.Comment = "; " + key.Comment
452 }
453 if _, err = buf.WriteString(key.Comment + LineBreak); err != nil {
454 return 0, err
455 }
456 }
457
458 if len(indent) > 0 && sname != DEFAULT_SECTION {
459 buf.WriteString(indent)
460 }
461
462 switch {
463 case key.isAutoIncrement:
464 kname = "-"
465 case strings.ContainsAny(kname, "\"=:"):
466 kname = "`" + kname + "`"
467 case strings.Contains(kname, "`"):
468 kname = `"""` + kname + `"""`
469 }
470 if _, err = buf.WriteString(kname); err != nil {
471 return 0, err
472 }
473
474 if key.isBooleanType {
475 continue
476 }
477
478 // Write out alignment spaces before "=" sign
479 if PrettyFormat {
480 buf.Write(alignSpaces[:alignLength-len(kname)])
481 }
482
483 val := key.value
484 // In case key value contains "\n", "`", "\"", "#" or ";"
485 if strings.ContainsAny(val, "\n`") {
486 val = `"""` + val + `"""`
487 } else if strings.ContainsAny(val, "#;") {
488 val = "`" + val + "`"
489 }
490 if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil {
491 return 0, err
492 }
493 }
494
495 // Put a line between sections
496 if _, err = buf.WriteString(LineBreak); err != nil {
497 return 0, err
498 }
499 }
500
501 return buf.WriteTo(w)
502}
503
504// WriteTo writes file content into io.Writer.
505func (f *File) WriteTo(w io.Writer) (int64, error) {
506 return f.WriteToIndent(w, "")
507}
508
509// SaveToIndent writes content to file system with given value indention.
510func (f *File) SaveToIndent(filename, indent string) error {
511 // Note: Because we are truncating with os.Create,
512 // so it's safer to save to a temporary file location and rename afte done.
513 tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp"
514 defer os.Remove(tmpPath)
515
516 fw, err := os.Create(tmpPath)
517 if err != nil {
518 return err
519 }
520
521 if _, err = f.WriteToIndent(fw, indent); err != nil {
522 fw.Close()
523 return err
524 }
525 fw.Close()
526
527 // Remove old file and rename the new one.
528 os.Remove(filename)
529 return os.Rename(tmpPath, filename)
530}
531
532// SaveTo writes content to file system.
533func (f *File) SaveTo(filename string) error {
534 return f.SaveToIndent(filename, "")
535}