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>
		
	
		
			
				
	
	
		
			130 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
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
 |