From e30ca744b704040427a1f32fe82e6815b43cf2c1 Mon Sep 17 00:00:00 2001 From: Jorge Melegati Date: Wed, 16 Oct 2024 21:18:00 +0200 Subject: [PATCH 1/3] Handling real multi line entries. --- .../dotenv/internal/DotenvParser.java | 77 ++++++++++++------- src/test/java/tests/BasicTests.java | 5 +- src/test/java/tests/DotenvTests.java | 1 - src/test/resources/.env | 14 +++- src/test/resources/env | 12 ++- 5 files changed, 77 insertions(+), 32 deletions(-) diff --git a/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java b/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java index d1dc881..5c7be91 100644 --- a/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java +++ b/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java @@ -60,33 +60,41 @@ public DotenvParser(final DotenvReader reader, final boolean throwIfMissing, fin */ public List parse() throws DotenvException { final var lines = lines(); - final var entries = new ArrayList(lines.size()); + final var entries = new ArrayList(); + + var currentEntry = ""; for (final var line : lines) { - addNewEntry(entries, line.trim()); - } + if (currentEntry == "" && (isWhiteSpace.test(line) || isComment.test(line) || isBlank(line))) + continue; - return entries; - } + currentEntry += line; - private void addNewEntry(final List entries, final String line) { - if (isWhiteSpace.test(line) || isComment.test(line) || isBlank(line)) - return; + final var entry = parseLine.apply(currentEntry); + if (entry == null) { + if (throwIfMalformed) + throw new DotenvException("Malformed entry " + currentEntry); + currentEntry = ""; + continue; + } - final var entry = parseLine.apply(line); - if (entry == null) { - if (throwIfMalformed) - throw new DotenvException("Malformed entry " + line); - return; + var value = entry.getValue(); + if (QuotedStringValidator.startsWithQuote(value) && !QuotedStringValidator.endsWithQuote(value)) { + currentEntry += "\n"; + continue; + } + if (!QuotedStringValidator.isValid(entry.getValue())) { + if (throwIfMalformed) + throw new DotenvException("Malformed entry, unmatched quotes " + line); + currentEntry = ""; + continue; + } + final var key = entry.getKey(); + value = QuotedStringValidator.stripQuotes(entry.getValue()); + entries.add(new DotenvEntry(key, value)); + currentEntry = ""; } - if (!QuotedStringValidator.isValid(entry.getValue())) { - if (throwIfMalformed) - throw new DotenvException("Malformed entry, unmatched quotes " + line); - return; - } - final var key = entry.getKey(); - final var value = QuotedStringValidator.stripQuotes(entry.getValue()); - entries.add(new DotenvEntry(key, value)); + return entries; } private List lines() throws DotenvException { @@ -123,14 +131,17 @@ private static boolean isBlank(String s) { private static class QuotedStringValidator { private static boolean isValid(String input) { final var s = input.trim(); - if (!s.startsWith("\"") && !s.endsWith("\"")) { - // not quoted, its valid + if (isNotQuoted(s)) { return true; } - if (input.length() == 1 || !(s.startsWith("\"") && s.endsWith("\""))) { - // doesn't start and end with quote + if (doesNotStartAndEndWithQuote(s)) { return false; } + + return !hasUnescapedQuote(s); // No unescaped quotes found + } + private static boolean hasUnescapedQuote(final String s) { + boolean hasUnescapedQuote = false; // remove start end quote var content = s.substring(1, s.length() - 1); var quotePattern = Pattern.compile("\""); @@ -141,10 +152,22 @@ private static boolean isValid(String input) { int quoteIndex = matcher.start(); // Check if the quote is escaped if (quoteIndex == 0 || content.charAt(quoteIndex - 1) != '\\') { - return false; // unescaped quote found + hasUnescapedQuote = true; // unescaped quote found } } - return true; // No unescaped quotes found + return hasUnescapedQuote; + } + private static boolean doesNotStartAndEndWithQuote(final String s) { + return s.length() == 1 || !(startsWithQuote(s) && endsWithQuote(s)); + } + private static boolean endsWithQuote(final String s) { + return s.endsWith("\""); + } + private static boolean startsWithQuote(final String s) { + return s.startsWith("\""); + } + private static boolean isNotQuoted(final String s) { + return !startsWithQuote(s) && !endsWithQuote(s); } private static String stripQuotes(String input) { var tr = input.trim(); diff --git a/src/test/java/tests/BasicTests.java b/src/test/java/tests/BasicTests.java index d0284ed..4d09703 100644 --- a/src/test/java/tests/BasicTests.java +++ b/src/test/java/tests/BasicTests.java @@ -14,9 +14,12 @@ class BasicTests { put("MY_TEST_EV1", "my test ev 1"); put("MY_TEST_EV2", "my test ev 2"); put("WITHOUT_VALUE", ""); - put("MULTI_LINE", "hello\\nworld"); + put("MULTI_LINE", "hello\nworld\nmulti"); + put("TWO_LINE", "hello\nworld"); put("TRAILING_COMMENT", "value"); put("QUOTED_VALUE", "iH4>hb_d0#_GN8d]6"); + put("MY_TEST_EV4", "my test ev 4"); + put("MULTI_LINE_WITH_SHARP", "hello\n#world"); }}; @Test diff --git a/src/test/java/tests/DotenvTests.java b/src/test/java/tests/DotenvTests.java index 6f89936..a8e5b30 100644 --- a/src/test/java/tests/DotenvTests.java +++ b/src/test/java/tests/DotenvTests.java @@ -110,7 +110,6 @@ void malformedWithUncloseQuote() { .load(); assertNull(dotenv.get("FOO")); - assertEquals(dotenv.get("BAR"), "bar"); assertNull(dotenv.get("BAZ"), "baz"); } } diff --git a/src/test/resources/.env b/src/test/resources/.env index d0ccf01..dec25bf 100644 --- a/src/test/resources/.env +++ b/src/test/resources/.env @@ -2,12 +2,22 @@ MY_TEST_EV1=my test ev 1 MY_TEST_EV2=my test ev 2 WITHOUT_VALUE= -MULTI_LINE=hello\nworld +MULTI_LINE="hello +world +multi" +TWO_LINE="hello +world" TRAILING_COMMENT=value # comment QUOTED_VALUE="iH4>hb_d0#_GN8d]6" # comment "test" -## Malformed EV! +## Malformed EVs! MY_TEST_EV3 +MISSING_START_QUOTE=teste" + +## Good EV after malformed +MY_TEST_EV4="my test ev 4" +MULTI_LINE_WITH_SHARP="hello +#world" QUOTED_EV1="jdbc:hive2://[domain]:10000/default;principal=hive/_HOST@[REALM]" diff --git a/src/test/resources/env b/src/test/resources/env index d01afb9..a3ed802 100644 --- a/src/test/resources/env +++ b/src/test/resources/env @@ -2,9 +2,19 @@ MY_TEST_EV1=my test ev 1 MY_TEST_EV2=my test ev 2 WITHOUT_VALUE= -MULTI_LINE=hello\nworld +MULTI_LINE="hello +world +multi" +TWO_LINE="hello +world" TRAILING_COMMENT=value # comment QUOTED_VALUE="iH4>hb_d0#_GN8d]6" # comment "test" ## Malformed EV! MY_TEST_EV3 +MISSING_START_QUOTE=teste" + +## Good EV after malformed +MY_TEST_EV4="my test ev 4" +MULTI_LINE_WITH_SHARP="hello +#world" From ca5db740e4bfd18f6a79108e3f97999ddfe3e738 Mon Sep 17 00:00:00 2001 From: Jorge Melegati Date: Fri, 25 Oct 2024 20:32:01 +0200 Subject: [PATCH 2/3] Using equals to compare objects. --- .../java/io/github/cdimascio/dotenv/internal/DotenvParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java b/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java index 5c7be91..c244a7b 100644 --- a/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java +++ b/src/main/java/io/github/cdimascio/dotenv/internal/DotenvParser.java @@ -64,7 +64,7 @@ public List parse() throws DotenvException { var currentEntry = ""; for (final var line : lines) { - if (currentEntry == "" && (isWhiteSpace.test(line) || isComment.test(line) || isBlank(line))) + if (currentEntry.equals("") && (isWhiteSpace.test(line) || isComment.test(line) || isBlank(line))) continue; currentEntry += line; From b3bb3d10de2055b46bab785913dd03f11b7d3f60 Mon Sep 17 00:00:00 2001 From: carmine Date: Sun, 15 Dec 2024 18:11:45 -0500 Subject: [PATCH 3/3] adds # line to multi-line ev test --- src/test/java/tests/BasicTests.java | 2 +- src/test/resources/.env | 1 + src/test/resources/env | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/tests/BasicTests.java b/src/test/java/tests/BasicTests.java index 4d09703..0c04cdd 100644 --- a/src/test/java/tests/BasicTests.java +++ b/src/test/java/tests/BasicTests.java @@ -14,7 +14,7 @@ class BasicTests { put("MY_TEST_EV1", "my test ev 1"); put("MY_TEST_EV2", "my test ev 2"); put("WITHOUT_VALUE", ""); - put("MULTI_LINE", "hello\nworld\nmulti"); + put("MULTI_LINE", "hello\nworld\n# not a comment\nmulti"); put("TWO_LINE", "hello\nworld"); put("TRAILING_COMMENT", "value"); put("QUOTED_VALUE", "iH4>hb_d0#_GN8d]6"); diff --git a/src/test/resources/.env b/src/test/resources/.env index dec25bf..1150e18 100644 --- a/src/test/resources/.env +++ b/src/test/resources/.env @@ -4,6 +4,7 @@ MY_TEST_EV2=my test ev 2 WITHOUT_VALUE= MULTI_LINE="hello world +# not a comment multi" TWO_LINE="hello world" diff --git a/src/test/resources/env b/src/test/resources/env index a3ed802..4753556 100644 --- a/src/test/resources/env +++ b/src/test/resources/env @@ -4,6 +4,7 @@ MY_TEST_EV2=my test ev 2 WITHOUT_VALUE= MULTI_LINE="hello world +# not a comment multi" TWO_LINE="hello world"