diff options
Diffstat (limited to 'vendor/gopkg.in/ini.v1/file.go')
-rw-r--r-- | vendor/gopkg.in/ini.v1/file.go | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/vendor/gopkg.in/ini.v1/file.go b/vendor/gopkg.in/ini.v1/file.go new file mode 100644 index 0000000..f8b2240 --- /dev/null +++ b/vendor/gopkg.in/ini.v1/file.go | |||
@@ -0,0 +1,541 @@ | |||
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 | } | ||