format-patch: do not wrap rfc2047 encoded headers too late

Encoded characters add more than one character at once to an encoded header. Include all characters that are about to be added in the length calculation for wrapping. Additionally, RFC 2047 imposes a maximum line length of 76 characters if that line contains an rfc2047 encoded word. Signed-off-by: Jan H. Schönherr <schnhrr@cs.tu-berlin.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jan H. Schönherr committed Oct 18, 2012 at 16:43 UTC 94f6cdf693c10eaec5a7fcc4f0b5cb1e0ea80f1b
2 files changed +51 -33
pretty.c
+16 -10
@@ -263,6 +263,9 @@ static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
263
264 static int is_rfc2047_special(char ch)
265 {
266 + if (ch == ' ' || ch == '\n')
267 + return 1;
268 +
269 return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
270 }
271
@@ -270,6 +273,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, int len,
273 const char *encoding)
274 {
275 static const int max_length = 78; /* per rfc2822 */
276 + static const int max_encoded_length = 76; /* per rfc2047 */
277 int i;
278 int line_len;
279
@@ -295,23 +299,25 @@ needquote:
299 line_len += strlen(encoding) + 5; /* 5 for =??q? */
300 for (i = 0; i < len; i++) {
301 unsigned ch = line[i] & 0xFF;
302 + int is_special = is_rfc2047_special(ch);
303 +
304 + /*
305 + * According to RFC 2047, we could encode the special character
306 + * ' ' (space) with '_' (underscore) for readability. But many
307 + * programs do not understand this and just leave the
308 + * underscore in place. Thus, we do nothing special here, which
309 + * causes ' ' to be encoded as '=20', avoiding this problem.
310 + */
311
299 - if (line_len >= max_length - 2) {
312 + if (line_len + 2 + (is_special ? 3 : 1) > max_encoded_length) {
313 strbuf_addf(sb, "?=\n =?%s?q?", encoding);
314 line_len = strlen(encoding) + 5 + 1; /* =??q? plus SP */
315 }
316
304 - /*
305 - * We encode ' ' using '=20' even though rfc2047
306 - * allows using '_' for readability. Unfortunately,
307 - * many programs do not understand this and just
308 - * leave the underscore in place.
309 - */
310 - if (is_rfc2047_special(ch) || ch == ' ' || ch == '\n') {
317 + if (is_special) {
318 strbuf_addf(sb, "=%02X", ch);
319 line_len += 3;
313 - }
314 - else {
320 + } else {
321 strbuf_addch(sb, ch);
322 line_len++;
323 }
t/t4014-format-patch.sh
+35 -23
@@ -772,30 +772,31 @@ M8="föö bar "
772 M64=$M8$M8$M8$M8$M8$M8$M8$M8
773 M512=$M64$M64$M64$M64$M64$M64$M64$M64
774 cat >expect <<'EOF'
775 -Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
776 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
777 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
778 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
779 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
780 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
781 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
782 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
783 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
784 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
785 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
786 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
787 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
788 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
789 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
790 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
791 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
792 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
793 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
794 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
795 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
796 - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
775 +Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
776 + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
777 + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
778 + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
779 + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
780 + =?UTF-8?q?=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
781 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3?=
782 + =?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
783 + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
784 + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
785 + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
786 + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
787 + =?UTF-8?q?=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
788 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3?=
789 + =?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
790 + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
791 + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
792 + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
793 + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
794 + =?UTF-8?q?=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
795 + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3?=
796 + =?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
797 + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
798 EOF
798 -test_expect_success 'format-patch wraps extremely long headers (rfc2047)' '
799 +test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '
800 rm -rf patches/ &&
801 echo content >>file &&
802 git add file &&
@@ -861,6 +862,17 @@ test_expect_success 'format-patch wraps extremely long from-header (rfc822)' '
862 check_author "Foo.Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar"
863 '
864
865 +cat >expect <<'EOF'
866 +From: =?UTF-8?q?Fo=C3=B6=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo?=
867 + =?UTF-8?q?=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20?=
868 + =?UTF-8?q?Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar?=
869 + =?UTF-8?q?=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20?=
870 + =?UTF-8?q?Foo=20Bar=20Foo=20Bar?= <author@example.com>
871 +EOF
872 +test_expect_success 'format-patch wraps extremely long from-header (rfc2047)' '
873 + check_author "Foö Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar"
874 +'
875 +
876 cat >expect <<'EOF'
877 Subject: header with . in it
878 EOF