Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ini_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,11 +507,16 @@ long_rsa_private_key = -----BEGIN RSA PRIVATE KEY-----
foobar
barfoo
-----END RSA PRIVATE KEY-----
multiline_list =
first
second
third
`))
So(err, ShouldBeNil)
So(f, ShouldNotBeNil)

So(f.Section("long").Key("long_rsa_private_key").String(), ShouldEqual, "-----BEGIN RSA PRIVATE KEY-----\nfoo\nbar\nfoobar\nbarfoo\n-----END RSA PRIVATE KEY-----")
So(f.Section("long").Key("multiline_list").String(), ShouldEqual, "\nfirst\nsecond\nthird")
})

Convey("Can parse big python-compatible INI files", func() {
Expand Down
59 changes: 33 additions & 26 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ func (p *parser) readValue(in []byte, bufferSize int) (string, error) {

line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
if len(line) == 0 {
if p.options.AllowPythonMultilineValues && len(in) > 0 && in[len(in)-1] == '\n' {
return p.readPythonMultilines(line, bufferSize)
}
return "", nil
}

Expand Down Expand Up @@ -272,42 +275,46 @@ func (p *parser) readValue(in []byte, bufferSize int) (string, error) {
line = strings.Replace(line, `\#`, "#", -1)
}
} else if p.options.AllowPythonMultilineValues && lastChar == '\n' {
parserBufferPeekResult, _ := p.buf.Peek(bufferSize)
peekBuffer := bytes.NewBuffer(parserBufferPeekResult)
return p.readPythonMultilines(line, bufferSize)
}

val := line
return line, nil
}

for {
peekData, peekErr := peekBuffer.ReadBytes('\n')
if peekErr != nil {
if peekErr == io.EOF {
return val, nil
}
return "", peekErr
}
func (p *parser) readPythonMultilines(line string, bufferSize int) (string, error) {
parserBufferPeekResult, _ := p.buf.Peek(bufferSize)
peekBuffer := bytes.NewBuffer(parserBufferPeekResult)

peekMatches := pythonMultiline.FindStringSubmatch(string(peekData))
if len(peekMatches) != 3 {
return val, nil
}
val := line

// NOTE: Return if not a python-ini multi-line value.
currentIdentSize := len(peekMatches[1])
if currentIdentSize <= 0 {
for {
peekData, peekErr := peekBuffer.ReadBytes('\n')
if peekErr != nil {
if peekErr == io.EOF {
return val, nil
}
return "", peekErr
}

// NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer.
_, err := p.readUntil('\n')
if err != nil {
return "", err
}
peekMatches := pythonMultiline.FindStringSubmatch(string(peekData))
if len(peekMatches) != 3 {
return val, nil
}

val += fmt.Sprintf("\n%s", peekMatches[2])
// NOTE: Return if not a python-ini multi-line value.
currentIdentSize := len(peekMatches[1])
if currentIdentSize <= 0 {
return val, nil
}
}

return line, nil
// NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer.
_, err := p.readUntil('\n')
if err != nil {
return "", err
}

val += fmt.Sprintf("\n%s", peekMatches[2])
}
}

// parse parses data through an io.Reader.
Expand Down