-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathparse.go
More file actions
88 lines (84 loc) · 2.49 KB
/
parse.go
File metadata and controls
88 lines (84 loc) · 2.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package inifile
import (
"bufio"
"bytes"
"io"
"regexp"
"strconv"
"strings"
)
var (
iniSectionRegex = regexp.MustCompile(`^\[([^\]\r\n]*)\](?:[ \t]*[;#][^\r\n]*)?$`)
iniAssignRegex = regexp.MustCompile(`^[ \t]*([^\r\n=]+)[ \t]*=[ \t]*([^\r\n]*)[ \t]*$`)
iniUTF8BOM = []byte("\uFEFF")
)
func scanLinesWithLineNumbers(scanLineNum, tokenLineNum *int) bufio.SplitFunc {
return func(data []byte, atEOF bool) (advance int, token []byte, err error) {
advance, token, err = bufio.ScanLines(data, atEOF)
if token != nil {
*tokenLineNum = *scanLineNum
if *tokenLineNum == 1 {
token = bytes.TrimPrefix(token, iniUTF8BOM)
}
}
if advance > 0 {
*scanLineNum += bytes.Count(data[:advance], []byte{'\n'})
}
return
}
}
// Parse reads INI data from an io.Reader and returns a new File.
//
// Unquoted values are trimmed of whitespace.
// Quoted values preserve whitespace inside the quotes.
// Parsed values are always valid UTF-8.
// Input should use LF (\n) or CRLF (\r\n) line endings.
// A trailing CR-only line ending on the final line is tolerated, but bare CR (\r)
// characters elsewhere are rejected as syntax errors.
// Lines are limited by bufio.Scanner's default token size (~64 KiB).
//
// If dupKeysJoin is zero, a duplicate key will replace the previous value.
// If dupKeysJoin is nonzero, a duplicate key will append it's value to
// the preexisting key's value using dupKeysJoin as a separator.
//
// You are allowed to pass in a nil io.Reader, which results in
// a nil File and no error.
func Parse(r io.Reader, dupKeysJoin rune) (inif File, err error) {
if r != nil {
scanLineNum := 1
tokenLineNum := 1
section := ""
line := ""
inif = make(File)
scanner := bufio.NewScanner(r)
scanner.Split(scanLinesWithLineNumbers(&scanLineNum, &tokenLineNum))
for err == nil && scanner.Scan() {
line = strings.TrimSpace(scanner.Text())
if len(line) > 0 && line[0] != ';' && line[0] != '#' {
if groups := iniAssignRegex.FindStringSubmatch(line); groups != nil {
var value string
if value, err = ParseValue(groups[2]); err == nil {
inif.Section(section).Set(groups[1], value, dupKeysJoin)
}
} else if groups := iniSectionRegex.FindStringSubmatch(line); groups != nil {
section = Key(groups[1])
} else {
err = strconv.ErrSyntax
}
}
}
if err == nil {
if err = scanner.Err(); err != nil {
inif = nil
}
} else {
inif = nil
err = SyntaxError{
Line: tokenLineNum,
Source: line,
Err: err,
}
}
}
return
}