Browse Source

Temporarily patch go-ini/ini with fork (#2255)

Ethan Koenig 1 year ago
parent
commit
27798c3efc

+ 50 - 7
vendor/gopkg.in/ini.v1/README.md

@@ -1,4 +1,4 @@
1
-INI [![Build Status](https://travis-ci.org/go-ini/ini.svg?branch=master)](https://travis-ci.org/go-ini/ini)
1
+INI [![Build Status](https://travis-ci.org/go-ini/ini.svg?branch=master)](https://travis-ci.org/go-ini/ini) [![Sourcegraph](https://sourcegraph.com/github.com/go-ini/ini/-/badge.svg)](https://sourcegraph.com/github.com/go-ini/ini?badge)
2 2
 ===
3 3
 
4 4
 ![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
@@ -9,7 +9,7 @@ Package ini provides INI file read and write functionality in Go.
9 9
 
10 10
 ## Feature
11 11
 
12
-- Load multiple data sources(`[]byte` or file) with overwrites.
12
+- Load multiple data sources(`[]byte`, file and `io.ReadCloser`) with overwrites.
13 13
 - Read with recursion values.
14 14
 - Read with parent-child sections.
15 15
 - Read with auto-increment key names.
@@ -44,10 +44,10 @@ Please add `-u` flag to update in the future.
44 44
 
45 45
 ### Loading from data sources
46 46
 
47
-A **Data Source** is either raw data in type `[]byte` or a file name with type `string` and you can load **as many data sources as you want**. Passing other types will simply return an error.
47
+A **Data Source** is either raw data in type `[]byte`, a file name with type `string` or `io.ReadCloser`. You can load **as many data sources as you want**. Passing other types will simply return an error.
48 48
 
49 49
 ```go
50
-cfg, err := ini.Load([]byte("raw data"), "filename")
50
+cfg, err := ini.Load([]byte("raw data"), "filename", ioutil.NopCloser(bytes.NewReader([]byte("some other data"))))
51 51
 ```
52 52
 
53 53
 Or start with an empty object:
@@ -83,8 +83,8 @@ sec1, err := cfg.GetSection("Section")
83 83
 sec2, err := cfg.GetSection("SecTIOn")
84 84
 
85 85
 // key1 and key2 are the exactly same key object
86
-key1, err := cfg.GetKey("Key")
87
-key2, err := cfg.GetKey("KeY")
86
+key1, err := sec1.GetKey("Key")
87
+key2, err := sec2.GetKey("KeY")
88 88
 ```
89 89
 
90 90
 #### MySQL-like boolean key 
@@ -106,6 +106,28 @@ cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf"))
106 106
 
107 107
 The value of those keys are always `true`, and when you save to a file, it will keep in the same foramt as you read.
108 108
 
109
+To generate such keys in your program, you could use `NewBooleanKey`:
110
+
111
+```go
112
+key, err := sec.NewBooleanKey("skip-host-cache")
113
+```
114
+
115
+#### Comment
116
+
117
+Take care that following format will be treated as comment:
118
+
119
+1. Line begins with `#` or `;`
120
+2. Words after `#` or `;`
121
+3. Words after section name (i.e words after `[some section name]`)
122
+
123
+If you want to save a value with `#` or `;`, please quote them with ``` ` ``` or ``` """ ```.
124
+
125
+Alternatively, you can use following `LoadOptions` to completely ignore inline comments:
126
+
127
+```go
128
+cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, "app.ini"))
129
+```
130
+
109 131
 ### Working with sections
110 132
 
111 133
 To get a section, you would need to:
@@ -123,7 +145,7 @@ section, err := cfg.GetSection("")
123 145
 When you're pretty sure the section exists, following code could make your life easier:
124 146
 
125 147
 ```go
126
-section := cfg.Section("")
148
+section := cfg.Section("section name")
127 149
 ```
128 150
 
129 151
 What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you.
@@ -400,6 +422,12 @@ cfg.WriteTo(writer)
400 422
 cfg.WriteToIndent(writer, "\t")
401 423
 ```
402 424
 
425
+By default, spaces are used to align "=" sign between key and values, to disable that:
426
+
427
+```go
428
+ini.PrettyFormat = false
429
+``` 
430
+
403 431
 ## Advanced Usage
404 432
 
405 433
 ### Recursive Values
@@ -447,6 +475,21 @@ cfg.Section("package.sub").Key("CLONE_URL").String()	// https://gopkg.in/ini.v1
447 475
 cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"]
448 476
 ```
449 477
 
478
+### Unparseable Sections
479
+
480
+Sometimes, you have sections that do not contain key-value pairs but raw content, to handle such case, you can use `LoadOptions.UnparsableSections`:
481
+
482
+```go
483
+cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS]
484
+<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
485
+
486
+body := cfg.Section("COMMENTS").Body()
487
+
488
+/* --- start ---
489
+<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
490
+------  end  --- */
491
+```
492
+
450 493
 ### Auto-increment Key Names
451 494
 
452 495
 If key name is `-` in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter.

+ 53 - 10
vendor/gopkg.in/ini.v1/README_ZH.md

@@ -2,7 +2,7 @@
2 2
 
3 3
 ## 功能特性
4 4
 
5
-- 支持覆盖加载多个数据源(`[]byte` 或文件
5
+- 支持覆盖加载多个数据源(`[]byte`、文件和 `io.ReadCloser`
6 6
 - 支持递归读取键值
7 7
 - 支持读取父子分区
8 8
 - 支持读取自增键名
@@ -37,10 +37,10 @@
37 37
 
38 38
 ### 从数据源加载
39 39
 
40
-一个 **数据源** 可以是 `[]byte` 类型的原始数据,`string` 类型的文件路径。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。
40
+一个 **数据源** 可以是 `[]byte` 类型的原始数据,`string` 类型的文件路径或 `io.ReadCloser`。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。
41 41
 
42 42
 ```go
43
-cfg, err := ini.Load([]byte("raw data"), "filename")
43
+cfg, err := ini.Load([]byte("raw data"), "filename", ioutil.NopCloser(bytes.NewReader([]byte("some other data"))))
44 44
 ```
45 45
 
46 46
 或者从一个空白的文件开始:
@@ -76,8 +76,8 @@ sec1, err := cfg.GetSection("Section")
76 76
 sec2, err := cfg.GetSection("SecTIOn")
77 77
 
78 78
 // key1 和 key2 指向同一个键对象
79
-key1, err := cfg.GetKey("Key")
80
-key2, err := cfg.GetKey("KeY")
79
+key1, err := sec1.GetKey("Key")
80
+key2, err := sec2.GetKey("KeY")
81 81
 ```
82 82
 
83 83
 #### 类似 MySQL 配置中的布尔值键
@@ -99,6 +99,28 @@ cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf"))
99 99
 
100 100
 这些键的值永远为 `true`,且在保存到文件时也只会输出键名。
101 101
 
102
+如果您想要通过程序来生成此类键,则可以使用 `NewBooleanKey`:
103
+
104
+```go
105
+key, err := sec.NewBooleanKey("skip-host-cache")
106
+```
107
+
108
+#### 关于注释
109
+
110
+下述几种情况的内容将被视为注释:
111
+
112
+1. 所有以 `#` 或 `;` 开头的行
113
+2. 所有在 `#` 或 `;` 之后的内容
114
+3. 分区标签后的文字 (即 `[分区名]` 之后的内容)
115
+
116
+如果你希望使用包含 `#` 或 `;` 的值,请使用 ``` ` ``` 或 ``` """ ``` 进行包覆。
117
+
118
+除此之外,您还可以通过 `LoadOptions` 完全忽略行内注释:
119
+
120
+```go
121
+cfg, err := LoadSources(LoadOptions{IgnoreInlineComment: true}, "app.ini"))
122
+```
123
+
102 124
 ### 操作分区(Section)
103 125
 
104 126
 获取指定分区:
@@ -116,7 +138,7 @@ section, err := cfg.GetSection("")
116 138
 当您非常确定某个分区是存在的,可以使用以下简便方法:
117 139
 
118 140
 ```go
119
-section := cfg.Section("")
141
+section := cfg.Section("section name")
120 142
 ```
121 143
 
122 144
 如果不小心判断错了,要获取的分区其实是不存在的,那会发生什么呢?没事的,它会自动创建并返回一个对应的分区对象给您。
@@ -393,9 +415,15 @@ cfg.WriteTo(writer)
393 415
 cfg.WriteToIndent(writer, "\t")
394 416
 ```
395 417
 
396
-### 高级用法
418
+默认情况下,空格将被用于对齐键值之间的等号以美化输出结果,以下代码可以禁用该功能:
397 419
 
398
-#### 递归读取键值
420
+```go
421
+ini.PrettyFormat = false
422
+``` 
423
+
424
+## 高级用法
425
+
426
+### 递归读取键值
399 427
 
400 428
 在获取所有键值的过程中,特殊语法 `%(<name>)s` 会被应用,其中 `<name>` 可以是相同分区或者默认分区下的键名。字符串 `%(<name>)s` 会被相应的键值所替代,如果指定的键不存在,则会用空字符串替代。您可以最多使用 99 层的递归嵌套。
401 429
 
@@ -415,7 +443,7 @@ cfg.Section("author").Key("GITHUB").String()		// https://github.com/Unknwon
415 443
 cfg.Section("package").Key("FULL_NAME").String()	// github.com/go-ini/ini
416 444
 ```
417 445
 
418
-#### 读取父子分区
446
+### 读取父子分区
419 447
 
420 448
 您可以在分区名称中使用 `.` 来表示两个或多个分区之间的父子关系。如果某个键在子分区中不存在,则会去它的父分区中再次寻找,直到没有父分区为止。
421 449
 
@@ -440,7 +468,22 @@ cfg.Section("package.sub").Key("CLONE_URL").String()	// https://gopkg.in/ini.v1
440 468
 cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"]
441 469
 ```
442 470
 
443
-#### 读取自增键名
471
+### 无法解析的分区
472
+
473
+如果遇到一些比较特殊的分区,它们不包含常见的键值对,而是没有固定格式的纯文本,则可以使用 `LoadOptions.UnparsableSections` 进行处理:
474
+
475
+```go
476
+cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS]
477
+<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
478
+
479
+body := cfg.Section("COMMENTS").Body()
480
+
481
+/* --- start ---
482
+<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
483
+------  end  --- */
484
+```
485
+
486
+### 读取自增键名
444 487
 
445 488
 如果数据源中的键名为 `-`,则认为该键使用了自增键名的特殊语法。计数器从 1 开始,并且分区之间是相互独立的。
446 489
 

+ 104 - 49
vendor/gopkg.in/ini.v1/ini.go

@@ -20,13 +20,12 @@ import (
20 20
 	"errors"
21 21
 	"fmt"
22 22
 	"io"
23
+	"io/ioutil"
23 24
 	"os"
24 25
 	"regexp"
25 26
 	"runtime"
26
-	"strconv"
27 27
 	"strings"
28 28
 	"sync"
29
-	"time"
30 29
 )
31 30
 
32 31
 const (
@@ -36,7 +35,7 @@ const (
36 35
 
37 36
 	// Maximum allowed depth when recursively substituing variable names.
38 37
 	_DEPTH_VALUES = 99
39
-	_VERSION      = "1.21.1"
38
+	_VERSION      = "1.28.1"
40 39
 )
41 40
 
42 41
 // Version returns current package version literal.
@@ -59,6 +58,9 @@ var (
59 58
 
60 59
 	// Explicitly write DEFAULT section header
61 60
 	DefaultHeader = false
61
+
62
+	// Indicate whether to put a line between sections
63
+	PrettySection = true
62 64
 )
63 65
 
64 66
 func init() {
@@ -108,7 +110,16 @@ type sourceData struct {
108 110
 }
109 111
 
110 112
 func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
111
-	return &bytesReadCloser{bytes.NewReader(s.data)}, nil
113
+	return ioutil.NopCloser(bytes.NewReader(s.data)), nil
114
+}
115
+
116
+// sourceReadCloser represents an input stream with Close method.
117
+type sourceReadCloser struct {
118
+	reader io.ReadCloser
119
+}
120
+
121
+func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
122
+	return s.reader, nil
112 123
 }
113 124
 
114 125
 // File represents a combination of a or more INI file(s) in memory.
@@ -149,6 +160,8 @@ func parseDataSource(source interface{}) (dataSource, error) {
149 160
 		return sourceFile{s}, nil
150 161
 	case []byte:
151 162
 		return &sourceData{s}, nil
163
+	case io.ReadCloser:
164
+		return &sourceReadCloser{s}, nil
152 165
 	default:
153 166
 		return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s)
154 167
 	}
@@ -161,9 +174,16 @@ type LoadOptions struct {
161 174
 	Insensitive bool
162 175
 	// IgnoreContinuation indicates whether to ignore continuation lines while parsing.
163 176
 	IgnoreContinuation bool
177
+	// IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
178
+	IgnoreInlineComment bool
164 179
 	// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
165 180
 	// This type of keys are mostly used in my.cnf.
166 181
 	AllowBooleanKeys bool
182
+	// AllowShadows indicates whether to keep track of keys with same name under same section.
183
+	AllowShadows bool
184
+	// Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
185
+	// conform to key/value pairs. Specify the names of those blocks here.
186
+	UnparseableSections []string
167 187
 }
168 188
 
169 189
 func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
@@ -204,6 +224,12 @@ func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
204 224
 	return LoadSources(LoadOptions{Insensitive: true}, source, others...)
205 225
 }
206 226
 
227
+// InsensitiveLoad has exactly same functionality as Load function
228
+// except it allows have shadow keys.
229
+func ShadowLoad(source interface{}, others ...interface{}) (*File, error) {
230
+	return LoadSources(LoadOptions{AllowShadows: true}, source, others...)
231
+}
232
+
207 233
 // Empty returns an empty file object.
208 234
 func Empty() *File {
209 235
 	// Ignore error here, we sure our data is good.
@@ -233,6 +259,18 @@ func (f *File) NewSection(name string) (*Section, error) {
233 259
 	return f.sections[name], nil
234 260
 }
235 261
 
262
+// NewRawSection creates a new section with an unparseable body.
263
+func (f *File) NewRawSection(name, body string) (*Section, error) {
264
+	section, err := f.NewSection(name)
265
+	if err != nil {
266
+		return nil, err
267
+	}
268
+
269
+	section.isRawSection = true
270
+	section.rawBody = body
271
+	return section, nil
272
+}
273
+
236 274
 // NewSections creates a list of sections.
237 275
 func (f *File) NewSections(names ...string) (err error) {
238 276
 	for _, name := range names {
@@ -284,6 +322,11 @@ func (f *File) Sections() []*Section {
284 322
 	return sections
285 323
 }
286 324
 
325
+// ChildSections returns a list of child sections of given section name.
326
+func (f *File) ChildSections(name string) []*Section {
327
+	return f.Section(name).ChildSections()
328
+}
329
+
287 330
 // SectionStrings returns list of section names.
288 331
 func (f *File) SectionStrings() []string {
289 332
 	list := make([]string, len(f.sectionList))
@@ -353,10 +396,7 @@ func (f *File) Append(source interface{}, others ...interface{}) error {
353 396
 	return f.Reload()
354 397
 }
355 398
 
356
-// WriteToIndent writes content into io.Writer with given indention.
357
-// If PrettyFormat has been set to be true,
358
-// it will align "=" sign with spaces under each section.
359
-func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
399
+func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
360 400
 	equalSign := "="
361 401
 	if PrettyFormat {
362 402
 		equalSign = " = "
@@ -370,14 +410,14 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
370 410
 			if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
371 411
 				sec.Comment = "; " + sec.Comment
372 412
 			}
373
-			if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil {
374
-				return 0, err
413
+			if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil {
414
+				return nil, err
375 415
 			}
376 416
 		}
377 417
 
378 418
 		if i > 0 || DefaultHeader {
379
-			if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
380
-				return 0, err
419
+			if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
420
+				return nil, err
381 421
 			}
382 422
 		} else {
383 423
 			// Write nothing if default section is empty
@@ -386,6 +426,13 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
386 426
 			}
387 427
 		}
388 428
 
429
+		if sec.isRawSection {
430
+			if _, err := buf.WriteString(sec.rawBody); err != nil {
431
+				return nil, err
432
+			}
433
+			continue
434
+		}
435
+
389 436
 		// Count and generate alignment length and buffer spaces using the
390 437
 		// longest key. Keys may be modifed if they contain certain characters so
391 438
 		// we need to take that into account in our calculation.
@@ -407,6 +454,7 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
407 454
 		}
408 455
 		alignSpaces := bytes.Repeat([]byte(" "), alignLength)
409 456
 
457
+	KEY_LIST:
410 458
 		for _, kname := range sec.keyList {
411 459
 			key := sec.Key(kname)
412 460
 			if len(key.Comment) > 0 {
@@ -416,8 +464,8 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
416 464
 				if key.Comment[0] != '#' && key.Comment[0] != ';' {
417 465
 					key.Comment = "; " + key.Comment
418 466
 				}
419
-				if _, err = buf.WriteString(key.Comment + LineBreak); err != nil {
420
-					return 0, err
467
+				if _, err := buf.WriteString(key.Comment + LineBreak); err != nil {
468
+					return nil, err
421 469
 				}
422 470
 			}
423 471
 
@@ -433,37 +481,55 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
433 481
 			case strings.Contains(kname, "`"):
434 482
 				kname = `"""` + kname + `"""`
435 483
 			}
436
-			if _, err = buf.WriteString(kname); err != nil {
437
-				return 0, err
438
-			}
439 484
 
440
-			if key.isBooleanType {
441
-				continue
442
-			}
485
+			for _, val := range key.ValueWithShadows() {
486
+				if _, err := buf.WriteString(kname); err != nil {
487
+					return nil, err
488
+				}
443 489
 
444
-			// Write out alignment spaces before "=" sign
445
-			if PrettyFormat {
446
-				buf.Write(alignSpaces[:alignLength-len(kname)])
447
-			}
490
+				if key.isBooleanType {
491
+					if kname != sec.keyList[len(sec.keyList)-1] {
492
+						buf.WriteString(LineBreak)
493
+					}
494
+					continue KEY_LIST
495
+				}
448 496
 
449
-			val := key.value
450
-			// In case key value contains "\n", "`", "\"", "#" or ";"
451
-			if strings.ContainsAny(val, "\n`") {
452
-				val = `"""` + val + `"""`
453
-			} else if strings.ContainsAny(val, "#;") {
454
-				val = "`" + val + "`"
455
-			}
456
-			if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil {
457
-				return 0, err
497
+				// Write out alignment spaces before "=" sign
498
+				if PrettyFormat {
499
+					buf.Write(alignSpaces[:alignLength-len(kname)])
500
+				}
501
+
502
+				// In case key value contains "\n", "`", "\"", "#" or ";"
503
+				if strings.ContainsAny(val, "\n`") {
504
+					val = `"""` + val + `"""`
505
+				} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
506
+					val = "`" + val + "`"
507
+				}
508
+				if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
509
+					return nil, err
510
+				}
458 511
 			}
459 512
 		}
460 513
 
461
-		// Put a line between sections
462
-		if _, err = buf.WriteString(LineBreak); err != nil {
463
-			return 0, err
514
+		if PrettySection {
515
+			// Put a line between sections
516
+			if _, err := buf.WriteString(LineBreak); err != nil {
517
+				return nil, err
518
+			}
464 519
 		}
465 520
 	}
466 521
 
522
+	return buf, nil
523
+}
524
+
525
+// WriteToIndent writes content into io.Writer with given indention.
526
+// If PrettyFormat has been set to be true,
527
+// it will align "=" sign with spaces under each section.
528
+func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) {
529
+	buf, err := f.writeToBuffer(indent)
530
+	if err != nil {
531
+		return 0, err
532
+	}
467 533
 	return buf.WriteTo(w)
468 534
 }
469 535
 
@@ -476,23 +542,12 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
476 542
 func (f *File) SaveToIndent(filename, indent string) error {
477 543
 	// Note: Because we are truncating with os.Create,
478 544
 	// 	so it's safer to save to a temporary file location and rename afte done.
479
-	tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp"
480
-	defer os.Remove(tmpPath)
481
-
482
-	fw, err := os.Create(tmpPath)
545
+	buf, err := f.writeToBuffer(indent);
483 546
 	if err != nil {
484 547
 		return err
485 548
 	}
486 549
 
487
-	if _, err = f.WriteToIndent(fw, indent); err != nil {
488
-		fw.Close()
489
-		return err
490
-	}
491
-	fw.Close()
492
-
493
-	// Remove old file and rename the new one.
494
-	os.Remove(filename)
495
-	return os.Rename(tmpPath, filename)
550
+	return ioutil.WriteFile(filename, buf.Bytes(), 0666)
496 551
 }
497 552
 
498 553
 // SaveTo writes content to file system.

+ 107 - 41
vendor/gopkg.in/ini.v1/key.go

@@ -15,6 +15,7 @@
15 15
 package ini
16 16
 
17 17
 import (
18
+	"errors"
18 19
 	"fmt"
19 20
 	"strconv"
20 21
 	"strings"
@@ -29,9 +30,42 @@ type Key struct {
29 30
 	isAutoIncrement bool
30 31
 	isBooleanType   bool
31 32
 
33
+	isShadow bool
34
+	shadows  []*Key
35
+
32 36
 	Comment string
33 37
 }
34 38
 
39
+// newKey simply return a key object with given values.
40
+func newKey(s *Section, name, val string) *Key {
41
+	return &Key{
42
+		s:     s,
43
+		name:  name,
44
+		value: val,
45
+	}
46
+}
47
+
48
+func (k *Key) addShadow(val string) error {
49
+	if k.isShadow {
50
+		return errors.New("cannot add shadow to another shadow key")
51
+	} else if k.isAutoIncrement || k.isBooleanType {
52
+		return errors.New("cannot add shadow to auto-increment or boolean key")
53
+	}
54
+
55
+	shadow := newKey(k.s, k.name, val)
56
+	shadow.isShadow = true
57
+	k.shadows = append(k.shadows, shadow)
58
+	return nil
59
+}
60
+
61
+// AddShadow adds a new shadow key to itself.
62
+func (k *Key) AddShadow(val string) error {
63
+	if !k.s.f.options.AllowShadows {
64
+		return errors.New("shadow key is not allowed")
65
+	}
66
+	return k.addShadow(val)
67
+}
68
+
35 69
 // ValueMapper represents a mapping function for values, e.g. os.ExpandEnv
36 70
 type ValueMapper func(string) string
37 71
 
@@ -45,16 +79,29 @@ func (k *Key) Value() string {
45 79
 	return k.value
46 80
 }
47 81
 
48
-// String returns string representation of value.
49
-func (k *Key) String() string {
50
-	val := k.value
82
+// ValueWithShadows returns raw values of key and its shadows if any.
83
+func (k *Key) ValueWithShadows() []string {
84
+	if len(k.shadows) == 0 {
85
+		return []string{k.value}
86
+	}
87
+	vals := make([]string, len(k.shadows)+1)
88
+	vals[0] = k.value
89
+	for i := range k.shadows {
90
+		vals[i+1] = k.shadows[i].value
91
+	}
92
+	return vals
93
+}
94
+
95
+// transformValue takes a raw value and transforms to its final string.
96
+func (k *Key) transformValue(val string) string {
51 97
 	if k.s.f.ValueMapper != nil {
52 98
 		val = k.s.f.ValueMapper(val)
53 99
 	}
54
-	if strings.Index(val, "%") == -1 {
100
+
101
+	// Fail-fast if no indicate char found for recursive value
102
+	if !strings.Contains(val, "%") {
55 103
 		return val
56 104
 	}
57
-
58 105
 	for i := 0; i < _DEPTH_VALUES; i++ {
59 106
 		vr := varPattern.FindString(val)
60 107
 		if len(vr) == 0 {
@@ -78,6 +125,11 @@ func (k *Key) String() string {
78 125
 	return val
79 126
 }
80 127
 
128
+// String returns string representation of value.
129
+func (k *Key) String() string {
130
+	return k.transformValue(k.value)
131
+}
132
+
81 133
 // Validate accepts a validate function which can
82 134
 // return modifed result as key value.
83 135
 func (k *Key) Validate(fn func(string) string) string {
@@ -394,45 +446,65 @@ func (k *Key) Strings(delim string) []string {
394 446
 
395 447
 	vals := strings.Split(str, delim)
396 448
 	for i := range vals {
449
+		// vals[i] = k.transformValue(strings.TrimSpace(vals[i]))
397 450
 		vals[i] = strings.TrimSpace(vals[i])
398 451
 	}
399 452
 	return vals
400 453
 }
401 454
 
455
+// StringsWithShadows returns list of string divided by given delimiter.
456
+// Shadows will also be appended if any.
457
+func (k *Key) StringsWithShadows(delim string) []string {
458
+	vals := k.ValueWithShadows()
459
+	results := make([]string, 0, len(vals)*2)
460
+	for i := range vals {
461
+		if len(vals) == 0 {
462
+			continue
463
+		}
464
+
465
+		results = append(results, strings.Split(vals[i], delim)...)
466
+	}
467
+
468
+	for i := range results {
469
+		results[i] = k.transformValue(strings.TrimSpace(results[i]))
470
+	}
471
+	return results
472
+}
473
+
402 474
 // Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value.
403 475
 func (k *Key) Float64s(delim string) []float64 {
404
-	vals, _ := k.getFloat64s(delim, true, false)
476
+	vals, _ := k.parseFloat64s(k.Strings(delim), true, false)
405 477
 	return vals
406 478
 }
407 479
 
408 480
 // Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value.
409 481
 func (k *Key) Ints(delim string) []int {
410
-	vals, _ := k.getInts(delim, true, false)
482
+	vals, _ := k.parseInts(k.Strings(delim), true, false)
411 483
 	return vals
412 484
 }
413 485
 
414 486
 // Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value.
415 487
 func (k *Key) Int64s(delim string) []int64 {
416
-	vals, _ := k.getInt64s(delim, true, false)
488
+	vals, _ := k.parseInt64s(k.Strings(delim), true, false)
417 489
 	return vals
418 490
 }
419 491
 
420 492
 // Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value.
421 493
 func (k *Key) Uints(delim string) []uint {
422
-	vals, _ := k.getUints(delim, true, false)
494
+	vals, _ := k.parseUints(k.Strings(delim), true, false)
423 495
 	return vals
424 496
 }
425 497
 
426 498
 // Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value.
427 499
 func (k *Key) Uint64s(delim string) []uint64 {
428
-	vals, _ := k.getUint64s(delim, true, false)
500
+	vals, _ := k.parseUint64s(k.Strings(delim), true, false)
429 501
 	return vals
430 502
 }
431 503
 
432 504
 // TimesFormat parses with given format and returns list of time.Time divided by given delimiter.
433 505
 // Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
434 506
 func (k *Key) TimesFormat(format, delim string) []time.Time {
435
-	vals, _ := k.getTimesFormat(format, delim, true, false)
507
+	vals, _ := k.parseTimesFormat(format, k.Strings(delim), true, false)
436 508
 	return vals
437 509
 }
438 510
 
@@ -445,41 +517,41 @@ func (k *Key) Times(delim string) []time.Time {
445 517
 // ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then
446 518
 // it will not be included to result list.
447 519
 func (k *Key) ValidFloat64s(delim string) []float64 {
448
-	vals, _ := k.getFloat64s(delim, false, false)
520
+	vals, _ := k.parseFloat64s(k.Strings(delim), false, false)
449 521
 	return vals
450 522
 }
451 523
 
452 524
 // ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will
453 525
 // not be included to result list.
454 526
 func (k *Key) ValidInts(delim string) []int {
455
-	vals, _ := k.getInts(delim, false, false)
527
+	vals, _ := k.parseInts(k.Strings(delim), false, false)
456 528
 	return vals
457 529
 }
458 530
 
459 531
 // ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer,
460 532
 // then it will not be included to result list.
461 533
 func (k *Key) ValidInt64s(delim string) []int64 {
462
-	vals, _ := k.getInt64s(delim, false, false)
534
+	vals, _ := k.parseInt64s(k.Strings(delim), false, false)
463 535
 	return vals
464 536
 }
465 537
 
466 538
 // ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer,
467 539
 // then it will not be included to result list.
468 540
 func (k *Key) ValidUints(delim string) []uint {
469
-	vals, _ := k.getUints(delim, false, false)
541
+	vals, _ := k.parseUints(k.Strings(delim), false, false)
470 542
 	return vals
471 543
 }
472 544
 
473 545
 // ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned
474 546
 // integer, then it will not be included to result list.
475 547
 func (k *Key) ValidUint64s(delim string) []uint64 {
476
-	vals, _ := k.getUint64s(delim, false, false)
548
+	vals, _ := k.parseUint64s(k.Strings(delim), false, false)
477 549
 	return vals
478 550
 }
479 551
 
480 552
 // ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter.
481 553
 func (k *Key) ValidTimesFormat(format, delim string) []time.Time {
482
-	vals, _ := k.getTimesFormat(format, delim, false, false)
554
+	vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false)
483 555
 	return vals
484 556
 }
485 557
 
@@ -490,33 +562,33 @@ func (k *Key) ValidTimes(delim string) []time.Time {
490 562
 
491 563
 // StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input.
492 564
 func (k *Key) StrictFloat64s(delim string) ([]float64, error) {
493
-	return k.getFloat64s(delim, false, true)
565
+	return k.parseFloat64s(k.Strings(delim), false, true)
494 566
 }
495 567
 
496 568
 // StrictInts returns list of int divided by given delimiter or error on first invalid input.
497 569
 func (k *Key) StrictInts(delim string) ([]int, error) {
498
-	return k.getInts(delim, false, true)
570
+	return k.parseInts(k.Strings(delim), false, true)
499 571
 }
500 572
 
501 573
 // StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input.
502 574
 func (k *Key) StrictInt64s(delim string) ([]int64, error) {
503
-	return k.getInt64s(delim, false, true)
575
+	return k.parseInt64s(k.Strings(delim), false, true)
504 576
 }
505 577
 
506 578
 // StrictUints returns list of uint divided by given delimiter or error on first invalid input.
507 579
 func (k *Key) StrictUints(delim string) ([]uint, error) {
508
-	return k.getUints(delim, false, true)
580
+	return k.parseUints(k.Strings(delim), false, true)
509 581
 }
510 582
 
511 583
 // StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input.
512 584
 func (k *Key) StrictUint64s(delim string) ([]uint64, error) {
513
-	return k.getUint64s(delim, false, true)
585
+	return k.parseUint64s(k.Strings(delim), false, true)
514 586
 }
515 587
 
516 588
 // StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter
517 589
 // or error on first invalid input.
518 590
 func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) {
519
-	return k.getTimesFormat(format, delim, false, true)
591
+	return k.parseTimesFormat(format, k.Strings(delim), false, true)
520 592
 }
521 593
 
522 594
 // StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter
@@ -525,9 +597,8 @@ func (k *Key) StrictTimes(delim string) ([]time.Time, error) {
525 597
 	return k.StrictTimesFormat(time.RFC3339, delim)
526 598
 }
527 599
 
528
-// getFloat64s returns list of float64 divided by given delimiter.
529
-func (k *Key) getFloat64s(delim string, addInvalid, returnOnInvalid bool) ([]float64, error) {
530
-	strs := k.Strings(delim)
600
+// parseFloat64s transforms strings to float64s.
601
+func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) {
531 602
 	vals := make([]float64, 0, len(strs))
532 603
 	for _, str := range strs {
533 604
 		val, err := strconv.ParseFloat(str, 64)
@@ -541,9 +612,8 @@ func (k *Key) getFloat64s(delim string, addInvalid, returnOnInvalid bool) ([]flo
541 612
 	return vals, nil
542 613
 }
543 614
 
544
-// getInts returns list of int divided by given delimiter.
545
-func (k *Key) getInts(delim string, addInvalid, returnOnInvalid bool) ([]int, error) {
546
-	strs := k.Strings(delim)
615
+// parseInts transforms strings to ints.
616
+func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) {
547 617
 	vals := make([]int, 0, len(strs))
548 618
 	for _, str := range strs {
549 619
 		val, err := strconv.Atoi(str)
@@ -557,9 +627,8 @@ func (k *Key) getInts(delim string, addInvalid, returnOnInvalid bool) ([]int, er
557 627
 	return vals, nil
558 628
 }
559 629
 
560
-// getInt64s returns list of int64 divided by given delimiter.
561
-func (k *Key) getInt64s(delim string, addInvalid, returnOnInvalid bool) ([]int64, error) {
562
-	strs := k.Strings(delim)
630
+// parseInt64s transforms strings to int64s.
631
+func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) {
563 632
 	vals := make([]int64, 0, len(strs))
564 633
 	for _, str := range strs {
565 634
 		val, err := strconv.ParseInt(str, 10, 64)
@@ -573,9 +642,8 @@ func (k *Key) getInt64s(delim string, addInvalid, returnOnInvalid bool) ([]int64
573 642
 	return vals, nil
574 643
 }
575 644
 
576
-// getUints returns list of uint divided by given delimiter.
577
-func (k *Key) getUints(delim string, addInvalid, returnOnInvalid bool) ([]uint, error) {
578
-	strs := k.Strings(delim)
645
+// parseUints transforms strings to uints.
646
+func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) {
579 647
 	vals := make([]uint, 0, len(strs))
580 648
 	for _, str := range strs {
581 649
 		val, err := strconv.ParseUint(str, 10, 0)
@@ -589,9 +657,8 @@ func (k *Key) getUints(delim string, addInvalid, returnOnInvalid bool) ([]uint,
589 657
 	return vals, nil
590 658
 }
591 659
 
592
-// getUint64s returns list of uint64 divided by given delimiter.
593
-func (k *Key) getUint64s(delim string, addInvalid, returnOnInvalid bool) ([]uint64, error) {
594
-	strs := k.Strings(delim)
660
+// parseUint64s transforms strings to uint64s.
661
+func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) {
595 662
 	vals := make([]uint64, 0, len(strs))
596 663
 	for _, str := range strs {
597 664
 		val, err := strconv.ParseUint(str, 10, 64)
@@ -605,9 +672,8 @@ func (k *Key) getUint64s(delim string, addInvalid, returnOnInvalid bool) ([]uint
605 672
 	return vals, nil
606 673
 }
607 674
 
608
-// getTimesFormat parses with given format and returns list of time.Time divided by given delimiter.
609
-func (k *Key) getTimesFormat(format, delim string, addInvalid, returnOnInvalid bool) ([]time.Time, error) {
610
-	strs := k.Strings(delim)
675
+// parseTimesFormat transforms strings to times in given format.
676
+func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) {
611 677
 	vals := make([]time.Time, 0, len(strs))
612 678
 	for _, str := range strs {
613 679
 		val, err := time.Parse(format, str)

+ 54 - 18
vendor/gopkg.in/ini.v1/parser.go

@@ -48,16 +48,31 @@ func newParser(r io.Reader) *parser {
48 48
 	}
49 49
 }
50 50
 
51
-// BOM handles header of BOM-UTF8 format.
51
+// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format.
52 52
 // http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
53 53
 func (p *parser) BOM() error {
54
-	mask, err := p.buf.Peek(3)
54
+	mask, err := p.buf.Peek(2)
55 55
 	if err != nil && err != io.EOF {
56 56
 		return err
57
-	} else if len(mask) < 3 {
57
+	} else if len(mask) < 2 {
58 58
 		return nil
59
-	} else if mask[0] == 239 && mask[1] == 187 && mask[2] == 191 {
59
+	}
60
+
61
+	switch {
62
+	case mask[0] == 254 && mask[1] == 255:
63
+		fallthrough
64
+	case mask[0] == 255 && mask[1] == 254:
60 65
 		p.buf.Read(mask)
66
+	case mask[0] == 239 && mask[1] == 187:
67
+		mask, err := p.buf.Peek(3)
68
+		if err != nil && err != io.EOF {
69
+			return err
70
+		} else if len(mask) < 3 {
71
+			return nil
72
+		}
73
+		if mask[2] == 191 {
74
+			p.buf.Read(mask)
75
+		}
61 76
 	}
62 77
 	return nil
63 78
 }
@@ -174,11 +189,11 @@ func (p *parser) readContinuationLines(val string) (string, error) {
174 189
 // are quotes \" or \'.
175 190
 // It returns false if any other parts also contain same kind of quotes.
176 191
 func hasSurroundedQuote(in string, quote byte) bool {
177
-	return len(in) > 2 && in[0] == quote && in[len(in)-1] == quote &&
192
+	return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote &&
178 193
 		strings.IndexByte(in[1:], quote) == len(in)-2
179 194
 }
180 195
 
181
-func (p *parser) readValue(in []byte, ignoreContinuation bool) (string, error) {
196
+func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bool) (string, error) {
182 197
 	line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
183 198
 	if len(line) == 0 {
184 199
 		return "", nil
@@ -202,18 +217,21 @@ func (p *parser) readValue(in []byte, ignoreContinuation bool) (string, error) {
202 217
 		return line[startIdx : pos+startIdx], nil
203 218
 	}
204 219
 
205
-	// Won't be able to reach here if value only contains whitespace.
220
+	// Won't be able to reach here if value only contains whitespace
206 221
 	line = strings.TrimSpace(line)
207 222
 
208
-	// Check continuation lines when desired.
223
+	// Check continuation lines when desired
209 224
 	if !ignoreContinuation && line[len(line)-1] == '\\' {
210 225
 		return p.readContinuationLines(line[:len(line)-1])
211 226
 	}
212 227
 
213
-	i := strings.IndexAny(line, "#;")
214
-	if i > -1 {
215
-		p.comment.WriteString(line[i:])
216
-		line = strings.TrimSpace(line[:i])
228
+	// Check if ignore inline comment
229
+	if !ignoreInlineComment {
230
+		i := strings.IndexAny(line, "#;")
231
+		if i > -1 {
232
+			p.comment.WriteString(line[i:])
233
+			line = strings.TrimSpace(line[:i])
234
+		}
217 235
 	}
218 236
 
219 237
 	// Trim single quotes
@@ -235,6 +253,7 @@ func (f *File) parse(reader io.Reader) (err error) {
235 253
 	section, _ := f.NewSection(DEFAULT_SECTION)
236 254
 
237 255
 	var line []byte
256
+	var inUnparseableSection bool
238 257
 	for !p.isEOF {
239 258
 		line, err = p.readUntil('\n')
240 259
 		if err != nil {
@@ -280,6 +299,21 @@ func (f *File) parse(reader io.Reader) (err error) {
280 299
 			// Reset aotu-counter and comments
281 300
 			p.comment.Reset()
282 301
 			p.count = 1
302
+
303
+			inUnparseableSection = false
304
+			for i := range f.options.UnparseableSections {
305
+				if f.options.UnparseableSections[i] == name ||
306
+					(f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) {
307
+					inUnparseableSection = true
308
+					continue
309
+				}
310
+			}
311
+			continue
312
+		}
313
+
314
+		if inUnparseableSection {
315
+			section.isRawSection = true
316
+			section.rawBody += string(line)
283 317
 			continue
284 318
 		}
285 319
 
@@ -287,11 +321,14 @@ func (f *File) parse(reader io.Reader) (err error) {
287 321
 		if err != nil {
288 322
 			// Treat as boolean key when desired, and whole line is key name.
289 323
 			if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys {
290
-				key, err := section.NewKey(string(line), "true")
324
+				kname, err := p.readValue(line, f.options.IgnoreContinuation, f.options.IgnoreInlineComment)
325
+				if err != nil {
326
+					return err
327
+				}
328
+				key, err := section.NewBooleanKey(kname)
291 329
 				if err != nil {
292 330
 					return err
293 331
 				}
294
-				key.isBooleanType = true
295 332
 				key.Comment = strings.TrimSpace(p.comment.String())
296 333
 				p.comment.Reset()
297 334
 				continue
@@ -307,17 +344,16 @@ func (f *File) parse(reader io.Reader) (err error) {
307 344
 			p.count++
308 345
 		}
309 346
 
310
-		key, err := section.NewKey(kname, "")
347
+		value, err := p.readValue(line[offset:], f.options.IgnoreContinuation, f.options.IgnoreInlineComment)
311 348
 		if err != nil {
312 349
 			return err
313 350
 		}
314
-		key.isAutoIncrement = isAutoIncr
315 351
 
316
-		value, err := p.readValue(line[offset:], f.options.IgnoreContinuation)
352
+		key, err := section.NewKey(kname, value)
317 353
 		if err != nil {
318 354
 			return err
319 355
 		}
320
-		key.SetValue(value)
356
+		key.isAutoIncrement = isAutoIncr
321 357
 		key.Comment = strings.TrimSpace(p.comment.String())
322 358
 		p.comment.Reset()
323 359
 	}

+ 49 - 7
vendor/gopkg.in/ini.v1/section.go

@@ -28,10 +28,19 @@ type Section struct {
28 28
 	keys     map[string]*Key
29 29
 	keyList  []string
30 30
 	keysHash map[string]string
31
+
32
+	isRawSection bool
33
+	rawBody      string
31 34
 }
32 35
 
33 36
 func newSection(f *File, name string) *Section {
34
-	return &Section{f, "", name, make(map[string]*Key), make([]string, 0, 10), make(map[string]string)}
37
+	return &Section{
38
+		f:        f,
39
+		name:     name,
40
+		keys:     make(map[string]*Key),
41
+		keyList:  make([]string, 0, 10),
42
+		keysHash: make(map[string]string),
43
+	}
35 44
 }
36 45
 
37 46
 // Name returns name of Section.
@@ -39,6 +48,12 @@ func (s *Section) Name() string {
39 48
 	return s.name
40 49
 }
41 50
 
51
+// Body returns rawBody of Section if the section was marked as unparseable.
52
+// It still follows the other rules of the INI format surrounding leading/trailing whitespace.
53
+func (s *Section) Body() string {
54
+	return strings.TrimSpace(s.rawBody)
55
+}
56
+
42 57
 // NewKey creates a new key to given section.
43 58
 func (s *Section) NewKey(name, val string) (*Key, error) {
44 59
 	if len(name) == 0 {
@@ -53,20 +68,33 @@ func (s *Section) NewKey(name, val string) (*Key, error) {
53 68
 	}
54 69
 
55 70
 	if inSlice(name, s.keyList) {
56
-		s.keys[name].value = val
71
+		if s.f.options.AllowShadows {
72
+			if err := s.keys[name].addShadow(val); err != nil {
73
+				return nil, err
74
+			}
75
+		} else {
76
+			s.keys[name].value = val
77
+		}
57 78
 		return s.keys[name], nil
58 79
 	}
59 80
 
60 81
 	s.keyList = append(s.keyList, name)
61
-	s.keys[name] = &Key{
62
-		s:     s,
63
-		name:  name,
64
-		value: val,
65
-	}
82
+	s.keys[name] = newKey(s, name, val)
66 83
 	s.keysHash[name] = val
67 84
 	return s.keys[name], nil
68 85
 }
69 86
 
87
+// NewBooleanKey creates a new boolean type key to given section.
88
+func (s *Section) NewBooleanKey(name string) (*Key, error) {
89
+	key, err := s.NewKey(name, "true")
90
+	if err != nil {
91
+		return nil, err
92
+	}
93
+
94
+	key.isBooleanType = true
95
+	return key, nil
96
+}
97
+
70 98
 // GetKey returns key in section by given name.
71 99
 func (s *Section) GetKey(name string) (*Key, error) {
72 100
 	// FIXME: change to section level lock?
@@ -204,3 +232,17 @@ func (s *Section) DeleteKey(name string) {
204 232
 		}
205 233
 	}
206 234
 }
235
+
236
+// ChildSections returns a list of child sections of current section.
237
+// For example, "[parent.child1]" and "[parent.child12]" are child sections
238
+// of section "[parent]".
239
+func (s *Section) ChildSections() []*Section {
240
+	prefix := s.name + "."
241
+	children := make([]*Section, 0, 3)
242
+	for _, name := range s.f.sectionList {
243
+		if strings.HasPrefix(name, prefix) {
244
+			children = append(children, s.f.sections[name])
245
+		}
246
+	}
247
+	return children
248
+}

+ 94 - 25
vendor/gopkg.in/ini.v1/struct.go

@@ -78,34 +78,44 @@ func parseDelim(actual string) string {
78 78
 var reflectTime = reflect.TypeOf(time.Now()).Kind()
79 79
 
80 80
 // setSliceWithProperType sets proper values to slice based on its type.
81
-func setSliceWithProperType(key *Key, field reflect.Value, delim string) error {
82
-	strs := key.Strings(delim)
81
+func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
82
+	var strs []string
83
+	if allowShadow {
84
+		strs = key.StringsWithShadows(delim)
85
+	} else {
86
+		strs = key.Strings(delim)
87
+	}
88
+
83 89
 	numVals := len(strs)
84 90
 	if numVals == 0 {
85 91
 		return nil
86 92
 	}
87 93
 
88 94
 	var vals interface{}
95
+	var err error
89 96
 
90 97
 	sliceOf := field.Type().Elem().Kind()
91 98
 	switch sliceOf {
92 99
 	case reflect.String:
93 100
 		vals = strs
94 101
 	case reflect.Int:
95
-		vals = key.Ints(delim)
102
+		vals, err = key.parseInts(strs, true, false)
96 103
 	case reflect.Int64:
97
-		vals = key.Int64s(delim)
104
+		vals, err = key.parseInt64s(strs, true, false)
98 105
 	case reflect.Uint:
99
-		vals = key.Uints(delim)
106
+		vals, err = key.parseUints(strs, true, false)
100 107
 	case reflect.Uint64:
101
-		vals = key.Uint64s(delim)
108
+		vals, err = key.parseUint64s(strs, true, false)
102 109
 	case reflect.Float64:
103
-		vals = key.Float64s(delim)
110
+		vals, err = key.parseFloat64s(strs, true, false)
104 111
 	case reflectTime:
105
-		vals = key.Times(delim)
112
+		vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)
106 113
 	default:
107 114
 		return fmt.Errorf("unsupported type '[]%s'", sliceOf)
108 115
 	}
116
+	if isStrict {
117
+		return err
118
+	}
109 119
 
110 120
 	slice := reflect.MakeSlice(field.Type(), numVals, numVals)
111 121
 	for i := 0; i < numVals; i++ {
@@ -130,10 +140,17 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string) error {
130 140
 	return nil
131 141
 }
132 142
 
143
+func wrapStrictError(err error, isStrict bool) error {
144
+	if isStrict {
145
+		return err
146
+	}
147
+	return nil
148
+}
149
+
133 150
 // setWithProperType sets proper value to field based on its type,
134 151
 // but it does not return error for failing parsing,
135 152
 // because we want to use default value that is already assigned to strcut.
136
-func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {
153
+func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
137 154
 	switch t.Kind() {
138 155
 	case reflect.String:
139 156
 		if len(key.String()) == 0 {
@@ -143,7 +160,7 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
143 160
 	case reflect.Bool:
144 161
 		boolVal, err := key.Bool()
145 162
 		if err != nil {
146
-			return nil
163
+			return wrapStrictError(err, isStrict)
147 164
 		}
148 165
 		field.SetBool(boolVal)
149 166
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -155,8 +172,8 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
155 172
 		}
156 173
 
157 174
 		intVal, err := key.Int64()
158
-		if err != nil || intVal == 0 {
159
-			return nil
175
+		if err != nil {
176
+			return wrapStrictError(err, isStrict)
160 177
 		}
161 178
 		field.SetInt(intVal)
162 179
 	//	byte is an alias for uint8, so supporting uint8 breaks support for byte
@@ -170,31 +187,43 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
170 187
 
171 188
 		uintVal, err := key.Uint64()
172 189
 		if err != nil {
173
-			return nil
190
+			return wrapStrictError(err, isStrict)
174 191
 		}
175 192
 		field.SetUint(uintVal)
176 193
 
177
-	case reflect.Float64:
194
+	case reflect.Float32, reflect.Float64:
178 195
 		floatVal, err := key.Float64()
179 196
 		if err != nil {
180
-			return nil
197
+			return wrapStrictError(err, isStrict)
181 198
 		}
182 199
 		field.SetFloat(floatVal)
183 200
 	case reflectTime:
184 201
 		timeVal, err := key.Time()
185 202
 		if err != nil {
186
-			return nil
203
+			return wrapStrictError(err, isStrict)
187 204
 		}
188 205
 		field.Set(reflect.ValueOf(timeVal))
189 206
 	case reflect.Slice:
190
-		return setSliceWithProperType(key, field, delim)
207
+		return setSliceWithProperType(key, field, delim, allowShadow, isStrict)
191 208
 	default:
192 209
 		return fmt.Errorf("unsupported type '%s'", t)
193 210
 	}
194 211
 	return nil
195 212
 }
196 213
 
197
-func (s *Section) mapTo(val reflect.Value) error {
214
+func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) {
215
+	opts := strings.SplitN(tag, ",", 3)
216
+	rawName = opts[0]
217
+	if len(opts) > 1 {
218
+		omitEmpty = opts[1] == "omitempty"
219
+	}
220
+	if len(opts) > 2 {
221
+		allowShadow = opts[2] == "allowshadow"
222
+	}
223
+	return rawName, omitEmpty, allowShadow
224
+}
225
+
226
+func (s *Section) mapTo(val reflect.Value, isStrict bool) error {
198 227
 	if val.Kind() == reflect.Ptr {
199 228
 		val = val.Elem()
200 229
 	}
@@ -209,8 +238,8 @@ func (s *Section) mapTo(val reflect.Value) error {
209 238
 			continue
210 239
 		}
211 240
 
212
-		opts := strings.SplitN(tag, ",", 2) // strip off possible omitempty
213
-		fieldName := s.parseFieldName(tpField.Name, opts[0])
241
+		rawName, _, allowShadow := parseTagOptions(tag)
242
+		fieldName := s.parseFieldName(tpField.Name, rawName)
214 243
 		if len(fieldName) == 0 || !field.CanSet() {
215 244
 			continue
216 245
 		}
@@ -223,7 +252,7 @@ func (s *Section) mapTo(val reflect.Value) error {
223 252
 
224 253
 		if isAnonymous || isStruct {
225 254
 			if sec, err := s.f.GetSection(fieldName); err == nil {
226
-				if err = sec.mapTo(field); err != nil {
255
+				if err = sec.mapTo(field, isStrict); err != nil {
227 256
 					return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
228 257
 				}
229 258
 				continue
@@ -231,7 +260,8 @@ func (s *Section) mapTo(val reflect.Value) error {
231 260
 		}
232 261
 
233 262
 		if key, err := s.GetKey(fieldName); err == nil {
234
-			if err = setWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
263
+			delim := parseDelim(tpField.Tag.Get("delim"))
264
+			if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
235 265
 				return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
236 266
 			}
237 267
 		}
@@ -250,7 +280,22 @@ func (s *Section) MapTo(v interface{}) error {
250 280
 		return errors.New("cannot map to non-pointer struct")
251 281
 	}
252 282
 
253
-	return s.mapTo(val)
283
+	return s.mapTo(val, false)
284
+}
285
+
286
+// MapTo maps section to given struct in strict mode,
287
+// which returns all possible error including value parsing error.
288
+func (s *Section) StrictMapTo(v interface{}) error {
289
+	typ := reflect.TypeOf(v)
290
+	val := reflect.ValueOf(v)
291
+	if typ.Kind() == reflect.Ptr {
292
+		typ = typ.Elem()
293
+		val = val.Elem()
294
+	} else {
295
+		return errors.New("cannot map to non-pointer struct")
296
+	}
297
+
298
+	return s.mapTo(val, true)
254 299
 }
255 300
 
256 301
 // MapTo maps file to given struct.
@@ -258,6 +303,12 @@ func (f *File) MapTo(v interface{}) error {
258 303
 	return f.Section("").MapTo(v)
259 304
 }
260 305
 
306
+// MapTo maps file to given struct in strict mode,
307
+// which returns all possible error including value parsing error.
308
+func (f *File) StrictMapTo(v interface{}) error {
309
+	return f.Section("").StrictMapTo(v)
310
+}
311
+
261 312
 // MapTo maps data sources to given struct with name mapper.
262 313
 func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
263 314
 	cfg, err := Load(source, others...)
@@ -268,11 +319,28 @@ func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, other
268 319
 	return cfg.MapTo(v)
269 320
 }
270 321
 
322
+// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode,
323
+// which returns all possible error including value parsing error.
324
+func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
325
+	cfg, err := Load(source, others...)
326
+	if err != nil {
327
+		return err
328
+	}
329
+	cfg.NameMapper = mapper
330
+	return cfg.StrictMapTo(v)
331
+}
332
+
271 333
 // MapTo maps data sources to given struct.
272 334
 func MapTo(v, source interface{}, others ...interface{}) error {
273 335
 	return MapToWithMapper(v, nil, source, others...)
274 336
 }
275 337
 
338
+// StrictMapTo maps data sources to given struct in strict mode,
339
+// which returns all possible error including value parsing error.
340
+func StrictMapTo(v, source interface{}, others ...interface{}) error {
341
+	return StrictMapToWithMapper(v, nil, source, others...)
342
+}
343
+
276 344
 // reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
277 345
 func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error {
278 346
 	slice := field.Slice(0, field.Len())
@@ -340,10 +408,11 @@ func isEmptyValue(v reflect.Value) bool {
340 408
 		return v.Uint() == 0
341 409
 	case reflect.Float32, reflect.Float64:
342 410
 		return v.Float() == 0
343
-	case reflectTime:
344
-		return v.Interface().(time.Time).IsZero()
345 411
 	case reflect.Interface, reflect.Ptr:
346 412
 		return v.IsNil()
413
+	case reflectTime:
414
+		t, ok := v.Interface().(time.Time)
415
+		return ok && t.IsZero()
347 416
 	}
348 417
 	return false
349 418
 }

+ 4 - 3
vendor/vendor.json

@@ -1466,10 +1466,11 @@
1466 1466
 			"revisionTime": "2016-04-11T21:29:32Z"
1467 1467
 		},
1468 1468
 		{
1469
-			"checksumSHA1": "YRD335tkMvgHzkfbfveMUpsE3Bw=",
1469
+			"checksumSHA1": "MMb7aeIRnJq17iQvuGvevymOIYQ=",
1470
+			"origin": "github.com/go-gitea/ini",
1470 1471
 			"path": "gopkg.in/ini.v1",
1471
-			"revision": "6e4869b434bd001f6983749881c7ead3545887d8",
1472
-			"revisionTime": "2016-08-27T06:11:18Z"
1472
+			"revision": "88679ba677ac064c7880c9bde81ef5b9fd132e82",
1473
+			"revisionTime": "2017-08-04T04:10:12Z"
1473 1474
 		},
1474 1475
 		{
1475 1476
 			"checksumSHA1": "7jPSjzw3mckHVQ2SjY4NvtIJR4g=",