From e6f5736fdcc0288463f8cdb92a566e15a2372433 Mon Sep 17 00:00:00 2001 From: hanjinpeng Date: Thu, 17 Sep 2026 02:31:13 -0400 Subject: [PATCH] utf7.c: decode "+-" as a plus sign RFC 2152 specifies that "+-" encodes a plus sign. The decoder saw no Base64 character after the `+', then swallowed the `-' as the optional end of a shifted sequence, so every literal plus sign was silently lost: $ echo '1 +- 1 = 2' | recode utf-7..utf-8 1 1 = 2 Output the plus sign. Also encode a plus sign as "+-", as recommended by RFC 2152, rather than as the longer, still valid, "+ACs-". Add a test. --- src/utf7.c | 16 ++++++++++++++++ tests/t40_utf7.py | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/utf7.c b/src/utf7.c index 745d288..65bec3b 100644 --- a/src/utf7.c +++ b/src/utf7.c @@ -85,6 +85,15 @@ transform_utf16_utf7 (RECODE_SUBTASK subtask) if (!recode_get_ucs2 (&value, subtask)) SUBTASK_RETURN (subtask); } + else if (value == '+') + { + /* A plus sign is simply written as "+-". */ + + recode_put_byte ('+', subtask); + recode_put_byte ('-', subtask); + if (!recode_get_ucs2 (&value, subtask)) + SUBTASK_RETURN (subtask); + } else { /* Copy a string of non-direct characters. */ @@ -162,6 +171,13 @@ transform_utf7_utf16 (RECODE_SUBTASK subtask) if (character == '+') { character = recode_get_byte (subtask); + if (character == '-') + { + /* "+-" is the encoding of a plus sign itself. */ + recode_put_ucs2 ('+', subtask); + character = recode_get_byte (subtask); + continue; + } while (IS_BASE64 (character)) { /* Process first byte of first quadruplet. */ diff --git a/tests/t40_utf7.py b/tests/t40_utf7.py index 329b617..ee091d0 100644 --- a/tests/t40_utf7.py +++ b/tests/t40_utf7.py @@ -8,6 +8,7 @@ 'Hi Mom +Jjo!\n', '+ZeVnLIqe\n', 'Item 3 is +AKM-1.\n', + '1 +- 1 = 2\n', ] outputs = [ @@ -17,7 +18,9 @@ '0x263A, 0x0021, 0x000A\n'), '0xFEFF, 0x65E5, 0x672C, 0x8A9E, 0x000A\n', ('0xFEFF, 0x0049, 0x0074, 0x0065, 0x006D, 0x0020, 0x0033, 0x0020,\n' - '0x0069, 0x0073, 0x0020, 0x00A3, 0x0031, 0x002E, 0x000A\n')] + '0x0069, 0x0073, 0x0020, 0x00A3, 0x0031, 0x002E, 0x000A\n'), + ('0xFEFF, 0x0031, 0x0020, 0x002B, 0x0020, 0x0031, 0x0020, 0x003D,\n' + '0x0020, 0x0032, 0x000A\n')] class Test: