summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-24 21:05:45 +0200
committerDanilo M. <danix@danix.xyz>2026-08-24 21:05:45 +0200
commit9b8a6da4ff39428ce22dc23e16fc48cc062bc01f (patch)
tree405b50f818b920c86d7ef0ca470b0e545be218b2
parent882bb1b36fd777ec5fd5f331f2d589d48b5af5c1 (diff)
downloadqtmaildir-9b8a6da4ff39428ce22dc23e16fc48cc062bc01f.tar.gz
qtmaildir-9b8a6da4ff39428ce22dc23e16fc48cc062bc01f.zip
fix(signatures): match the guard against on-disk text, newline and all
The guard compared the buffer block, with trailing blank lines trimmed, against knownSignatures() values returned verbatim by text(), which carry the trailing newline every editor writes. The two never compared equal, so a signature read back from disk was always treated as unknown: switching appended a second signature instead of replacing, and None removed nothing. Normalise each known entry the same way the block scan does, once in replace(), rather than per comparison. Part of item 152.
-rw-r--r--src/signatures.cpp25
-rw-r--r--tests/test_signatures.cpp17
2 files changed, 41 insertions, 1 deletions
diff --git a/src/signatures.cpp b/src/signatures.cpp
index eb43048..ef631d6 100644
--- a/src/signatures.cpp
+++ b/src/signatures.cpp
@@ -69,6 +69,20 @@ bool isQuoted(const QString &line)
return line.startsWith(QLatin1Char('>'));
}
+/// \p text with trailing blank lines removed, the same normalisation the block
+/// scan below applies. text() returns file content verbatim, so a signature
+/// file ends with the newline every editor writes; without this the match
+/// compares a block with no trailing newline against a known entry that has
+/// one, and the guard silently fails, appending a second signature instead of
+/// replacing the first.
+QString stripTrailingBlankLines(const QString &text)
+{
+ QStringList lines = text.split(QLatin1Char('\n'));
+ while (!lines.isEmpty() && lines.last().trimmed().isEmpty())
+ lines.removeLast();
+ return lines.join(QLatin1Char('\n'));
+}
+
/// The index of the first line of the quote, or -1 when the buffer has none.
///
/// The attribution line ("On Mon, someone wrote:") is deliberately NOT
@@ -164,9 +178,18 @@ QStringList withoutSignature(const QStringList &lines, int delimiter)
QString replace(const QString &buffer, const QString &signature,
const QStringList &known, Position position)
{
+ // Normalise known to the same footing the block scan uses, once here rather
+ // than per comparison. knownSignatures() passes text() verbatim, trailing
+ // newline and all, and the match must be newline-insensitive or the guard
+ // treats every on-disk signature as unknown.
+ QStringList normalized;
+ normalized.reserve(known.size());
+ for (const QString &entry : known)
+ normalized.append(stripTrailingBlankLines(entry));
+
QStringList lines = buffer.split(QLatin1Char('\n'));
- const int existing = existingSignature(lines, known);
+ const int existing = existingSignature(lines, normalized);
if (existing >= 0)
lines = withoutSignature(lines, existing);
diff --git a/tests/test_signatures.cpp b/tests/test_signatures.cpp
index 31f9eb4..47b404b 100644
--- a/tests/test_signatures.cpp
+++ b/tests/test_signatures.cpp
@@ -40,6 +40,7 @@ private slots:
void selectingNoneRemovesAKnownSignature();
void aBlockMatchingNoKnownSignatureIsNotRemoved();
void aDelimiterInsideTheQuoteIsNotTheSignature();
+ void aSignatureReadBackFromDiskIsReplaced();
private:
/// Writes \p files as name -> content into a fresh temporary directory.
@@ -273,5 +274,21 @@ void TestSignatures::aDelimiterInsideTheQuoteIsNotTheSignature()
QVERIFY(result.endsWith(QStringLiteral("-- \nJane Doe")));
}
+void TestSignatures::aSignatureReadBackFromDiskIsReplaced()
+{
+ // known here is what knownSignatures() produces: text() verbatim, carrying
+ // the trailing newline every editor writes into a file. The block scan
+ // treats a trailing blank line as separation rather than text, so a naive
+ // match compares "Jane Doe" against "Jane Doe\n" and silently fails, and
+ // switching then APPENDS a second signature instead of replacing the first.
+ const QStringList known = { QStringLiteral("Jane Doe\n") };
+ const QString buffer = QStringLiteral("Hello.\n\n-- \nJane Doe\n");
+
+ const QString result = Signatures::replace(
+ buffer, QStringLiteral("Brief"), known, Signatures::Position::End);
+
+ QCOMPARE(result, QStringLiteral("Hello.\n\n-- \nBrief"));
+}
+
QTEST_MAIN(TestSignatures)
#include "test_signatures.moc"