{"thread":{"id":"44114","subject":"[PATCH v2 0/4] handle multiline in-body headers","startedAt":"2016-09-19T21:09:06Z","lastAt":"2016-09-21T17:25:22Z","messageCount":12,"participants":["Jonathan Tan","Junio C Hamano","Jeff King"],"isPatch":true,"patchVersion":2,"patchTotal":4},"messages":[{"id":"302188","messageId":"cover.1474319086.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":null,"subject":"[PATCH v2 0/4] handle multiline in-body headers","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-19T21:08:49Z","receivedAt":"2016-09-19T21:09:06Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"This is an iteration of the patch set after the discussion in\n <cover.1474047135.git.jonathantanmy@google.com>.\n\nChanges:\no largely rewritten to follow Junio's suggested design (refactor of\n  check_header to separate out \">From\" and \"[PATCH]\", and an\n  is_inbody_header function performing an airtight check on whether a\n  line is an in-body header)\no simpler try_convert_to_utf8 API\no one file of the expected output of t/t5100/*0015 is modified (instead\n  of the input)\no t/t5100/*0018--no-inbody-headers test files added\no example in commit message improved following Peff's suggestion\n\nJonathan Tan (4):\n  mailinfo: separate in-body header processing\n  mailinfo: refactor to support utf8 decode attempts\n  mailinfo: make is_scissors_line take plain char *\n  mailinfo: handle in-body header continuations\n\n mailinfo.c                           | 164 ++++++++++++++++++++++++++---------\n mailinfo.h                           |   1 +\n t/t4150-am.sh                        |  23 +++++\n t/t5100-mailinfo.sh                  |   2 +-\n t/t5100/info0018                     |   5 ++\n t/t5100/info0018--no-inbody-headers  |   5 ++\n t/t5100/msg0015                      |   2 -\n t/t5100/msg0018                      |   2 +\n t/t5100/msg0018--no-inbody-headers   |   8 ++\n t/t5100/patch0018                    |   6 ++\n t/t5100/patch0018--no-inbody-headers |   6 ++\n t/t5100/sample.mbox                  |  19 ++++\n 12 files changed, 198 insertions(+), 45 deletions(-)\n create mode 100644 t/t5100/info0018\n create mode 100644 t/t5100/info0018--no-inbody-headers\n create mode 100644 t/t5100/msg0018\n create mode 100644 t/t5100/msg0018--no-inbody-headers\n create mode 100644 t/t5100/patch0018\n create mode 100644 t/t5100/patch0018--no-inbody-headers\n\n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302189","messageId":"12ea344c46ed17ed90dd92bebc45be761a51ef80.1474319086.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":"cover.1474319086.git.jonathantanmy@google.com","subject":"[PATCH v2 1/4] mailinfo: separate in-body header processing","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-19T21:08:50Z","receivedAt":"2016-09-19T21:09:11Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"The check_header function contains logic specific to in-body headers,\nalthough it is invoked during both the processing of actual headers and\nin-body headers. Separate out the in-body header part into its own\nfunction.\n\nSigned-off-by: Jonathan Tan <jonathantanmy@google.com>\n---\n mailinfo.c | 33 +++++++++++++++++----------------\n 1 file changed, 17 insertions(+), 16 deletions(-)\n\ndiff --git a/mailinfo.c b/mailinfo.c\nindex e19abe3..0c4738a 100644\n--- a/mailinfo.c\n+++ b/mailinfo.c\n@@ -495,21 +495,6 @@ static int check_header(struct mailinfo *mi,\n \t\tgoto check_header_out;\n \t}\n \n-\t/* for inbody stuff */\n-\tif (starts_with(line->buf, \">From\") && isspace(line->buf[5])) {\n-\t\tret = is_format_patch_separator(line->buf + 1, line->len - 1);\n-\t\tgoto check_header_out;\n-\t}\n-\tif (starts_with(line->buf, \"[PATCH]\") && isspace(line->buf[7])) {\n-\t\tfor (i = 0; header[i]; i++) {\n-\t\t\tif (!strcmp(\"Subject\", header[i])) {\n-\t\t\t\thandle_header(&hdr_data[i], line);\n-\t\t\t\tret = 1;\n-\t\t\t\tgoto check_header_out;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n check_header_out:\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -623,6 +608,22 @@ static int is_scissors_line(const struct strbuf *line)\n \t\tgap * 2 < perforation);\n }\n \n+static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)\n+{\n+\tif (starts_with(line->buf, \">From\") && isspace(line->buf[5]))\n+\t\treturn is_format_patch_separator(line->buf + 1, line->len - 1);\n+\tif (starts_with(line->buf, \"[PATCH]\") && isspace(line->buf[7])) {\n+\t\tint i;\n+\t\tfor (i = 0; header[i]; i++)\n+\t\t\tif (!strcmp(\"Subject\", header[i])) {\n+\t\t\t\thandle_header(&mi->s_hdr_data[i], line);\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\t\treturn 0;\n+\t}\n+\treturn check_header(mi, line, mi->s_hdr_data, 0);\n+}\n+\n static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)\n {\n \tassert(!mi->filter_stage);\n@@ -633,7 +634,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)\n \t}\n \n \tif (mi->use_inbody_headers && mi->header_stage) {\n-\t\tmi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);\n+\t\tmi->header_stage = check_inbody_header(mi, line);\n \t\tif (mi->header_stage)\n \t\t\treturn 0;\n \t} else\n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302190","messageId":"fe75e92ee7108ef410cb0edad81d1b464b7694eb.1474319086.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":"cover.1474319086.git.jonathantanmy@google.com","subject":"[PATCH v2 3/4] mailinfo: make is_scissors_line take plain char *","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-19T21:08:52Z","receivedAt":"2016-09-19T21:09:15Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"The is_scissors_line takes a struct strbuf * when a char * would\nsuffice. Make it take char *.\n\nSigned-off-by: Jonathan Tan <jonathantanmy@google.com>\n---\n mailinfo.c | 35 ++++++++++++++++++-----------------\n 1 file changed, 18 insertions(+), 17 deletions(-)\n\ndiff --git a/mailinfo.c b/mailinfo.c\nindex aadad09..1f487df 100644\n--- a/mailinfo.c\n+++ b/mailinfo.c\n@@ -581,37 +581,35 @@ static inline int patchbreak(const struct strbuf *line)\n \treturn 0;\n }\n \n-static int is_scissors_line(const struct strbuf *line)\n+static int is_scissors_line(const char *line)\n {\n-\tsize_t i, len = line->len;\n+\tconst char *c;\n \tint scissors = 0, gap = 0;\n-\tint first_nonblank = -1;\n-\tint last_nonblank = 0, visible, perforation = 0, in_perforation = 0;\n-\tconst char *buf = line->buf;\n+\tconst char *first_nonblank = NULL, *last_nonblank = NULL;\n+\tint visible, perforation = 0, in_perforation = 0;\n \n-\tfor (i = 0; i < len; i++) {\n-\t\tif (isspace(buf[i])) {\n+\tfor (c = line; *c; c++) {\n+\t\tif (isspace(*c)) {\n \t\t\tif (in_perforation) {\n \t\t\t\tperforation++;\n \t\t\t\tgap++;\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n-\t\tlast_nonblank = i;\n-\t\tif (first_nonblank < 0)\n-\t\t\tfirst_nonblank = i;\n-\t\tif (buf[i] == '-') {\n+\t\tlast_nonblank = c;\n+\t\tif (first_nonblank == NULL)\n+\t\t\tfirst_nonblank = c;\n+\t\tif (*c == '-') {\n \t\t\tin_perforation = 1;\n \t\t\tperforation++;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (i + 1 < len &&\n-\t\t    (!memcmp(buf + i, \">8\", 2) || !memcmp(buf + i, \"8<\", 2) ||\n-\t\t     !memcmp(buf + i, \">%\", 2) || !memcmp(buf + i, \"%<\", 2))) {\n+\t\tif ((!memcmp(c, \">8\", 2) || !memcmp(c, \"8<\", 2) ||\n+\t\t     !memcmp(c, \">%\", 2) || !memcmp(c, \"%<\", 2))) {\n \t\t\tin_perforation = 1;\n \t\t\tperforation += 2;\n \t\t\tscissors += 2;\n-\t\t\ti++;\n+\t\t\tc++;\n \t\t\tcontinue;\n \t\t}\n \t\tin_perforation = 0;\n@@ -626,7 +624,10 @@ static int is_scissors_line(const struct strbuf *line)\n \t * than half of the perforation.\n \t */\n \n-\tvisible = last_nonblank - first_nonblank + 1;\n+\tif (first_nonblank && last_nonblank)\n+\t\tvisible = last_nonblank - first_nonblank + 1;\n+\telse\n+\t\tvisible = 0;\n \treturn (scissors && 8 <= visible &&\n \t\tvisible < perforation * 3 &&\n \t\tgap * 2 < perforation);\n@@ -671,7 +672,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)\n \tif (convert_to_utf8(mi, line, mi->charset.buf))\n \t\treturn 0; /* mi->input_error already set */\n \n-\tif (mi->use_scissors && is_scissors_line(line)) {\n+\tif (mi->use_scissors && is_scissors_line(line->buf)) {\n \t\tint i;\n \n \t\tstrbuf_setlen(&mi->log_message, 0);\n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302191","messageId":"e882f6395d1079037843e6e5b632c0010e1f4cdf.1474319086.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":"cover.1474319086.git.jonathantanmy@google.com","subject":"[PATCH v2 2/4] mailinfo: refactor to support utf8 decode attempts","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-19T21:08:51Z","receivedAt":"2016-09-19T21:09:18Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"mailinfo.c currently has a convert_to_utf8 function that overrides its\nargument and prints an error message when the decoding fails. Refactor\nit, creating another function that does not override anything and does\nnot print an error message when the decoding fails. This is to be used\nby a subsequent patch.\n\nSigned-off-by: Jonathan Tan <jonathantanmy@google.com>\n---\n mailinfo.c | 42 +++++++++++++++++++++++++++++++++---------\n 1 file changed, 33 insertions(+), 9 deletions(-)\n\ndiff --git a/mailinfo.c b/mailinfo.c\nindex 0c4738a..aadad09 100644\n--- a/mailinfo.c\n+++ b/mailinfo.c\n@@ -340,23 +340,47 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)\n \treturn out;\n }\n \n+/*\n+ * Attempts to convert line into UTF-8.\n+ *\n+ * This differs from convert_to_utf8 in that no inputs are overridden, and that\n+ * conversion non-success is not considered an error case - mi->input_error is\n+ * not set, and no error message is printed.\n+ *\n+ * If the conversion is unnecessary, returns 1 and stores NULL in result.\n+ *\n+ * If the conversion is successful, returns 1 and stores the converted string\n+ * in result.\n+ *\n+ * If the conversion is unsuccessful, returns 0 and stores NULL in result.\n+ */\n+static int try_convert_to_utf8(const struct mailinfo *mi, const char *line,\n+\t\t\t       const char *charset, char **result)\n+{\n+\tif (!mi->metainfo_charset || !charset || !*charset ||\n+\t    same_encoding(mi->metainfo_charset, charset)) {\n+\t\t*result = NULL;\n+\t\treturn 1;\n+\t}\n+\n+\t*result = reencode_string(line, mi->metainfo_charset, charset);\n+\treturn !!*result;\n+}\n+\n+/*\n+ * Converts line into UTF-8, setting mi->input_error to -1 upon failure.\n+ */\n static int convert_to_utf8(struct mailinfo *mi,\n \t\t\t   struct strbuf *line, const char *charset)\n {\n \tchar *out;\n-\n-\tif (!mi->metainfo_charset || !charset || !*charset)\n-\t\treturn 0;\n-\n-\tif (same_encoding(mi->metainfo_charset, charset))\n-\t\treturn 0;\n-\tout = reencode_string(line->buf, mi->metainfo_charset, charset);\n-\tif (!out) {\n+\tif (!try_convert_to_utf8(mi, line->buf, charset, &out)) {\n \t\tmi->input_error = -1;\n \t\treturn error(\"cannot convert from %s to %s\",\n \t\t\t     charset, mi->metainfo_charset);\n \t}\n-\tstrbuf_attach(line, out, strlen(out), strlen(out));\n+\tif (out)\n+\t\tstrbuf_attach(line, out, strlen(out), strlen(out));\n \treturn 0;\n }\n \n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302192","messageId":"9208b9759e8219c1d28564507fbd5a01a7e91d57.1474319086.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":"cover.1474319086.git.jonathantanmy@google.com","subject":"[PATCH v2 4/4] mailinfo: handle in-body header continuations","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-19T21:08:53Z","receivedAt":"2016-09-19T21:09:22Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"Mailinfo currently handles multi-line headers, but it does not handle\nmulti-line in-body headers. Teach it to handle such headers, for\nexample, for this input:\n\n  From: author <author@example.com>\n  Date: Fri, 9 Jun 2006 00:44:16 -0700\n  Subject: a very long\n   broken line\n\n  Subject: another very long\n   broken line\n\ninterpret the in-body subject to be \"another very long broken line\"\ninstead of \"another very long\".\n\nAn existing test (t/t5100/msg0015) has an indented line immediately\nafter an in-body header - it has been modified to reflect the new\nfunctionality.\n\nSigned-off-by: Jonathan Tan <jonathantanmy@google.com>\n---\n mailinfo.c                           | 56 +++++++++++++++++++++++++++++++++++-\n mailinfo.h                           |  1 +\n t/t4150-am.sh                        | 23 +++++++++++++++\n t/t5100-mailinfo.sh                  |  2 +-\n t/t5100/info0018                     |  5 ++++\n t/t5100/info0018--no-inbody-headers  |  5 ++++\n t/t5100/msg0015                      |  2 --\n t/t5100/msg0018                      |  2 ++\n t/t5100/msg0018--no-inbody-headers   |  8 ++++++\n t/t5100/patch0018                    |  6 ++++\n t/t5100/patch0018--no-inbody-headers |  6 ++++\n t/t5100/sample.mbox                  | 19 ++++++++++++\n 12 files changed, 131 insertions(+), 4 deletions(-)\n create mode 100644 t/t5100/info0018\n create mode 100644 t/t5100/info0018--no-inbody-headers\n create mode 100644 t/t5100/msg0018\n create mode 100644 t/t5100/msg0018--no-inbody-headers\n create mode 100644 t/t5100/patch0018\n create mode 100644 t/t5100/patch0018--no-inbody-headers\n\ndiff --git a/mailinfo.c b/mailinfo.c\nindex 1f487df..aec3c52 100644\n--- a/mailinfo.c\n+++ b/mailinfo.c\n@@ -524,6 +524,21 @@ static int check_header(struct mailinfo *mi,\n \treturn ret;\n }\n \n+/*\n+ * Returns 1 if the given line or any line beginning with the given line is an\n+ * in-body header (that is, check_header will succeed when passed\n+ * mi->s_hdr_data).\n+ */\n+static int is_inbody_header(const struct mailinfo *mi,\n+\t\t\t    const struct strbuf *line)\n+{\n+\tint i;\n+\tfor (i = 0; header[i]; i++)\n+\t\tif (!mi->s_hdr_data[i] && cmp_header(line, header[i]))\n+\t\t\treturn 1;\n+\treturn 0;\n+}\n+\n static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)\n {\n \tstruct strbuf *ret;\n@@ -633,8 +648,39 @@ static int is_scissors_line(const char *line)\n \t\tgap * 2 < perforation);\n }\n \n+static void flush_inbody_header_accum(struct mailinfo *mi)\n+{\n+\tif (!mi->inbody_header_accum.len)\n+\t\treturn;\n+\tassert(check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0));\n+\tstrbuf_reset(&mi->inbody_header_accum);\n+}\n+\n static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)\n {\n+\tif (mi->inbody_header_accum.len &&\n+\t    (line->buf[0] == ' ' || line->buf[0] == '\\t')) {\n+\t\tchar *utf8 = NULL;\n+\t\tint is_scissors = mi->use_scissors && \n+\t\t\t\t  try_convert_to_utf8(mi, line->buf,\n+\t\t\t\t\t\t      mi->charset.buf, &utf8) &&\n+\t\t\t\t  is_scissors_line(utf8 ? utf8 : line->buf);\n+\t\tfree(utf8);\n+\t\tif (is_scissors) {\n+\t\t\t/*\n+\t\t\t * This is a scissors line; do not consider this line\n+\t\t\t * as a header continuation line.\n+\t\t\t */\n+\t\t\tflush_inbody_header_accum(mi);\n+\t\t\treturn 0;\n+\t\t}\n+\t\tstrbuf_strip_suffix(&mi->inbody_header_accum, \"\\n\");\n+\t\tstrbuf_addbuf(&mi->inbody_header_accum, line);\n+\t\treturn 1;\n+\t}\n+\n+\tflush_inbody_header_accum(mi);\n+\n \tif (starts_with(line->buf, \">From\") && isspace(line->buf[5]))\n \t\treturn is_format_patch_separator(line->buf + 1, line->len - 1);\n \tif (starts_with(line->buf, \"[PATCH]\") && isspace(line->buf[7])) {\n@@ -646,7 +692,11 @@ static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)\n \t\t\t}\n \t\treturn 0;\n \t}\n-\treturn check_header(mi, line, mi->s_hdr_data, 0);\n+\tif (is_inbody_header(mi, line)) {\n+\t\tstrbuf_addbuf(&mi->inbody_header_accum, line);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n }\n \n static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)\n@@ -912,6 +962,8 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)\n \t\t\tbreak;\n \t} while (!strbuf_getwholeline(line, mi->input, '\\n'));\n \n+\tflush_inbody_header_accum(mi);\n+\n handle_body_out:\n \tstrbuf_release(&prev);\n }\n@@ -1027,6 +1079,7 @@ void setup_mailinfo(struct mailinfo *mi)\n \tstrbuf_init(&mi->email, 0);\n \tstrbuf_init(&mi->charset, 0);\n \tstrbuf_init(&mi->log_message, 0);\n+\tstrbuf_init(&mi->inbody_header_accum, 0);\n \tmi->header_stage = 1;\n \tmi->use_inbody_headers = 1;\n \tmi->content_top = mi->content;\n@@ -1040,6 +1093,7 @@ void clear_mailinfo(struct mailinfo *mi)\n \tstrbuf_release(&mi->name);\n \tstrbuf_release(&mi->email);\n \tstrbuf_release(&mi->charset);\n+\tstrbuf_release(&mi->inbody_header_accum);\n \tfree(mi->message_id);\n \n \tfor (i = 0; mi->p_hdr_data[i]; i++)\ndiff --git a/mailinfo.h b/mailinfo.h\nindex 93776a7..04a2535 100644\n--- a/mailinfo.h\n+++ b/mailinfo.h\n@@ -27,6 +27,7 @@ struct mailinfo {\n \tint patch_lines;\n \tint filter_stage; /* still reading log or are we copying patch? */\n \tint header_stage; /* still checking in-body headers? */\n+\tstruct strbuf inbody_header_accum;\n \tstruct strbuf **p_hdr_data;\n \tstruct strbuf **s_hdr_data;\n \ndiff --git a/t/t4150-am.sh b/t/t4150-am.sh\nindex 9ce9424..89a5bac 100755\n--- a/t/t4150-am.sh\n+++ b/t/t4150-am.sh\n@@ -977,4 +977,27 @@ test_expect_success 'am --patch-format=mboxrd handles mboxrd' '\n \ttest_cmp msg out\n '\n \n+test_expect_success 'am works with multi-line in-body headers' '\n+\tFORTY=\"String that has a length of more than forty characters\" &&\n+\tLONG=\"$FORTY $FORTY\" &&\n+\trm -fr .git/rebase-apply &&\n+\tgit checkout -f first &&\n+\techo one >> file &&\n+\tgit commit -am \"$LONG\" --author=\"$LONG <long@example.com>\" &&\n+\tgit format-patch --stdout -1 >patch &&\n+\t# bump from, date, and subject down to in-body header\n+\tperl -lpe \"\n+\t\tif (/^From:/) {\n+\t\t\tprint \\\"From: x <x\\@example.com>\\\";\n+\t\t\tprint \\\"Date: Sat, 1 Jan 2000 00:00:00 +0000\\\";\n+\t\t\tprint \\\"Subject: x\\n\\\";\n+\t\t}\n+\t\" patch >msg &&\n+\tgit checkout HEAD^ &&\n+\tgit am msg &&\n+\t# Ensure that the author and full message are present\n+\tgit cat-file commit HEAD | grep \"^author.*long@example.com\" &&\n+\tgit cat-file commit HEAD | grep \"^$LONG\"\n+'\n+\n test_done\ndiff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh\nindex 1a5a546..e173c33 100755\n--- a/t/t5100-mailinfo.sh\n+++ b/t/t5100-mailinfo.sh\n@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \\\n \t'git mailsplit -o. \"$TEST_DIRECTORY\"/t5100/sample.mbox >last &&\n \tlast=$(cat last) &&\n \techo total is $last &&\n-\ttest $(cat last) = 17'\n+\ttest $(cat last) = 18'\n \n check_mailinfo () {\n \tmail=$1 opt=$2\ndiff --git a/t/t5100/info0018 b/t/t5100/info0018\nnew file mode 100644\nindex 0000000..d53e749\n--- /dev/null\n+++ b/t/t5100/info0018\n@@ -0,0 +1,5 @@\n+Author: Another Thor\n+Email: a.thor@example.com\n+Subject: This one contains a tab and a space\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+\ndiff --git a/t/t5100/info0018--no-inbody-headers b/t/t5100/info0018--no-inbody-headers\nnew file mode 100644\nindex 0000000..30b17bd\n--- /dev/null\n+++ b/t/t5100/info0018--no-inbody-headers\n@@ -0,0 +1,5 @@\n+Author: A U Thor\n+Email: a.u.thor@example.com\n+Subject: check multiline inbody headers\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+\ndiff --git a/t/t5100/msg0015 b/t/t5100/msg0015\nindex 4abb3d5..e69de29 100644\n--- a/t/t5100/msg0015\n+++ b/t/t5100/msg0015\n@@ -1,2 +0,0 @@\n-  - a list\n-  - of stuff\ndiff --git a/t/t5100/msg0018 b/t/t5100/msg0018\nnew file mode 100644\nindex 0000000..56de83d\n--- /dev/null\n+++ b/t/t5100/msg0018\n@@ -0,0 +1,2 @@\n+a commit message\n+\ndiff --git a/t/t5100/msg0018--no-inbody-headers b/t/t5100/msg0018--no-inbody-headers\nnew file mode 100644\nindex 0000000..b1e05d3\n--- /dev/null\n+++ b/t/t5100/msg0018--no-inbody-headers\n@@ -0,0 +1,8 @@\n+From: Another Thor\n+ <a.thor@example.com>\n+Subject: This one contains\n+\ta tab\n+ and a space\n+\n+a commit message\n+\ndiff --git a/t/t5100/patch0018 b/t/t5100/patch0018\nnew file mode 100644\nindex 0000000..789df6d\n--- /dev/null\n+++ b/t/t5100/patch0018\n@@ -0,0 +1,6 @@\n+diff --git a/foo b/foo\n+index e69de29..d95f3ad 100644\n+--- a/foo\n++++ b/foo\n+@@ -0,0 +1 @@\n++content\ndiff --git a/t/t5100/patch0018--no-inbody-headers b/t/t5100/patch0018--no-inbody-headers\nnew file mode 100644\nindex 0000000..789df6d\n--- /dev/null\n+++ b/t/t5100/patch0018--no-inbody-headers\n@@ -0,0 +1,6 @@\n+diff --git a/foo b/foo\n+index e69de29..d95f3ad 100644\n+--- a/foo\n++++ b/foo\n+@@ -0,0 +1 @@\n++content\ndiff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox\nindex 8b2ae06..6d4d0e4 100644\n--- a/t/t5100/sample.mbox\n+++ b/t/t5100/sample.mbox\n@@ -699,3 +699,22 @@ index e69de29..d95f3ad 100644\n +++ b/foo\n @@ -0,0 +1 @@\n +New content\n+From nobody Mon Sep 17 00:00:00 2001\n+From: A U Thor <a.u.thor@example.com>\n+Subject: check multiline inbody headers\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+\n+From: Another Thor\n+ <a.thor@example.com>\n+Subject: This one contains\n+\ta tab\n+ and a space\n+\n+a commit message\n+\n+diff --git a/foo b/foo\n+index e69de29..d95f3ad 100644\n+--- a/foo\n++++ b/foo\n+@@ -0,0 +1 @@\n++content\n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302195","messageId":"xmqqr38fd0y1.fsf@gitster.mtv.corp.google.com","threadId":"44114","inReplyTo":"cover.1474319086.git.jonathantanmy@google.com","subject":"Re: [PATCH v2 0/4] handle multiline in-body headers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-09-19T21:39:02Z","receivedAt":"2016-09-19T21:39:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Tan <jonathantanmy@google.com> writes:\n\n> This is an iteration of the patch set after the discussion in\n>  <cover.1474047135.git.jonathantanmy@google.com>.\n>\n> Changes:\n> o largely rewritten to follow Junio's suggested design (refactor of\n>   check_header to separate out \">From\" and \"[PATCH]\", and an\n>   is_inbody_header function performing an airtight check on whether a\n>   line is an in-body header)\n> o simpler try_convert_to_utf8 API\n> o one file of the expected output of t/t5100/*0015 is modified (instead\n>   of the input)\n> o t/t5100/*0018--no-inbody-headers test files added\n> o example in commit message improved following Peff's suggestion\n\nOverall it looks much nicer, but I still do not understand why we\nwant to do try-convert-to-utf8.  The _only_ caller of that helper\nfunction is in 4/4 where it tries to convert the line to see if a\nline is a scissors line.\n\nIt seems to me that the only thing doing so gives us is that you\ncould later enhance the definition of what a scissors-line looks\nlike by adding unicode characters [*1*], but I would strongly\nsuggest not doing that kind of enhancement [*2*].  With the current\ndefinition of what a scissors-line looks like, it can be checked\nbefore you do the UTF-8 conversion, I think, which would mean that\nthe helper is not strictly necessary.\n\nSo...\n\n\n[Footnotes]\n\n*1* E.g. ✂ encoded in non-UTF8 may have to be converted to UTF-8\nfirst before being recognized as such.\n\n*2* Encouraging people to use non-ASCII that older version of Git\ndid not take for structural things like \"what is a scissors line\"\nmerely for the looks is a bad trade-off; patch e-mails that use the\nenhanced definition will break for those with older version of Git,\nand the benefit of \"prettier scissors\" is not really worth it, I\nwould think.\n\n\n\n\n\n"},{"id":"302226","messageId":"cover.1474391594.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":"cover.1474319086.git.jonathantanmy@google.com","subject":"[PATCH v3 0/3] handle multiline in-body headers","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-20T17:17:50Z","receivedAt":"2016-09-20T17:18:05Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"Changes since v2:\no Removed utf8 translation before scissors line check in\n  check_inbody_header (I was thinking of support for encodings like\n  UTF-16, but I guess those don't work with the current reencode_string\n  anyway since it uses strlen internally)\n\nWith the above change, it is actually no longer necessary to make\nis_scissors_line take plain char * (the second patch) - I think that\nthat patch still improves the code, but let me know if you want me to\nremove it from this patch set.\n\nJonathan Tan (3):\n  mailinfo: separate in-body header processing\n  mailinfo: make is_scissors_line take plain char *\n  mailinfo: handle in-body header continuations\n\n mailinfo.c                           | 116 +++++++++++++++++++++++++----------\n mailinfo.h                           |   1 +\n t/t4150-am.sh                        |  23 +++++++\n t/t5100-mailinfo.sh                  |   2 +-\n t/t5100/info0018                     |   5 ++\n t/t5100/info0018--no-inbody-headers  |   5 ++\n t/t5100/msg0015                      |   2 -\n t/t5100/msg0018                      |   2 +\n t/t5100/msg0018--no-inbody-headers   |   8 +++\n t/t5100/patch0018                    |   6 ++\n t/t5100/patch0018--no-inbody-headers |   6 ++\n t/t5100/sample.mbox                  |  19 ++++++\n 12 files changed, 159 insertions(+), 36 deletions(-)\n create mode 100644 t/t5100/info0018\n create mode 100644 t/t5100/info0018--no-inbody-headers\n create mode 100644 t/t5100/msg0018\n create mode 100644 t/t5100/msg0018--no-inbody-headers\n create mode 100644 t/t5100/patch0018\n create mode 100644 t/t5100/patch0018--no-inbody-headers\n\n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302227","messageId":"12ea344c46ed17ed90dd92bebc45be761a51ef80.1474391594.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":"cover.1474391594.git.jonathantanmy@google.com","subject":"[PATCH v3 1/3] mailinfo: separate in-body header processing","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-20T17:17:51Z","receivedAt":"2016-09-20T17:18:10Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"The check_header function contains logic specific to in-body headers,\nalthough it is invoked during both the processing of actual headers and\nin-body headers. Separate out the in-body header part into its own\nfunction.\n\nSigned-off-by: Jonathan Tan <jonathantanmy@google.com>\n---\n mailinfo.c | 33 +++++++++++++++++----------------\n 1 file changed, 17 insertions(+), 16 deletions(-)\n\ndiff --git a/mailinfo.c b/mailinfo.c\nindex e19abe3..0c4738a 100644\n--- a/mailinfo.c\n+++ b/mailinfo.c\n@@ -495,21 +495,6 @@ static int check_header(struct mailinfo *mi,\n \t\tgoto check_header_out;\n \t}\n \n-\t/* for inbody stuff */\n-\tif (starts_with(line->buf, \">From\") && isspace(line->buf[5])) {\n-\t\tret = is_format_patch_separator(line->buf + 1, line->len - 1);\n-\t\tgoto check_header_out;\n-\t}\n-\tif (starts_with(line->buf, \"[PATCH]\") && isspace(line->buf[7])) {\n-\t\tfor (i = 0; header[i]; i++) {\n-\t\t\tif (!strcmp(\"Subject\", header[i])) {\n-\t\t\t\thandle_header(&hdr_data[i], line);\n-\t\t\t\tret = 1;\n-\t\t\t\tgoto check_header_out;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n check_header_out:\n \tstrbuf_release(&sb);\n \treturn ret;\n@@ -623,6 +608,22 @@ static int is_scissors_line(const struct strbuf *line)\n \t\tgap * 2 < perforation);\n }\n \n+static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)\n+{\n+\tif (starts_with(line->buf, \">From\") && isspace(line->buf[5]))\n+\t\treturn is_format_patch_separator(line->buf + 1, line->len - 1);\n+\tif (starts_with(line->buf, \"[PATCH]\") && isspace(line->buf[7])) {\n+\t\tint i;\n+\t\tfor (i = 0; header[i]; i++)\n+\t\t\tif (!strcmp(\"Subject\", header[i])) {\n+\t\t\t\thandle_header(&mi->s_hdr_data[i], line);\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\t\treturn 0;\n+\t}\n+\treturn check_header(mi, line, mi->s_hdr_data, 0);\n+}\n+\n static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)\n {\n \tassert(!mi->filter_stage);\n@@ -633,7 +634,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)\n \t}\n \n \tif (mi->use_inbody_headers && mi->header_stage) {\n-\t\tmi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);\n+\t\tmi->header_stage = check_inbody_header(mi, line);\n \t\tif (mi->header_stage)\n \t\t\treturn 0;\n \t} else\n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302228","messageId":"820980e95ee640013b2cf9c30b616ced39af67fa.1474391594.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":"cover.1474391594.git.jonathantanmy@google.com","subject":"[PATCH v3 2/3] mailinfo: make is_scissors_line take plain char *","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-20T17:17:52Z","receivedAt":"2016-09-20T17:18:12Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"The is_scissors_line takes a struct strbuf * when a char * would\nsuffice. Make it take char *.\n\nSigned-off-by: Jonathan Tan <jonathantanmy@google.com>\n---\n mailinfo.c | 35 ++++++++++++++++++-----------------\n 1 file changed, 18 insertions(+), 17 deletions(-)\n\ndiff --git a/mailinfo.c b/mailinfo.c\nindex 0c4738a..69391aa 100644\n--- a/mailinfo.c\n+++ b/mailinfo.c\n@@ -557,37 +557,35 @@ static inline int patchbreak(const struct strbuf *line)\n \treturn 0;\n }\n \n-static int is_scissors_line(const struct strbuf *line)\n+static int is_scissors_line(const char *line)\n {\n-\tsize_t i, len = line->len;\n+\tconst char *c;\n \tint scissors = 0, gap = 0;\n-\tint first_nonblank = -1;\n-\tint last_nonblank = 0, visible, perforation = 0, in_perforation = 0;\n-\tconst char *buf = line->buf;\n+\tconst char *first_nonblank = NULL, *last_nonblank = NULL;\n+\tint visible, perforation = 0, in_perforation = 0;\n \n-\tfor (i = 0; i < len; i++) {\n-\t\tif (isspace(buf[i])) {\n+\tfor (c = line; *c; c++) {\n+\t\tif (isspace(*c)) {\n \t\t\tif (in_perforation) {\n \t\t\t\tperforation++;\n \t\t\t\tgap++;\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n-\t\tlast_nonblank = i;\n-\t\tif (first_nonblank < 0)\n-\t\t\tfirst_nonblank = i;\n-\t\tif (buf[i] == '-') {\n+\t\tlast_nonblank = c;\n+\t\tif (first_nonblank == NULL)\n+\t\t\tfirst_nonblank = c;\n+\t\tif (*c == '-') {\n \t\t\tin_perforation = 1;\n \t\t\tperforation++;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (i + 1 < len &&\n-\t\t    (!memcmp(buf + i, \">8\", 2) || !memcmp(buf + i, \"8<\", 2) ||\n-\t\t     !memcmp(buf + i, \">%\", 2) || !memcmp(buf + i, \"%<\", 2))) {\n+\t\tif ((!memcmp(c, \">8\", 2) || !memcmp(c, \"8<\", 2) ||\n+\t\t     !memcmp(c, \">%\", 2) || !memcmp(c, \"%<\", 2))) {\n \t\t\tin_perforation = 1;\n \t\t\tperforation += 2;\n \t\t\tscissors += 2;\n-\t\t\ti++;\n+\t\t\tc++;\n \t\t\tcontinue;\n \t\t}\n \t\tin_perforation = 0;\n@@ -602,7 +600,10 @@ static int is_scissors_line(const struct strbuf *line)\n \t * than half of the perforation.\n \t */\n \n-\tvisible = last_nonblank - first_nonblank + 1;\n+\tif (first_nonblank && last_nonblank)\n+\t\tvisible = last_nonblank - first_nonblank + 1;\n+\telse\n+\t\tvisible = 0;\n \treturn (scissors && 8 <= visible &&\n \t\tvisible < perforation * 3 &&\n \t\tgap * 2 < perforation);\n@@ -647,7 +648,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)\n \tif (convert_to_utf8(mi, line, mi->charset.buf))\n \t\treturn 0; /* mi->input_error already set */\n \n-\tif (mi->use_scissors && is_scissors_line(line)) {\n+\tif (mi->use_scissors && is_scissors_line(line->buf)) {\n \t\tint i;\n \n \t\tstrbuf_setlen(&mi->log_message, 0);\n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302229","messageId":"54f0a036b504e88af3122069864524d60a954a4d.1474391594.git.jonathantanmy@google.com","threadId":"44114","inReplyTo":"cover.1474391594.git.jonathantanmy@google.com","subject":"[PATCH v3 3/3] mailinfo: handle in-body header continuations","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2016-09-20T17:17:53Z","receivedAt":"2016-09-20T17:18:16Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"Mailinfo currently handles multi-line headers, but it does not handle\nmulti-line in-body headers. Teach it to handle such headers, for\nexample, for this input:\n\n  From: author <author@example.com>\n  Date: Fri, 9 Jun 2006 00:44:16 -0700\n  Subject: a very long\n   broken line\n\n  Subject: another very long\n   broken line\n\ninterpret the in-body subject to be \"another very long broken line\"\ninstead of \"another very long\".\n\nAn existing test (t/t5100/msg0015) has an indented line immediately\nafter an in-body header - it has been modified to reflect the new\nfunctionality.\n\nSigned-off-by: Jonathan Tan <jonathantanmy@google.com>\n---\n mailinfo.c                           | 50 +++++++++++++++++++++++++++++++++++-\n mailinfo.h                           |  1 +\n t/t4150-am.sh                        | 23 +++++++++++++++++\n t/t5100-mailinfo.sh                  |  2 +-\n t/t5100/info0018                     |  5 ++++\n t/t5100/info0018--no-inbody-headers  |  5 ++++\n t/t5100/msg0015                      |  2 --\n t/t5100/msg0018                      |  2 ++\n t/t5100/msg0018--no-inbody-headers   |  8 ++++++\n t/t5100/patch0018                    |  6 +++++\n t/t5100/patch0018--no-inbody-headers |  6 +++++\n t/t5100/sample.mbox                  | 19 ++++++++++++++\n 12 files changed, 125 insertions(+), 4 deletions(-)\n create mode 100644 t/t5100/info0018\n create mode 100644 t/t5100/info0018--no-inbody-headers\n create mode 100644 t/t5100/msg0018\n create mode 100644 t/t5100/msg0018--no-inbody-headers\n create mode 100644 t/t5100/patch0018\n create mode 100644 t/t5100/patch0018--no-inbody-headers\n\ndiff --git a/mailinfo.c b/mailinfo.c\nindex 69391aa..2275b28 100644\n--- a/mailinfo.c\n+++ b/mailinfo.c\n@@ -500,6 +500,21 @@ static int check_header(struct mailinfo *mi,\n \treturn ret;\n }\n \n+/*\n+ * Returns 1 if the given line or any line beginning with the given line is an\n+ * in-body header (that is, check_header will succeed when passed\n+ * mi->s_hdr_data).\n+ */\n+static int is_inbody_header(const struct mailinfo *mi,\n+\t\t\t    const struct strbuf *line)\n+{\n+\tint i;\n+\tfor (i = 0; header[i]; i++)\n+\t\tif (!mi->s_hdr_data[i] && cmp_header(line, header[i]))\n+\t\t\treturn 1;\n+\treturn 0;\n+}\n+\n static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)\n {\n \tstruct strbuf *ret;\n@@ -609,8 +624,33 @@ static int is_scissors_line(const char *line)\n \t\tgap * 2 < perforation);\n }\n \n+static void flush_inbody_header_accum(struct mailinfo *mi)\n+{\n+\tif (!mi->inbody_header_accum.len)\n+\t\treturn;\n+\tassert(check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0));\n+\tstrbuf_reset(&mi->inbody_header_accum);\n+}\n+\n static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)\n {\n+\tif (mi->inbody_header_accum.len &&\n+\t    (line->buf[0] == ' ' || line->buf[0] == '\\t')) {\n+\t\tif (mi->use_scissors && is_scissors_line(line->buf)) {\n+\t\t\t/*\n+\t\t\t * This is a scissors line; do not consider this line\n+\t\t\t * as a header continuation line.\n+\t\t\t */\n+\t\t\tflush_inbody_header_accum(mi);\n+\t\t\treturn 0;\n+\t\t}\n+\t\tstrbuf_strip_suffix(&mi->inbody_header_accum, \"\\n\");\n+\t\tstrbuf_addbuf(&mi->inbody_header_accum, line);\n+\t\treturn 1;\n+\t}\n+\n+\tflush_inbody_header_accum(mi);\n+\n \tif (starts_with(line->buf, \">From\") && isspace(line->buf[5]))\n \t\treturn is_format_patch_separator(line->buf + 1, line->len - 1);\n \tif (starts_with(line->buf, \"[PATCH]\") && isspace(line->buf[7])) {\n@@ -622,7 +662,11 @@ static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)\n \t\t\t}\n \t\treturn 0;\n \t}\n-\treturn check_header(mi, line, mi->s_hdr_data, 0);\n+\tif (is_inbody_header(mi, line)) {\n+\t\tstrbuf_addbuf(&mi->inbody_header_accum, line);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n }\n \n static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)\n@@ -888,6 +932,8 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)\n \t\t\tbreak;\n \t} while (!strbuf_getwholeline(line, mi->input, '\\n'));\n \n+\tflush_inbody_header_accum(mi);\n+\n handle_body_out:\n \tstrbuf_release(&prev);\n }\n@@ -1003,6 +1049,7 @@ void setup_mailinfo(struct mailinfo *mi)\n \tstrbuf_init(&mi->email, 0);\n \tstrbuf_init(&mi->charset, 0);\n \tstrbuf_init(&mi->log_message, 0);\n+\tstrbuf_init(&mi->inbody_header_accum, 0);\n \tmi->header_stage = 1;\n \tmi->use_inbody_headers = 1;\n \tmi->content_top = mi->content;\n@@ -1016,6 +1063,7 @@ void clear_mailinfo(struct mailinfo *mi)\n \tstrbuf_release(&mi->name);\n \tstrbuf_release(&mi->email);\n \tstrbuf_release(&mi->charset);\n+\tstrbuf_release(&mi->inbody_header_accum);\n \tfree(mi->message_id);\n \n \tfor (i = 0; mi->p_hdr_data[i]; i++)\ndiff --git a/mailinfo.h b/mailinfo.h\nindex 93776a7..04a2535 100644\n--- a/mailinfo.h\n+++ b/mailinfo.h\n@@ -27,6 +27,7 @@ struct mailinfo {\n \tint patch_lines;\n \tint filter_stage; /* still reading log or are we copying patch? */\n \tint header_stage; /* still checking in-body headers? */\n+\tstruct strbuf inbody_header_accum;\n \tstruct strbuf **p_hdr_data;\n \tstruct strbuf **s_hdr_data;\n \ndiff --git a/t/t4150-am.sh b/t/t4150-am.sh\nindex 9ce9424..89a5bac 100755\n--- a/t/t4150-am.sh\n+++ b/t/t4150-am.sh\n@@ -977,4 +977,27 @@ test_expect_success 'am --patch-format=mboxrd handles mboxrd' '\n \ttest_cmp msg out\n '\n \n+test_expect_success 'am works with multi-line in-body headers' '\n+\tFORTY=\"String that has a length of more than forty characters\" &&\n+\tLONG=\"$FORTY $FORTY\" &&\n+\trm -fr .git/rebase-apply &&\n+\tgit checkout -f first &&\n+\techo one >> file &&\n+\tgit commit -am \"$LONG\" --author=\"$LONG <long@example.com>\" &&\n+\tgit format-patch --stdout -1 >patch &&\n+\t# bump from, date, and subject down to in-body header\n+\tperl -lpe \"\n+\t\tif (/^From:/) {\n+\t\t\tprint \\\"From: x <x\\@example.com>\\\";\n+\t\t\tprint \\\"Date: Sat, 1 Jan 2000 00:00:00 +0000\\\";\n+\t\t\tprint \\\"Subject: x\\n\\\";\n+\t\t}\n+\t\" patch >msg &&\n+\tgit checkout HEAD^ &&\n+\tgit am msg &&\n+\t# Ensure that the author and full message are present\n+\tgit cat-file commit HEAD | grep \"^author.*long@example.com\" &&\n+\tgit cat-file commit HEAD | grep \"^$LONG\"\n+'\n+\n test_done\ndiff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh\nindex 1a5a546..e173c33 100755\n--- a/t/t5100-mailinfo.sh\n+++ b/t/t5100-mailinfo.sh\n@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \\\n \t'git mailsplit -o. \"$TEST_DIRECTORY\"/t5100/sample.mbox >last &&\n \tlast=$(cat last) &&\n \techo total is $last &&\n-\ttest $(cat last) = 17'\n+\ttest $(cat last) = 18'\n \n check_mailinfo () {\n \tmail=$1 opt=$2\ndiff --git a/t/t5100/info0018 b/t/t5100/info0018\nnew file mode 100644\nindex 0000000..d53e749\n--- /dev/null\n+++ b/t/t5100/info0018\n@@ -0,0 +1,5 @@\n+Author: Another Thor\n+Email: a.thor@example.com\n+Subject: This one contains a tab and a space\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+\ndiff --git a/t/t5100/info0018--no-inbody-headers b/t/t5100/info0018--no-inbody-headers\nnew file mode 100644\nindex 0000000..30b17bd\n--- /dev/null\n+++ b/t/t5100/info0018--no-inbody-headers\n@@ -0,0 +1,5 @@\n+Author: A U Thor\n+Email: a.u.thor@example.com\n+Subject: check multiline inbody headers\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+\ndiff --git a/t/t5100/msg0015 b/t/t5100/msg0015\nindex 4abb3d5..e69de29 100644\n--- a/t/t5100/msg0015\n+++ b/t/t5100/msg0015\n@@ -1,2 +0,0 @@\n-  - a list\n-  - of stuff\ndiff --git a/t/t5100/msg0018 b/t/t5100/msg0018\nnew file mode 100644\nindex 0000000..56de83d\n--- /dev/null\n+++ b/t/t5100/msg0018\n@@ -0,0 +1,2 @@\n+a commit message\n+\ndiff --git a/t/t5100/msg0018--no-inbody-headers b/t/t5100/msg0018--no-inbody-headers\nnew file mode 100644\nindex 0000000..b1e05d3\n--- /dev/null\n+++ b/t/t5100/msg0018--no-inbody-headers\n@@ -0,0 +1,8 @@\n+From: Another Thor\n+ <a.thor@example.com>\n+Subject: This one contains\n+\ta tab\n+ and a space\n+\n+a commit message\n+\ndiff --git a/t/t5100/patch0018 b/t/t5100/patch0018\nnew file mode 100644\nindex 0000000..789df6d\n--- /dev/null\n+++ b/t/t5100/patch0018\n@@ -0,0 +1,6 @@\n+diff --git a/foo b/foo\n+index e69de29..d95f3ad 100644\n+--- a/foo\n++++ b/foo\n+@@ -0,0 +1 @@\n++content\ndiff --git a/t/t5100/patch0018--no-inbody-headers b/t/t5100/patch0018--no-inbody-headers\nnew file mode 100644\nindex 0000000..789df6d\n--- /dev/null\n+++ b/t/t5100/patch0018--no-inbody-headers\n@@ -0,0 +1,6 @@\n+diff --git a/foo b/foo\n+index e69de29..d95f3ad 100644\n+--- a/foo\n++++ b/foo\n+@@ -0,0 +1 @@\n++content\ndiff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox\nindex 8b2ae06..6d4d0e4 100644\n--- a/t/t5100/sample.mbox\n+++ b/t/t5100/sample.mbox\n@@ -699,3 +699,22 @@ index e69de29..d95f3ad 100644\n +++ b/foo\n @@ -0,0 +1 @@\n +New content\n+From nobody Mon Sep 17 00:00:00 2001\n+From: A U Thor <a.u.thor@example.com>\n+Subject: check multiline inbody headers\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+\n+From: Another Thor\n+ <a.thor@example.com>\n+Subject: This one contains\n+\ta tab\n+ and a space\n+\n+a commit message\n+\n+diff --git a/foo b/foo\n+index e69de29..d95f3ad 100644\n+--- a/foo\n++++ b/foo\n+@@ -0,0 +1 @@\n++content\n-- \n2.10.0.rc2.20.g5b18e70\n\n"},{"id":"302252","messageId":"20160920230616.n2jwhavi2dzgflno@sigill.intra.peff.net","threadId":"44114","inReplyTo":"cover.1474391594.git.jonathantanmy@google.com","subject":"Re: [PATCH v3 0/3] handle multiline in-body headers","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-09-20T23:06:16Z","receivedAt":"2016-09-20T23:06:24Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 20, 2016 at 10:17:50AM -0700, Jonathan Tan wrote:\n\n> Changes since v2:\n> o Removed utf8 translation before scissors line check in\n>   check_inbody_header (I was thinking of support for encodings like\n>   UTF-16, but I guess those don't work with the current reencode_string\n>   anyway since it uses strlen internally)\n\nYeah, I'd be surprised if UTF-16 works very well with our code in\ngeneral. If we want to address that, though, the sanest thing is\nprobably to convert it internally to UTF-8 when we remove the transfer\nencoding in handle_body().\n\n-Peff\n"},{"id":"302298","messageId":"xmqqbmzh9nds.fsf@gitster.mtv.corp.google.com","threadId":"44114","inReplyTo":"cover.1474391594.git.jonathantanmy@google.com","subject":"Re: [PATCH v3 0/3] handle multiline in-body headers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-09-21T17:24:47Z","receivedAt":"2016-09-21T17:25:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Tan <jonathantanmy@google.com> writes:\n\n> With the above change, it is actually no longer necessary to make\n> is_scissors_line take plain char * (the second patch) - I think that\n> that patch still improves the code, but let me know if you want me to\n> remove it from this patch set.\n\nI agree with you that it is an independently good change.  Let's\nkeep it.\n\nOverall looked very good.  Thanks, will queue.\n"}]}