mirror of
https://github.com/git/git
synced 2024-08-23 18:00:25 +00:00
mailinfo: support format=flowed
Add best-effort support for patches sent using format=flowed (RFC 3676). Remove leading spaces ("unstuff"), remove soft line breaks (indicated by space + newline), but leave the signature separator (dash dash space newline) alone. Warn in git am when encountering a format=flowed patch, because any trailing spaces would most probably be lost, as the sending MUA is encouraged to remove them when preparing the email. Provide a test patch formatted by Mozilla Thunderbird 60 using its default configuration. It reuses the contents of the file mailinfo.c before and after this patch. Signed-off-by: Rene Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
53f9a3e157
commit
3aa4d81f88
|
@ -1243,6 +1243,10 @@ static int parse_mail(struct am_state *state, const char *mail)
|
|||
fclose(mi.input);
|
||||
fclose(mi.output);
|
||||
|
||||
if (mi.format_flowed)
|
||||
warning(_("Patch sent with format=flowed; "
|
||||
"space at the end of lines might be lost."));
|
||||
|
||||
/* Extract message and author information */
|
||||
fp = xfopen(am_path(state, "info"), "r");
|
||||
while (!strbuf_getline_lf(&sb, fp)) {
|
||||
|
|
64
mailinfo.c
64
mailinfo.c
|
@ -237,11 +237,22 @@ static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int has_attr_value(const char *line, const char *name, const char *value)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
int rc = slurp_attr(line, name, &sb) && !strcasecmp(sb.buf, value);
|
||||
strbuf_release(&sb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
|
||||
{
|
||||
struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
|
||||
strbuf_init(boundary, line->len);
|
||||
|
||||
mi->format_flowed = has_attr_value(line->buf, "format=", "flowed");
|
||||
mi->delsp = has_attr_value(line->buf, "delsp=", "yes");
|
||||
|
||||
if (slurp_attr(line->buf, "boundary=", boundary)) {
|
||||
strbuf_insert(boundary, 0, "--", 2);
|
||||
if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
|
||||
|
@ -964,6 +975,52 @@ static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void handle_filter_flowed(struct mailinfo *mi, struct strbuf *line,
|
||||
struct strbuf *prev)
|
||||
{
|
||||
size_t len = line->len;
|
||||
const char *rest;
|
||||
|
||||
if (!mi->format_flowed) {
|
||||
handle_filter(mi, line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (line->buf[len - 1] == '\n') {
|
||||
len--;
|
||||
if (len && line->buf[len - 1] == '\r')
|
||||
len--;
|
||||
}
|
||||
|
||||
/* Keep signature separator as-is. */
|
||||
if (skip_prefix(line->buf, "-- ", &rest) && rest - line->buf == len) {
|
||||
if (prev->len) {
|
||||
handle_filter(mi, prev);
|
||||
strbuf_reset(prev);
|
||||
}
|
||||
handle_filter(mi, line);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unstuff space-stuffed line. */
|
||||
if (len && line->buf[0] == ' ') {
|
||||
strbuf_remove(line, 0, 1);
|
||||
len--;
|
||||
}
|
||||
|
||||
/* Save flowed line for later, but without the soft line break. */
|
||||
if (len && line->buf[len - 1] == ' ') {
|
||||
strbuf_add(prev, line->buf, len - !!mi->delsp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepend any previous partial lines */
|
||||
strbuf_insert(line, 0, prev->buf, prev->len);
|
||||
strbuf_reset(prev);
|
||||
|
||||
handle_filter(mi, line);
|
||||
}
|
||||
|
||||
static void handle_body(struct mailinfo *mi, struct strbuf *line)
|
||||
{
|
||||
struct strbuf prev = STRBUF_INIT;
|
||||
|
@ -1012,7 +1069,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
|
|||
strbuf_addbuf(&prev, sb);
|
||||
break;
|
||||
}
|
||||
handle_filter(mi, sb);
|
||||
handle_filter_flowed(mi, sb, &prev);
|
||||
}
|
||||
/*
|
||||
* The partial chunk is saved in "prev" and will be
|
||||
|
@ -1022,13 +1079,16 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
|
|||
break;
|
||||
}
|
||||
default:
|
||||
handle_filter(mi, line);
|
||||
handle_filter_flowed(mi, line, &prev);
|
||||
}
|
||||
|
||||
if (mi->input_error)
|
||||
break;
|
||||
} while (!strbuf_getwholeline(line, mi->input, '\n'));
|
||||
|
||||
if (prev.len)
|
||||
handle_filter(mi, &prev);
|
||||
|
||||
flush_inbody_header_accum(mi);
|
||||
|
||||
handle_body_out:
|
||||
|
|
|
@ -20,6 +20,8 @@ struct mailinfo {
|
|||
struct strbuf *content[MAX_BOUNDARIES];
|
||||
struct strbuf **content_top;
|
||||
struct strbuf charset;
|
||||
unsigned int format_flowed:1;
|
||||
unsigned int delsp:1;
|
||||
char *message_id;
|
||||
enum {
|
||||
TE_DONTCARE, TE_QP, TE_BASE64
|
||||
|
|
19
t/t4256-am-format-flowed.sh
Executable file
19
t/t4256-am-format-flowed.sh
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='test format=flowed support of git am'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
cp "$TEST_DIRECTORY/t4256/1/mailinfo.c.orig" mailinfo.c &&
|
||||
git add mailinfo.c &&
|
||||
git commit -m initial
|
||||
'
|
||||
|
||||
test_expect_success 'am with format=flowed' '
|
||||
git am <"$TEST_DIRECTORY/t4256/1/patch" >stdout 2>stderr &&
|
||||
test_i18ngrep "warning: Patch sent with format=flowed" stderr &&
|
||||
test_cmp "$TEST_DIRECTORY/t4256/1/mailinfo.c" mailinfo.c
|
||||
'
|
||||
|
||||
test_done
|
1245
t/t4256/1/mailinfo.c
Normal file
1245
t/t4256/1/mailinfo.c
Normal file
File diff suppressed because it is too large
Load diff
1185
t/t4256/1/mailinfo.c.orig
Normal file
1185
t/t4256/1/mailinfo.c.orig
Normal file
File diff suppressed because it is too large
Load diff
129
t/t4256/1/patch
Normal file
129
t/t4256/1/patch
Normal file
|
@ -0,0 +1,129 @@
|
|||
From: A <author@example.com>
|
||||
Subject: [PATCH] mailinfo: support format=flowed
|
||||
Message-ID: <aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa@example.com>
|
||||
Date: Sat, 25 Aug 2018 22:04:50 +0200
|
||||
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:60.0) Gecko/20100101
|
||||
Thunderbird/60.0
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=utf-8; format=flowed
|
||||
Content-Language: en-US
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
---
|
||||
mailinfo.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 62 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/mailinfo.c b/mailinfo.c
|
||||
index 3281a37d51..b395adbdf2 100644
|
||||
--- a/mailinfo.c
|
||||
+++ b/mailinfo.c
|
||||
@@ -237,11 +237,22 @@ static int slurp_attr(const char *line, const char
|
||||
*name, struct strbuf *attr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+static int has_attr_value(const char *line, const char *name, const
|
||||
char *value)
|
||||
+{
|
||||
+ struct strbuf sb = STRBUF_INIT;
|
||||
+ int rc = slurp_attr(line, name, &sb) && !strcasecmp(sb.buf, value);
|
||||
+ strbuf_release(&sb);
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
|
||||
{
|
||||
struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
|
||||
strbuf_init(boundary, line->len);
|
||||
|
||||
+ mi->format_flowed = has_attr_value(line->buf, "format=", "flowed");
|
||||
+ mi->delsp = has_attr_value(line->buf, "delsp=", "yes");
|
||||
+
|
||||
if (slurp_attr(line->buf, "boundary=", boundary)) {
|
||||
strbuf_insert(boundary, 0, "--", 2);
|
||||
if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
|
||||
@@ -964,6 +975,52 @@ static int handle_boundary(struct mailinfo *mi,
|
||||
struct strbuf *line)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+static void handle_filter_flowed(struct mailinfo *mi, struct strbuf *line,
|
||||
+ struct strbuf *prev)
|
||||
+{
|
||||
+ size_t len = line->len;
|
||||
+ const char *rest;
|
||||
+
|
||||
+ if (!mi->format_flowed) {
|
||||
+ handle_filter(mi, line);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (line->buf[len - 1] == '\n') {
|
||||
+ len--;
|
||||
+ if (len && line->buf[len - 1] == '\r')
|
||||
+ len--;
|
||||
+ }
|
||||
+
|
||||
+ /* Keep signature separator as-is. */
|
||||
+ if (skip_prefix(line->buf, "-- ", &rest) && rest - line->buf == len) {
|
||||
+ if (prev->len) {
|
||||
+ handle_filter(mi, prev);
|
||||
+ strbuf_reset(prev);
|
||||
+ }
|
||||
+ handle_filter(mi, line);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Unstuff space-stuffed line. */
|
||||
+ if (len && line->buf[0] == ' ') {
|
||||
+ strbuf_remove(line, 0, 1);
|
||||
+ len--;
|
||||
+ }
|
||||
+
|
||||
+ /* Save flowed line for later, but without the soft line break. */
|
||||
+ if (len && line->buf[len - 1] == ' ') {
|
||||
+ strbuf_add(prev, line->buf, len - !!mi->delsp);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Prepend any previous partial lines */
|
||||
+ strbuf_insert(line, 0, prev->buf, prev->len);
|
||||
+ strbuf_reset(prev);
|
||||
+
|
||||
+ handle_filter(mi, line);
|
||||
+}
|
||||
+
|
||||
static void handle_body(struct mailinfo *mi, struct strbuf *line)
|
||||
{
|
||||
struct strbuf prev = STRBUF_INIT;
|
||||
@@ -1012,7 +1069,7 @@ static void handle_body(struct mailinfo *mi,
|
||||
struct strbuf *line)
|
||||
strbuf_addbuf(&prev, sb);
|
||||
break;
|
||||
}
|
||||
- handle_filter(mi, sb);
|
||||
+ handle_filter_flowed(mi, sb, &prev);
|
||||
}
|
||||
/*
|
||||
* The partial chunk is saved in "prev" and will be
|
||||
@@ -1022,13 +1079,16 @@ static void handle_body(struct mailinfo *mi,
|
||||
struct strbuf *line)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
- handle_filter(mi, line);
|
||||
+ handle_filter_flowed(mi, line, &prev);
|
||||
}
|
||||
|
||||
if (mi->input_error)
|
||||
break;
|
||||
} while (!strbuf_getwholeline(line, mi->input, '\n'));
|
||||
|
||||
+ if (prev.len)
|
||||
+ handle_filter(mi, &prev);
|
||||
+
|
||||
flush_inbody_header_accum(mi);
|
||||
|
||||
handle_body_out:
|
||||
--
|
||||
2.18.0
|
Loading…
Reference in a new issue