From 1b5fda5df6267155e65972f08d6654429b104239 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Mon, 24 Nov 2025 15:33:34 -0300 Subject: [PATCH 1/3] Fix parsing of double-quoted values with backslash continuations When UnescapeValueDoubleQuotes is enabled, properly handle multiline values where backslash continuations appear after quotes within the value. Previously caused "key-value delimiter not found" errors. --- ini_test.go | 15 +++++++++++++++ parser.go | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/ini_test.go b/ini_test.go index 306a2a8..b5f2afd 100644 --- a/ini_test.go +++ b/ini_test.go @@ -567,6 +567,21 @@ key = test value more text assert.Equal(t, `test value more text`, f.Section("").Key("key").String()) }) + t.Run("unescape double quotes with backslash continuation", func(t *testing.T) { + f, err := LoadSources(LoadOptions{ + UnescapeValueDoubleQuotes: true, + }, []byte(`hello = "!f() { \ + echo "hello world"; \ +};f"`)) + require.NoError(t, err) + require.NotNil(t, f) + + expected := `!f() { \ + echo "hello world"; \ +};f` + assert.Equal(t, expected, f.Section("").Key("hello").String()) + }) + t.Run("can parse small python-compatible INI files", func(t *testing.T) { f, err := LoadSources(LoadOptions{ AllowPythonMultilineValues: true, diff --git a/parser.go b/parser.go index 44fc526..909d633 100644 --- a/parser.go +++ b/parser.go @@ -181,6 +181,13 @@ func (p *parser) readMultilines(line, val, valQuote string) (string, error) { pos := strings.LastIndex(next, valQuote) if pos > -1 { + // Check if the line ends with backslash continuation after the quote + restOfLine := strings.TrimRight(next[pos+len(valQuote):], "\r\n") + if !p.options.IgnoreContinuation && strings.HasSuffix(strings.TrimSpace(restOfLine), "\\") { + val += next + continue + } + val += next[:pos] comment, has := cleanComment([]byte(next[pos:])) From 2d47b41a1d825ecf954f109ac2b0e82179c3ad09 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Mon, 1 Dec 2025 18:47:00 -0300 Subject: [PATCH 2/3] Add inverse test case with IgnoreContinuation --- ini_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ini_test.go b/ini_test.go index b5f2afd..aaad1a0 100644 --- a/ini_test.go +++ b/ini_test.go @@ -580,6 +580,16 @@ key = test value more text echo "hello world"; \ };f` assert.Equal(t, expected, f.Section("").Key("hello").String()) + + t.Run("inverse case", func(t *testing.T) { + _, err := LoadSources(LoadOptions{ + UnescapeValueDoubleQuotes: true, + IgnoreContinuation: true, + }, []byte(`hello = "!f() { \ + echo "hello world"; \ +};f"`)) + require.Error(t, err) + }) }) t.Run("can parse small python-compatible INI files", func(t *testing.T) { From c5638decd82ea58921f24662119ab9c6cc5e8f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=B4=8A=E1=B4=8F=E1=B4=87=20=E1=B4=84=CA=9C=E1=B4=87?= =?UTF-8?q?=C9=B4?= Date: Sat, 10 Jan 2026 12:52:57 -0500 Subject: [PATCH 3/3] Update parser.go --- parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.go b/parser.go index 909d633..afed76f 100644 --- a/parser.go +++ b/parser.go @@ -183,7 +183,7 @@ func (p *parser) readMultilines(line, val, valQuote string) (string, error) { if pos > -1 { // Check if the line ends with backslash continuation after the quote restOfLine := strings.TrimRight(next[pos+len(valQuote):], "\r\n") - if !p.options.IgnoreContinuation && strings.HasSuffix(strings.TrimSpace(restOfLine), "\\") { + if !p.options.IgnoreContinuation && strings.HasSuffix(strings.TrimSpace(restOfLine), `\`) { val += next continue }