All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/26] mailinfo libification
@ 2015-10-13 23:16 Junio C Hamano
  2015-10-13 23:16 ` [PATCH 01/26] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
                   ` (26 more replies)
  0 siblings, 27 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

So here is an attempt to libify "git mailinfo" so that the built-in
version of "git am" does not have to run it via run_command()
interface.  "git am", when fed an N-patch series, runs one
"mailsplit", N "mailinfo" and N "apply" all via run_command()
interface (plus 2 more "apply" and 1 "merge-recursive" per a patch
that does not apply cleanly, when run with the "-3" option), and
among the various programs spawned from "git am", "mailinfo" is the
most straight-forward, stupid and light-weight program, so it is a
no-brainer to pick it as the candidate for libification.

This goes on top of c5920b21 (mailinfo: ignore in-body header that
we do not care about, 2015-10-08) that was posted earlier as a
weatherbaloon patch.

Junio C Hamano (26):
  mailinfo: remove a no-op call convert_to_utf8(it, "")
  mailinfo: fix for off-by-one error in boundary stack
  mailinfo: fold decode_header_bq() into decode_header()
  mailinfo: move handle_boundary() lower
  mailinfo: get rid of function-local static states
  mailinfo: always pass "line" as an argument
  mailinfo: move global "line" into mailinfo() function
  mailinfo: introduce "struct mailinfo" to hold globals
  mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  mailinfo: move filter/header stage to struct mailinfo
  mailinfo: move patch_lines to struct mailinfo
  mailinfo: move add_message_id and message_id to struct mailinfo
  mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  mailinfo: move metainfo_charset to struct mailinfo
  mailinfo: move transfer_encoding to struct mailinfo
  mailinfo: move charset to struct mailinfo
  mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  mailinfo: move cmitmsg and patchfile to struct mailinfo
  mailinfo: move [ps]_hdr_data to struct mailinfo
  mailinfo: keep the parsed log message in a strbuf
  mailinfo: move content/content_top to struct mailinfo
  mailinfo: handle errors found in decode_header() better
  mailinfo: handle charset conversion errors in the caller
  mailinfo: remove calls to exit() and die() deep in the callchain
  mailinfo: libify the whole thing

 Makefile           |    1 +
 builtin/mailinfo.c | 1083 +---------------------------------------------------
 mailinfo.c         | 1058 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mailinfo.h         |   41 ++
 4 files changed, 1120 insertions(+), 1063 deletions(-)
 create mode 100644 mailinfo.c
 create mode 100644 mailinfo.h

-- 
2.6.1-320-g86a1181

^ permalink raw reply	[flat|nested] 124+ messages in thread

* [PATCH 01/26] mailinfo: remove a no-op call convert_to_utf8(it, "")
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 02/26] mailinfo: fix for off-by-one error in boundary stack Junio C Hamano
                   ` (25 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

The called function checks if the second parameter is either a NULL
or an empty string at the very beginning and returns without doing
anything.  Remove the useless call.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 169ee54..330bef4 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -612,11 +612,6 @@ static void decode_header(struct strbuf *it)
 {
 	if (decode_header_bq(it))
 		return;
-	/* otherwise "it" is a straight copy of the input.
-	 * This can be binary guck but there is no charset specified.
-	 */
-	if (metainfo_charset)
-		convert_to_utf8(it, "");
 }
 
 static void decode_transfer_encoding(struct strbuf *line)
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 02/26] mailinfo: fix for off-by-one error in boundary stack
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
  2015-10-13 23:16 ` [PATCH 01/26] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-14 20:12   ` Stefan Beller
  2015-10-13 23:16 ` [PATCH 03/26] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
                   ` (24 subsequent siblings)
  26 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

We pre-increment the pointer that we will use to store something at,
so the pointer is already beyond the end of the array if it points
at content[MAX_BOUNDARIES].

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 As always, I am very bad at checking and fixing off-by-one errors.
 A few extra sets of eyeballs are very much appreciated.
---
 builtin/mailinfo.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 330bef4..2742d0d 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -185,7 +185,7 @@ static void handle_content_type(struct strbuf *line)
 
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
-		if (++content_top > &content[MAX_BOUNDARIES]) {
+		if (++content_top >= &content[MAX_BOUNDARIES]) {
 			fprintf(stderr, "Too many boundaries to handle\n");
 			exit(1);
 		}
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 03/26] mailinfo: fold decode_header_bq() into decode_header()
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
  2015-10-13 23:16 ` [PATCH 01/26] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
  2015-10-13 23:16 ` [PATCH 02/26] mailinfo: fix for off-by-one error in boundary stack Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 04/26] mailinfo: move handle_boundary() lower Junio C Hamano
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

In olden days we might have wanted to behave differently in
decode_header() if the header line was encoded with RFC2047, but we
apparently do not do so, hence this helper function can go, together
with its return value.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 2742d0d..3b015a5 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -525,19 +525,17 @@ static void convert_to_utf8(struct strbuf *line, const char *charset)
 	strbuf_attach(line, out, strlen(out), strlen(out));
 }
 
-static int decode_header_bq(struct strbuf *it)
+static void decode_header(struct strbuf *it)
 {
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
 	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
-	int rfc2047 = 0;
 
 	in = it->buf;
 	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
 		int encoding;
 		strbuf_reset(&charset_q);
 		strbuf_reset(&piecebuf);
-		rfc2047 = 1;
 
 		if (in != ep) {
 			/*
@@ -567,22 +565,22 @@ static int decode_header_bq(struct strbuf *it)
 		ep += 2;
 
 		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
-			goto decode_header_bq_out;
+			goto release_return;
 
 		if (cp + 3 - it->buf > it->len)
-			goto decode_header_bq_out;
+			goto release_return;
 		strbuf_add(&charset_q, ep, cp - ep);
 
 		encoding = cp[1];
 		if (!encoding || cp[2] != '?')
-			goto decode_header_bq_out;
+			goto release_return;
 		ep = strstr(cp + 3, "?=");
 		if (!ep)
-			goto decode_header_bq_out;
+			goto release_return;
 		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
 		switch (tolower(encoding)) {
 		default:
-			goto decode_header_bq_out;
+			goto release_return;
 		case 'b':
 			dec = decode_b_segment(&piecebuf);
 			break;
@@ -601,17 +599,10 @@ static int decode_header_bq(struct strbuf *it)
 	strbuf_addstr(&outbuf, in);
 	strbuf_reset(it);
 	strbuf_addbuf(it, &outbuf);
-decode_header_bq_out:
+release_return:
 	strbuf_release(&outbuf);
 	strbuf_release(&charset_q);
 	strbuf_release(&piecebuf);
-	return rfc2047;
-}
-
-static void decode_header(struct strbuf *it)
-{
-	if (decode_header_bq(it))
-		return;
 }
 
 static void decode_transfer_encoding(struct strbuf *line)
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 04/26] mailinfo: move handle_boundary() lower
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (2 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 03/26] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 05/26] mailinfo: get rid of function-local static states Junio C Hamano
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

This function wants to call find_boundary() and is called only from
one place without any recursing, so it becomes easier to read if it
appears after the called function.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 114 ++++++++++++++++++++++++++---------------------------
 1 file changed, 56 insertions(+), 58 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 3b015a5..175c6ae 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -626,64 +626,6 @@ static void decode_transfer_encoding(struct strbuf *line)
 	free(ret);
 }
 
-static void handle_filter(struct strbuf *line);
-
-static int find_boundary(void)
-{
-	while (!strbuf_getline(&line, fin, '\n')) {
-		if (*content_top && is_multipart_boundary(&line))
-			return 1;
-	}
-	return 0;
-}
-
-static int handle_boundary(void)
-{
-	struct strbuf newline = STRBUF_INIT;
-
-	strbuf_addch(&newline, '\n');
-again:
-	if (line.len >= (*content_top)->len + 2 &&
-	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
-		/* we hit an end boundary */
-		/* pop the current boundary off the stack */
-		strbuf_release(*content_top);
-		free(*content_top);
-		*content_top = NULL;
-
-		/* technically won't happen as is_multipart_boundary()
-		   will fail first.  But just in case..
-		 */
-		if (--content_top < content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
-		}
-		handle_filter(&newline);
-		strbuf_release(&newline);
-
-		/* skip to the next boundary */
-		if (!find_boundary())
-			return 0;
-		goto again;
-	}
-
-	/* set some defaults */
-	transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&charset);
-
-	/* slurp in this section's info */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 0);
-
-	strbuf_release(&newline);
-	/* replenish line */
-	if (strbuf_getline(&line, fin, '\n'))
-		return 0;
-	strbuf_addch(&line, '\n');
-	return 1;
-}
-
 static inline int patchbreak(const struct strbuf *line)
 {
 	size_t i;
@@ -879,6 +821,62 @@ static void handle_filter(struct strbuf *line)
 	}
 }
 
+static int find_boundary(void)
+{
+	while (!strbuf_getline(&line, fin, '\n')) {
+		if (*content_top && is_multipart_boundary(&line))
+			return 1;
+	}
+	return 0;
+}
+
+static int handle_boundary(void)
+{
+	struct strbuf newline = STRBUF_INIT;
+
+	strbuf_addch(&newline, '\n');
+again:
+	if (line.len >= (*content_top)->len + 2 &&
+	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
+		/* we hit an end boundary */
+		/* pop the current boundary off the stack */
+		strbuf_release(*content_top);
+		free(*content_top);
+		*content_top = NULL;
+
+		/* technically won't happen as is_multipart_boundary()
+		   will fail first.  But just in case..
+		 */
+		if (--content_top < content) {
+			fprintf(stderr, "Detected mismatched boundaries, "
+					"can't recover\n");
+			exit(1);
+		}
+		handle_filter(&newline);
+		strbuf_release(&newline);
+
+		/* skip to the next boundary */
+		if (!find_boundary())
+			return 0;
+		goto again;
+	}
+
+	/* set some defaults */
+	transfer_encoding = TE_DONTCARE;
+	strbuf_reset(&charset);
+
+	/* slurp in this section's info */
+	while (read_one_header_line(&line, fin))
+		check_header(&line, p_hdr_data, 0);
+
+	strbuf_release(&newline);
+	/* replenish line */
+	if (strbuf_getline(&line, fin, '\n'))
+		return 0;
+	strbuf_addch(&line, '\n');
+	return 1;
+}
+
 static void handle_body(void)
 {
 	struct strbuf prev = STRBUF_INIT;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 05/26] mailinfo: get rid of function-local static states
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (3 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 04/26] mailinfo: move handle_boundary() lower Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 06/26] mailinfo: always pass "line" as an argument Junio C Hamano
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Two helper functions use "static int" in their scope to keep track
of the state while repeatedly getting called once for each input
line.  Move these state variables their ultimate caller and pass
down pointers to them, as a small step in preparation for making
this entire callchain more reentrant.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 48 +++++++++++++++++++++++-------------------------
 1 file changed, 23 insertions(+), 25 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 175c6ae..5a74811 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -713,21 +713,20 @@ static int is_scissors_line(const struct strbuf *line)
 		gap * 2 < perforation);
 }
 
-static int handle_commit_msg(struct strbuf *line)
+static int handle_commit_msg(struct strbuf *line, int *still_looking)
 {
 	/*
 	 * Are we still scanning and discarding in-body headers?
 	 * It is initially set to 1, set to 2 when we do see a
 	 * valid in-body header.
 	 */
-	static int still_looking = 1;
 	int is_empty_line;
 
 	if (!cmitmsg)
 		return 0;
 
 	is_empty_line = (!line->len || (line->len == 1 && line->buf[0] == '\n'));
-	if (still_looking == 1) {
+	if (*still_looking == 1) {
 		/*
 		 * Haven't seen a known in-body header; discard an empty line.
 		 */
@@ -735,33 +734,33 @@ static int handle_commit_msg(struct strbuf *line)
 			return 0;
 	}
 
-	if (use_inbody_headers && still_looking) {
+	if (use_inbody_headers && *still_looking) {
 		int is_known_header = check_header(line, s_hdr_data, 0);
 
-		if (still_looking == 2) {
+		if (*still_looking == 2) {
 			/*
 			 * an empty line after the in-body header block,
 			 * or a line obviously not an attempt to invent
 			 * an unsupported in-body header.
 			 */
 			if (is_empty_line || !is_rfc2822_header(line))
-				still_looking = 0;
+				*still_looking = 0;
 			if (is_empty_line)
 				return 0;
 			/* otherwise do not discard the line, but keep going */
 		} else if (is_known_header) {
-			still_looking = 2;
-		} else if (still_looking != 2) {
-			still_looking = 0;
+			*still_looking = 2;
+		} else if (*still_looking != 2) {
+			*still_looking = 0;
 		}
 
-		if (still_looking)
+		if (*still_looking)
 			return 0;
 	} else
 		/* Only trim the first (blank) line of the commit message
 		 * when ignoring in-body headers.
 		 */
-		still_looking = 0;
+		*still_looking = 0;
 
 	/* normalize the log message to UTF-8. */
 	if (metainfo_charset)
@@ -773,7 +772,7 @@ static int handle_commit_msg(struct strbuf *line)
 			die_errno("Could not rewind output message file");
 		if (ftruncate(fileno(cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
-		still_looking = 1;
+		*still_looking = 1;
 
 		/*
 		 * We may have already read "secondary headers"; purge
@@ -805,16 +804,13 @@ static void handle_patch(const struct strbuf *line)
 	patch_lines++;
 }
 
-static void handle_filter(struct strbuf *line)
+static void handle_filter(struct strbuf *line, int *filter_stage, int *header_stage)
 {
-	static int filter = 0;
-
-	/* filter tells us which part we left off on */
-	switch (filter) {
+	switch (*filter_stage) {
 	case 0:
-		if (!handle_commit_msg(line))
+		if (!handle_commit_msg(line, header_stage))
 			break;
-		filter++;
+		(*filter_stage)++;
 	case 1:
 		handle_patch(line);
 		break;
@@ -830,7 +826,7 @@ static int find_boundary(void)
 	return 0;
 }
 
-static int handle_boundary(void)
+static int handle_boundary(int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -852,7 +848,7 @@ again:
 					"can't recover\n");
 			exit(1);
 		}
-		handle_filter(&newline);
+		handle_filter(&newline, filter_stage, header_stage);
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
@@ -880,6 +876,8 @@ again:
 static void handle_body(void)
 {
 	struct strbuf prev = STRBUF_INIT;
+	int filter_stage = 0;
+	int header_stage = 1;
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
@@ -892,10 +890,10 @@ static void handle_body(void)
 		if (*content_top && is_multipart_boundary(&line)) {
 			/* flush any leftover */
 			if (prev.len) {
-				handle_filter(&prev);
+				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary())
+			if (!handle_boundary(&filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
@@ -925,7 +923,7 @@ static void handle_body(void)
 						strbuf_addbuf(&prev, sb);
 						break;
 					}
-				handle_filter(sb);
+				handle_filter(sb, &filter_stage, &header_stage);
 			}
 			/*
 			 * The partial chunk is saved in "prev" and will be
@@ -935,7 +933,7 @@ static void handle_body(void)
 			break;
 		}
 		default:
-			handle_filter(&line);
+			handle_filter(&line, &filter_stage, &header_stage);
 		}
 
 	} while (!strbuf_getwholeline(&line, fin, '\n'));
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 06/26] mailinfo: always pass "line" as an argument
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (4 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 05/26] mailinfo: get rid of function-local static states Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-14 20:22   ` Stefan Beller
  2015-10-13 23:16 ` [PATCH 07/26] mailinfo: move global "line" into mailinfo() function Junio C Hamano
                   ` (20 subsequent siblings)
  26 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Some functions in this module accessed the global "struct strbuf
line" while many others used a strbuf passed as an argument.
Convert the former to ensure that nobody deeper in the callchains
relies on the global one.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 48 ++++++++++++++++++++++++------------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 5a74811..c3c7d67 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,7 +12,7 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
-static struct strbuf line = STRBUF_INIT;
+static struct strbuf line_global = STRBUF_INIT;
 static struct strbuf name = STRBUF_INIT;
 static struct strbuf email = STRBUF_INIT;
 static char *message_id;
@@ -817,23 +817,23 @@ static void handle_filter(struct strbuf *line, int *filter_stage, int *header_st
 	}
 }
 
-static int find_boundary(void)
+static int find_boundary(struct strbuf *line)
 {
-	while (!strbuf_getline(&line, fin, '\n')) {
-		if (*content_top && is_multipart_boundary(&line))
+	while (!strbuf_getline(line, fin, '\n')) {
+		if (*content_top && is_multipart_boundary(line))
 			return 1;
 	}
 	return 0;
 }
 
-static int handle_boundary(int *filter_stage, int *header_stage)
+static int handle_boundary(struct strbuf *line, int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
 	strbuf_addch(&newline, '\n');
 again:
-	if (line.len >= (*content_top)->len + 2 &&
-	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
+	if (line->len >= (*content_top)->len + 2 &&
+	    !memcmp(line->buf + (*content_top)->len, "--", 2)) {
 		/* we hit an end boundary */
 		/* pop the current boundary off the stack */
 		strbuf_release(*content_top);
@@ -852,7 +852,7 @@ again:
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
-		if (!find_boundary())
+		if (!find_boundary(line))
 			return 0;
 		goto again;
 	}
@@ -862,18 +862,18 @@ again:
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 0);
+	while (read_one_header_line(line, fin))
+		check_header(line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
-	if (strbuf_getline(&line, fin, '\n'))
+	if (strbuf_getline(line, fin, '\n'))
 		return 0;
-	strbuf_addch(&line, '\n');
+	strbuf_addch(line, '\n');
 	return 1;
 }
 
-static void handle_body(void)
+static void handle_body(struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
 	int filter_stage = 0;
@@ -881,24 +881,24 @@ static void handle_body(void)
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
-		if (!find_boundary())
+		if (!find_boundary(line))
 			goto handle_body_out;
 	}
 
 	do {
 		/* process any boundary lines */
-		if (*content_top && is_multipart_boundary(&line)) {
+		if (*content_top && is_multipart_boundary(line)) {
 			/* flush any leftover */
 			if (prev.len) {
 				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(&filter_stage, &header_stage))
+			if (!handle_boundary(line, &filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
 		/* Unwrap transfer encoding */
-		decode_transfer_encoding(&line);
+		decode_transfer_encoding(line);
 
 		switch (transfer_encoding) {
 		case TE_BASE64:
@@ -907,7 +907,7 @@ static void handle_body(void)
 			struct strbuf **lines, **it, *sb;
 
 			/* Prepend any previous partial lines */
-			strbuf_insert(&line, 0, prev.buf, prev.len);
+			strbuf_insert(line, 0, prev.buf, prev.len);
 			strbuf_reset(&prev);
 
 			/*
@@ -915,7 +915,7 @@ static void handle_body(void)
 			 * multiple new lines.  Pass only one chunk
 			 * at a time to handle_filter()
 			 */
-			lines = strbuf_split(&line, '\n');
+			lines = strbuf_split(line, '\n');
 			for (it = lines; (sb = *it); it++) {
 				if (*(it + 1) == NULL) /* The last line */
 					if (sb->buf[sb->len - 1] != '\n') {
@@ -933,10 +933,10 @@ static void handle_body(void)
 			break;
 		}
 		default:
-			handle_filter(&line, &filter_stage, &header_stage);
+			handle_filter(line, &filter_stage, &header_stage);
 		}
 
-	} while (!strbuf_getwholeline(&line, fin, '\n'));
+	} while (!strbuf_getwholeline(line, fin, '\n'));
 
 handle_body_out:
 	strbuf_release(&prev);
@@ -1019,10 +1019,10 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 	ungetc(peek, in);
 
 	/* process the email header */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 1);
+	while (read_one_header_line(&line_global, fin))
+		check_header(&line_global, p_hdr_data, 1);
 
-	handle_body();
+	handle_body(&line_global);
 	handle_info();
 
 	return 0;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 07/26] mailinfo: move global "line" into mailinfo() function
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (5 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 06/26] mailinfo: always pass "line" as an argument Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-14 20:27   ` Stefan Beller
  2015-10-13 23:16 ` [PATCH 08/26] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
                   ` (19 subsequent siblings)
  26 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

The mailinfo() function is the only one that wants the "line_global"
to be directly touchable.  Note that handle_body() has to be passed
this strbuf so that it sees the "first line of the input" after the
loop in this function processes the headers.  It feels a bit dirty
that handle_body() then keeps reusing this strbuf to read more lines
and does its processing, but that is how the code is structured now.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index c3c7d67..6c671fb 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,7 +12,6 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
-static struct strbuf line_global = STRBUF_INIT;
 static struct strbuf name = STRBUF_INIT;
 static struct strbuf email = STRBUF_INIT;
 static char *message_id;
@@ -995,6 +994,8 @@ static void handle_info(void)
 static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 {
 	int peek;
+	struct strbuf line = STRBUF_INIT;
+
 	fin = in;
 	fout = out;
 
@@ -1019,10 +1020,10 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 	ungetc(peek, in);
 
 	/* process the email header */
-	while (read_one_header_line(&line_global, fin))
-		check_header(&line_global, p_hdr_data, 1);
+	while (read_one_header_line(&line, fin))
+		check_header(&line, p_hdr_data, 1);
 
-	handle_body(&line_global);
+	handle_body(&line);
 	handle_info();
 
 	return 0;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 08/26] mailinfo: introduce "struct mailinfo" to hold globals
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (6 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 07/26] mailinfo: move global "line" into mailinfo() function Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 09/26] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Initially have only 'email' and 'name' fields in there and remove
the corresponding globals.  In subsequent patches, more globals will
be moved to this and the structure will be passed around as a new
parameter to more functions.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 61 +++++++++++++++++++++++++++++++++---------------------
 1 file changed, 37 insertions(+), 24 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 6c671fb..7b61187 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,8 +12,11 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
-static struct strbuf name = STRBUF_INIT;
-static struct strbuf email = STRBUF_INIT;
+
+struct mailinfo {
+	struct strbuf name;
+	struct strbuf email;
+};
 static char *message_id;
 
 static enum  {
@@ -45,7 +48,7 @@ static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf
 	strbuf_addbuf(out, src);
 }
 
-static void parse_bogus_from(const struct strbuf *line)
+static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
 {
 	/* John Doe <johndoe> */
 
@@ -53,7 +56,7 @@ static void parse_bogus_from(const struct strbuf *line)
 	/* This is fallback, so do not bother if we already have an
 	 * e-mail address.
 	 */
-	if (email.len)
+	if (mi->email.len)
 		return;
 
 	bra = strchr(line->buf, '<');
@@ -63,16 +66,16 @@ static void parse_bogus_from(const struct strbuf *line)
 	if (!ket)
 		return;
 
-	strbuf_reset(&email);
-	strbuf_add(&email, bra + 1, ket - bra - 1);
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, bra + 1, ket - bra - 1);
 
-	strbuf_reset(&name);
-	strbuf_add(&name, line->buf, bra - line->buf);
-	strbuf_trim(&name);
-	get_sane_name(&name, &name, &email);
+	strbuf_reset(&mi->name);
+	strbuf_add(&mi->name, line->buf, bra - line->buf);
+	strbuf_trim(&mi->name);
+	get_sane_name(&mi->name, &mi->name, &mi->email);
 }
 
-static void handle_from(const struct strbuf *from)
+static void handle_from(struct mailinfo *mi, const struct strbuf *from)
 {
 	char *at;
 	size_t el;
@@ -83,14 +86,14 @@ static void handle_from(const struct strbuf *from)
 
 	at = strchr(f.buf, '@');
 	if (!at) {
-		parse_bogus_from(from);
+		parse_bogus_from(mi, from);
 		return;
 	}
 
 	/*
 	 * If we already have one email, don't take any confusing lines
 	 */
-	if (email.len && strchr(at + 1, '@')) {
+	if (mi->email.len && strchr(at + 1, '@')) {
 		strbuf_release(&f);
 		return;
 	}
@@ -109,8 +112,8 @@ static void handle_from(const struct strbuf *from)
 		at--;
 	}
 	el = strcspn(at, " \n\t\r\v\f>");
-	strbuf_reset(&email);
-	strbuf_add(&email, at, el);
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, at, el);
 	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
 
 	/* The remainder is name.  It could be
@@ -132,7 +135,7 @@ static void handle_from(const struct strbuf *from)
 		strbuf_setlen(&f, f.len - 1);
 	}
 
-	get_sane_name(&name, &f, &email);
+	get_sane_name(&mi->name, &f, &mi->email);
 	strbuf_release(&f);
 }
 
@@ -958,7 +961,7 @@ static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf
 	}
 }
 
-static void handle_info(void)
+static void handle_info(struct mailinfo *mi)
 {
 	struct strbuf *hdr;
 	int i;
@@ -980,9 +983,9 @@ static void handle_info(void)
 			output_header_lines(fout, "Subject", hdr);
 		} else if (!strcmp(header[i], "From")) {
 			cleanup_space(hdr);
-			handle_from(hdr);
-			fprintf(fout, "Author: %s\n", name.buf);
-			fprintf(fout, "Email: %s\n", email.buf);
+			handle_from(mi, hdr);
+			fprintf(fout, "Author: %s\n", mi->name.buf);
+			fprintf(fout, "Email: %s\n", mi->email.buf);
 		} else {
 			cleanup_space(hdr);
 			fprintf(fout, "%s: %s\n", header[i], hdr->buf);
@@ -991,7 +994,8 @@ static void handle_info(void)
 	fprintf(fout, "\n");
 }
 
-static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
+static int mailinfo(struct mailinfo *mi,
+		    FILE *in, FILE *out, const char *msg, const char *patch)
 {
 	int peek;
 	struct strbuf line = STRBUF_INIT;
@@ -1024,7 +1028,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 		check_header(&line, p_hdr_data, 1);
 
 	handle_body(&line);
-	handle_info();
+	handle_info(mi);
 
 	return 0;
 }
@@ -1041,17 +1045,26 @@ static int git_mailinfo_config(const char *var, const char *value, void *unused)
 	return 0;
 }
 
+static void setup_mailinfo(struct mailinfo *mi)
+{
+	memset(mi, 0, sizeof(*mi));
+	strbuf_init(&mi->name, 0);
+	strbuf_init(&mi->email, 0);
+	git_config(git_mailinfo_config, &mi);
+}
+
 static const char mailinfo_usage[] =
 	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
 
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
 	const char *def_charset;
+	struct mailinfo mi;
 
 	/* NEEDSWORK: might want to do the optional .git/ directory
 	 * discovery
 	 */
-	git_config(git_mailinfo_config, NULL);
+	setup_mailinfo(&mi);
 
 	def_charset = get_commit_output_encoding();
 	metainfo_charset = def_charset;
@@ -1083,5 +1096,5 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	if (argc != 3)
 		usage(mailinfo_usage);
 
-	return !!mailinfo(stdin, stdout, argv[1], argv[2]);
+	return !!mailinfo(&mi, stdin, stdout, argv[1], argv[2]);
 }
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 09/26] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (7 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 08/26] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 10/26] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

These two are the only easy ones that do not require passing the
structure around to deep corners of the callchain.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 7b61187..d642b0d 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -9,13 +9,13 @@
 
 static FILE *cmitmsg, *patchfile, *fin, *fout;
 
-static int keep_subject;
-static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
 
 struct mailinfo {
 	struct strbuf name;
 	struct strbuf email;
+	int keep_subject;
+	int keep_non_patch_brackets_in_subject;
 };
 static char *message_id;
 
@@ -224,7 +224,7 @@ static int is_multipart_boundary(const struct strbuf *line)
 		!memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
 }
 
-static void cleanup_subject(struct strbuf *subject)
+static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
 {
 	size_t at = 0;
 
@@ -252,7 +252,7 @@ static void cleanup_subject(struct strbuf *subject)
 			if (!pos)
 				break;
 			remove = pos - subject->buf + at + 1;
-			if (!keep_non_patch_brackets_in_subject ||
+			if (!mi->keep_non_patch_brackets_in_subject ||
 			    (7 <= remove &&
 			     memmem(subject->buf + at, remove, "PATCH", 5)))
 				strbuf_remove(subject, at, remove);
@@ -976,8 +976,8 @@ static void handle_info(struct mailinfo *mi)
 			continue;
 
 		if (!strcmp(header[i], "Subject")) {
-			if (!keep_subject) {
-				cleanup_subject(hdr);
+			if (!mi->keep_subject) {
+				cleanup_subject(mi, hdr);
 				cleanup_space(hdr);
 			}
 			output_header_lines(fout, "Subject", hdr);
@@ -1071,9 +1071,9 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 
 	while (1 < argc && argv[1][0] == '-') {
 		if (!strcmp(argv[1], "-k"))
-			keep_subject = 1;
+			mi.keep_subject = 1;
 		else if (!strcmp(argv[1], "-b"))
-			keep_non_patch_brackets_in_subject = 1;
+			mi.keep_non_patch_brackets_in_subject = 1;
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
 			add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 10/26] mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (8 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 09/26] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 11/26] mailinfo: move filter/header stage " Junio C Hamano
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

This requires us to pass "struct mailinfo" to more functions
throughout the codepath that read input lines, which makes
later steps easier.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 54 ++++++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 26 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index d642b0d..a9da338 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,11 +7,14 @@
 #include "utf8.h"
 #include "strbuf.h"
 
-static FILE *cmitmsg, *patchfile, *fin, *fout;
+static FILE *cmitmsg, *patchfile;
 
 static const char *metainfo_charset;
 
 struct mailinfo {
+	FILE *input;
+	FILE *output;
+
 	struct strbuf name;
 	struct strbuf email;
 	int keep_subject;
@@ -819,16 +822,17 @@ static void handle_filter(struct strbuf *line, int *filter_stage, int *header_st
 	}
 }
 
-static int find_boundary(struct strbuf *line)
+static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 {
-	while (!strbuf_getline(line, fin, '\n')) {
+	while (!strbuf_getline(line, mi->input, '\n')) {
 		if (*content_top && is_multipart_boundary(line))
 			return 1;
 	}
 	return 0;
 }
 
-static int handle_boundary(struct strbuf *line, int *filter_stage, int *header_stage)
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line,
+			   int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -854,7 +858,7 @@ again:
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
-		if (!find_boundary(line))
+		if (!find_boundary(mi, line))
 			return 0;
 		goto again;
 	}
@@ -864,18 +868,18 @@ again:
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
-	while (read_one_header_line(line, fin))
+	while (read_one_header_line(line, mi->input))
 		check_header(line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
-	if (strbuf_getline(line, fin, '\n'))
+	if (strbuf_getline(line, mi->input, '\n'))
 		return 0;
 	strbuf_addch(line, '\n');
 	return 1;
 }
 
-static void handle_body(struct strbuf *line)
+static void handle_body(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
 	int filter_stage = 0;
@@ -883,7 +887,7 @@ static void handle_body(struct strbuf *line)
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
-		if (!find_boundary(line))
+		if (!find_boundary(mi, line))
 			goto handle_body_out;
 	}
 
@@ -895,7 +899,7 @@ static void handle_body(struct strbuf *line)
 				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(line, &filter_stage, &header_stage))
+			if (!handle_boundary(mi, line, &filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
@@ -938,7 +942,7 @@ static void handle_body(struct strbuf *line)
 			handle_filter(line, &filter_stage, &header_stage);
 		}
 
-	} while (!strbuf_getwholeline(line, fin, '\n'));
+	} while (!strbuf_getwholeline(line, mi->input, '\n'));
 
 handle_body_out:
 	strbuf_release(&prev);
@@ -980,29 +984,25 @@ static void handle_info(struct mailinfo *mi)
 				cleanup_subject(mi, hdr);
 				cleanup_space(hdr);
 			}
-			output_header_lines(fout, "Subject", hdr);
+			output_header_lines(mi->output, "Subject", hdr);
 		} else if (!strcmp(header[i], "From")) {
 			cleanup_space(hdr);
 			handle_from(mi, hdr);
-			fprintf(fout, "Author: %s\n", mi->name.buf);
-			fprintf(fout, "Email: %s\n", mi->email.buf);
+			fprintf(mi->output, "Author: %s\n", mi->name.buf);
+			fprintf(mi->output, "Email: %s\n", mi->email.buf);
 		} else {
 			cleanup_space(hdr);
-			fprintf(fout, "%s: %s\n", header[i], hdr->buf);
+			fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
 		}
 	}
-	fprintf(fout, "\n");
+	fprintf(mi->output, "\n");
 }
 
-static int mailinfo(struct mailinfo *mi,
-		    FILE *in, FILE *out, const char *msg, const char *patch)
+static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 {
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	fin = in;
-	fout = out;
-
 	cmitmsg = fopen(msg, "w");
 	if (!cmitmsg) {
 		perror(msg);
@@ -1019,15 +1019,15 @@ static int mailinfo(struct mailinfo *mi,
 	s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
 
 	do {
-		peek = fgetc(in);
+		peek = fgetc(mi->input);
 	} while (isspace(peek));
-	ungetc(peek, in);
+	ungetc(peek, mi->input);
 
 	/* process the email header */
-	while (read_one_header_line(&line, fin))
+	while (read_one_header_line(&line, mi->input))
 		check_header(&line, p_hdr_data, 1);
 
-	handle_body(&line);
+	handle_body(mi, &line);
 	handle_info(mi);
 
 	return 0;
@@ -1096,5 +1096,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	if (argc != 3)
 		usage(mailinfo_usage);
 
-	return !!mailinfo(&mi, stdin, stdout, argv[1], argv[2]);
+	mi.input = stdin;
+	mi.output = stdout;
+	return !!mailinfo(&mi, argv[1], argv[2]);
 }
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 11/26] mailinfo: move filter/header stage to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (9 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 10/26] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 12/26] mailinfo: move patch_lines " Junio C Hamano
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Earlier we got rid of two function-scope static variables that kept
track of the states of helper functions by making them extra arguments
that are passed throughout the callchain.  Now we have a convenient
place to store and pass them around in the form of "struct mailinfo",
change them into two fields in the struct.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 50 ++++++++++++++++++++++++++------------------------
 1 file changed, 26 insertions(+), 24 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index a9da338..7e39769 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -19,6 +19,9 @@ struct mailinfo {
 	struct strbuf email;
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
+
+	int filter_stage; /* still reading log or are we copying patch? */
+	int header_stage; /* still checking in-body headers? */
 };
 static char *message_id;
 
@@ -28,6 +31,7 @@ static enum  {
 
 static struct strbuf charset = STRBUF_INIT;
 static int patch_lines;
+
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
 static int add_message_id;
@@ -718,7 +722,7 @@ static int is_scissors_line(const struct strbuf *line)
 		gap * 2 < perforation);
 }
 
-static int handle_commit_msg(struct strbuf *line, int *still_looking)
+static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 {
 	/*
 	 * Are we still scanning and discarding in-body headers?
@@ -731,7 +735,7 @@ static int handle_commit_msg(struct strbuf *line, int *still_looking)
 		return 0;
 
 	is_empty_line = (!line->len || (line->len == 1 && line->buf[0] == '\n'));
-	if (*still_looking == 1) {
+	if (mi->header_stage == 1) {
 		/*
 		 * Haven't seen a known in-body header; discard an empty line.
 		 */
@@ -739,33 +743,33 @@ static int handle_commit_msg(struct strbuf *line, int *still_looking)
 			return 0;
 	}
 
-	if (use_inbody_headers && *still_looking) {
+	if (use_inbody_headers && mi->header_stage) {
 		int is_known_header = check_header(line, s_hdr_data, 0);
 
-		if (*still_looking == 2) {
+		if (mi->header_stage == 2) {
 			/*
 			 * an empty line after the in-body header block,
 			 * or a line obviously not an attempt to invent
 			 * an unsupported in-body header.
 			 */
 			if (is_empty_line || !is_rfc2822_header(line))
-				*still_looking = 0;
+				mi->header_stage = 0;
 			if (is_empty_line)
 				return 0;
 			/* otherwise do not discard the line, but keep going */
 		} else if (is_known_header) {
-			*still_looking = 2;
-		} else if (*still_looking != 2) {
-			*still_looking = 0;
+			mi->header_stage = 2;
+		} else if (mi->header_stage != 2) {
+			mi->header_stage = 0;
 		}
 
-		if (*still_looking)
+		if (mi->header_stage)
 			return 0;
 	} else
 		/* Only trim the first (blank) line of the commit message
 		 * when ignoring in-body headers.
 		 */
-		*still_looking = 0;
+		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
 	if (metainfo_charset)
@@ -777,7 +781,7 @@ static int handle_commit_msg(struct strbuf *line, int *still_looking)
 			die_errno("Could not rewind output message file");
 		if (ftruncate(fileno(cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
-		*still_looking = 1;
+		mi->header_stage = 1;
 
 		/*
 		 * We may have already read "secondary headers"; purge
@@ -809,13 +813,13 @@ static void handle_patch(const struct strbuf *line)
 	patch_lines++;
 }
 
-static void handle_filter(struct strbuf *line, int *filter_stage, int *header_stage)
+static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 {
-	switch (*filter_stage) {
+	switch (mi->filter_stage) {
 	case 0:
-		if (!handle_commit_msg(line, header_stage))
+		if (!handle_commit_msg(mi, line))
 			break;
-		(*filter_stage)++;
+		mi->filter_stage++;
 	case 1:
 		handle_patch(line);
 		break;
@@ -831,8 +835,7 @@ static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 	return 0;
 }
 
-static int handle_boundary(struct mailinfo *mi, struct strbuf *line,
-			   int *filter_stage, int *header_stage)
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -854,7 +857,7 @@ again:
 					"can't recover\n");
 			exit(1);
 		}
-		handle_filter(&newline, filter_stage, header_stage);
+		handle_filter(mi, &newline);
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
@@ -882,8 +885,6 @@ again:
 static void handle_body(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
-	int filter_stage = 0;
-	int header_stage = 1;
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
@@ -896,10 +897,10 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 		if (*content_top && is_multipart_boundary(line)) {
 			/* flush any leftover */
 			if (prev.len) {
-				handle_filter(&prev, &filter_stage, &header_stage);
+				handle_filter(mi, &prev);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(mi, line, &filter_stage, &header_stage))
+			if (!handle_boundary(mi, line))
 				goto handle_body_out;
 		}
 
@@ -929,7 +930,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 						strbuf_addbuf(&prev, sb);
 						break;
 					}
-				handle_filter(sb, &filter_stage, &header_stage);
+				handle_filter(mi, sb);
 			}
 			/*
 			 * The partial chunk is saved in "prev" and will be
@@ -939,7 +940,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 			break;
 		}
 		default:
-			handle_filter(line, &filter_stage, &header_stage);
+			handle_filter(mi, line);
 		}
 
 	} while (!strbuf_getwholeline(line, mi->input, '\n'));
@@ -1050,6 +1051,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	memset(mi, 0, sizeof(*mi));
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
+	mi->header_stage = 1;
 	git_config(git_mailinfo_config, &mi);
 }
 
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 12/26] mailinfo: move patch_lines to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (10 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 11/26] mailinfo: move filter/header stage " Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 13/26] mailinfo: move add_message_id and message_id " Junio C Hamano
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

This one is trivial thanks to previous steps that started passing
the structure throughout the input codepaths.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 7e39769..163032e 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -20,6 +20,7 @@ struct mailinfo {
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
 
+	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
@@ -30,7 +31,6 @@ static enum  {
 } transfer_encoding;
 
 static struct strbuf charset = STRBUF_INIT;
-static int patch_lines;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
@@ -807,10 +807,10 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	return 0;
 }
 
-static void handle_patch(const struct strbuf *line)
+static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
 {
 	fwrite(line->buf, 1, line->len, patchfile);
-	patch_lines++;
+	mi->patch_lines++;
 }
 
 static void handle_filter(struct mailinfo *mi, struct strbuf *line)
@@ -821,7 +821,7 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 			break;
 		mi->filter_stage++;
 	case 1:
-		handle_patch(line);
+		handle_patch(mi, line);
 		break;
 	}
 }
@@ -973,7 +973,7 @@ static void handle_info(struct mailinfo *mi)
 
 	for (i = 0; header[i]; i++) {
 		/* only print inbody headers if we output a patch file */
-		if (patch_lines && s_hdr_data[i])
+		if (mi->patch_lines && s_hdr_data[i])
 			hdr = s_hdr_data[i];
 		else if (p_hdr_data[i])
 			hdr = p_hdr_data[i];
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 13/26] mailinfo: move add_message_id and message_id to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (11 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 12/26] mailinfo: move patch_lines " Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 14/26] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

This requires us to pass the structure into check_header() codepath.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 163032e..35e1ab9 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -19,12 +19,13 @@ struct mailinfo {
 	struct strbuf email;
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
+	int add_message_id;
 
+	char *message_id;
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
-static char *message_id;
 
 static enum  {
 	TE_DONTCARE, TE_QP, TE_BASE64
@@ -34,7 +35,6 @@ static struct strbuf charset = STRBUF_INIT;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
-static int add_message_id;
 static int use_inbody_headers = 1;
 
 #define MAX_HDR_PARSED 10
@@ -209,10 +209,10 @@ static void handle_content_type(struct strbuf *line)
 	}
 }
 
-static void handle_message_id(const struct strbuf *line)
+static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
 {
-	if (add_message_id)
-		message_id = strdup(line->buf);
+	if (mi->add_message_id)
+		mi->message_id = strdup(line->buf);
 }
 
 static void handle_content_transfer_encoding(const struct strbuf *line)
@@ -321,11 +321,13 @@ static int is_format_patch_separator(const char *line, int len)
 	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
 }
 
-static int check_header(const struct strbuf *line,
-				struct strbuf *hdr_data[], int overwrite)
+static int check_header(struct mailinfo *mi,
+			const struct strbuf *line,
+			struct strbuf *hdr_data[], int overwrite)
 {
 	int i, ret = 0, len;
 	struct strbuf sb = STRBUF_INIT;
+
 	/* search for the interesting parts */
 	for (i = 0; header[i]; i++) {
 		int len = strlen(header[i]);
@@ -363,7 +365,7 @@ static int check_header(const struct strbuf *line,
 		len = strlen("Message-Id: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(&sb);
-		handle_message_id(&sb);
+		handle_message_id(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -744,7 +746,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (use_inbody_headers && mi->header_stage) {
-		int is_known_header = check_header(line, s_hdr_data, 0);
+		int is_known_header = check_header(mi, line, s_hdr_data, 0);
 
 		if (mi->header_stage == 2) {
 			/*
@@ -796,8 +798,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (patchbreak(line)) {
-		if (message_id)
-			fprintf(cmitmsg, "Message-Id: %s\n", message_id);
+		if (mi->message_id)
+			fprintf(cmitmsg, "Message-Id: %s\n", mi->message_id);
 		fclose(cmitmsg);
 		cmitmsg = NULL;
 		return 1;
@@ -872,7 +874,7 @@ again:
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
-		check_header(line, p_hdr_data, 0);
+		check_header(mi, line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
@@ -1026,7 +1028,7 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
 	/* process the email header */
 	while (read_one_header_line(&line, mi->input))
-		check_header(&line, p_hdr_data, 1);
+		check_header(mi, &line, p_hdr_data, 1);
 
 	handle_body(mi, &line);
 	handle_info(mi);
@@ -1077,7 +1079,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-b"))
 			mi.keep_non_patch_brackets_in_subject = 1;
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
-			add_message_id = 1;
+			mi.add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
 			metainfo_charset = def_charset;
 		else if (!strcmp(argv[1], "-n"))
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 14/26] mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (12 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 13/26] mailinfo: move add_message_id and message_id " Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 15/26] mailinfo: move metainfo_charset " Junio C Hamano
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 35e1ab9..5302c03 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -20,6 +20,8 @@ struct mailinfo {
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
 	int add_message_id;
+	int use_scissors;
+	int use_inbody_headers; /* defaults to 1 */
 
 	char *message_id;
 	int patch_lines;
@@ -34,8 +36,6 @@ static enum  {
 static struct strbuf charset = STRBUF_INIT;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
-static int use_scissors;
-static int use_inbody_headers = 1;
 
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
@@ -745,7 +745,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 			return 0;
 	}
 
-	if (use_inbody_headers && mi->header_stage) {
+	if (mi->use_inbody_headers && mi->header_stage) {
 		int is_known_header = check_header(mi, line, s_hdr_data, 0);
 
 		if (mi->header_stage == 2) {
@@ -777,7 +777,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	if (metainfo_charset)
 		convert_to_utf8(line, charset.buf);
 
-	if (use_scissors && is_scissors_line(line)) {
+	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
 		if (fseek(cmitmsg, 0L, SEEK_SET))
 			die_errno("Could not rewind output message file");
@@ -1036,12 +1036,14 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 	return 0;
 }
 
-static int git_mailinfo_config(const char *var, const char *value, void *unused)
+static int git_mailinfo_config(const char *var, const char *value, void *mi_)
 {
+	struct mailinfo *mi = mi_;
+
 	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, unused);
+		return git_default_config(var, value, NULL);
 	if (!strcmp(var, "mailinfo.scissors")) {
-		use_scissors = git_config_bool(var, value);
+		mi->use_scissors = git_config_bool(var, value);
 		return 0;
 	}
 	/* perhaps others here */
@@ -1054,6 +1056,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
 	mi->header_stage = 1;
+	mi->use_inbody_headers = 1;
 	git_config(git_mailinfo_config, &mi);
 }
 
@@ -1087,11 +1090,11 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (starts_with(argv[1], "--encoding="))
 			metainfo_charset = argv[1] + 11;
 		else if (!strcmp(argv[1], "--scissors"))
-			use_scissors = 1;
+			mi.use_scissors = 1;
 		else if (!strcmp(argv[1], "--no-scissors"))
-			use_scissors = 0;
+			mi.use_scissors = 0;
 		else if (!strcmp(argv[1], "--no-inbody-headers"))
-			use_inbody_headers = 0;
+			mi.use_inbody_headers = 0;
 		else
 			usage(mailinfo_usage);
 		argc--; argv++;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 15/26] mailinfo: move metainfo_charset to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (13 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 14/26] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 16/26] mailinfo: move transfer_encoding " Junio C Hamano
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

This requires us to pass the struct down to decode_header() and
convert_to_utf8() callchain.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 40 +++++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 5302c03..7e01efa 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -9,8 +9,6 @@
 
 static FILE *cmitmsg, *patchfile;
 
-static const char *metainfo_charset;
-
 struct mailinfo {
 	FILE *input;
 	FILE *output;
@@ -22,6 +20,7 @@ struct mailinfo {
 	int add_message_id;
 	int use_scissors;
 	int use_inbody_headers; /* defaults to 1 */
+	const char *metainfo_charset;
 
 	char *message_id;
 	int patch_lines;
@@ -293,7 +292,7 @@ static void cleanup_space(struct strbuf *sb)
 	}
 }
 
-static void decode_header(struct strbuf *line);
+static void decode_header(struct mailinfo *mi, struct strbuf *line);
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
@@ -336,7 +335,7 @@ static int check_header(struct mailinfo *mi,
 			 * normalize the meta information to utf8.
 			 */
 			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(&sb);
+			decode_header(mi, &sb);
 			handle_header(&hdr_data[i], &sb);
 			ret = 1;
 			goto check_header_out;
@@ -347,7 +346,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Content-Type")) {
 		len = strlen("Content-Type: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		strbuf_insert(&sb, 0, "Content-Type: ", len);
 		handle_content_type(&sb);
 		ret = 1;
@@ -356,7 +355,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Content-Transfer-Encoding")) {
 		len = strlen("Content-Transfer-Encoding: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		handle_content_transfer_encoding(&sb);
 		ret = 1;
 		goto check_header_out;
@@ -364,7 +363,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Message-Id")) {
 		len = strlen("Message-Id: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		handle_message_id(mi, &sb);
 		ret = 1;
 		goto check_header_out;
@@ -520,23 +519,24 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
 	return out;
 }
 
-static void convert_to_utf8(struct strbuf *line, const char *charset)
+static void convert_to_utf8(struct mailinfo *mi,
+			    struct strbuf *line, const char *charset)
 {
 	char *out;
 
-	if (!charset || !*charset)
+	if (!mi->metainfo_charset || !charset || !*charset)
 		return;
 
-	if (same_encoding(metainfo_charset, charset))
+	if (same_encoding(mi->metainfo_charset, charset))
 		return;
-	out = reencode_string(line->buf, metainfo_charset, charset);
+	out = reencode_string(line->buf, mi->metainfo_charset, charset);
 	if (!out)
 		die("cannot convert from %s to %s",
-		    charset, metainfo_charset);
+		    charset, mi->metainfo_charset);
 	strbuf_attach(line, out, strlen(out), strlen(out));
 }
 
-static void decode_header(struct strbuf *it)
+static void decode_header(struct mailinfo *mi, struct strbuf *it)
 {
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
@@ -599,8 +599,7 @@ static void decode_header(struct strbuf *it)
 			dec = decode_q_segment(&piecebuf, 1);
 			break;
 		}
-		if (metainfo_charset)
-			convert_to_utf8(dec, charset_q.buf);
+		convert_to_utf8(mi, dec, charset_q.buf);
 
 		strbuf_addbuf(&outbuf, dec);
 		strbuf_release(dec);
@@ -774,8 +773,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	if (metainfo_charset)
-		convert_to_utf8(line, charset.buf);
+	convert_to_utf8(mi, line, charset.buf);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -1074,7 +1072,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	setup_mailinfo(&mi);
 
 	def_charset = get_commit_output_encoding();
-	metainfo_charset = def_charset;
+	mi.metainfo_charset = def_charset;
 
 	while (1 < argc && argv[1][0] == '-') {
 		if (!strcmp(argv[1], "-k"))
@@ -1084,11 +1082,11 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
 			mi.add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
-			metainfo_charset = def_charset;
+			mi.metainfo_charset = def_charset;
 		else if (!strcmp(argv[1], "-n"))
-			metainfo_charset = NULL;
+			mi.metainfo_charset = NULL;
 		else if (starts_with(argv[1], "--encoding="))
-			metainfo_charset = argv[1] + 11;
+			mi.metainfo_charset = argv[1] + 11;
 		else if (!strcmp(argv[1], "--scissors"))
 			mi.use_scissors = 1;
 		else if (!strcmp(argv[1], "--no-scissors"))
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 16/26] mailinfo: move transfer_encoding to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (14 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 15/26] mailinfo: move metainfo_charset " Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 17/26] mailinfo: move charset " Junio C Hamano
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 7e01efa..fbfa27e 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -23,14 +23,14 @@ struct mailinfo {
 	const char *metainfo_charset;
 
 	char *message_id;
+	enum  {
+		TE_DONTCARE, TE_QP, TE_BASE64
+	} transfer_encoding;
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
 
-static enum  {
-	TE_DONTCARE, TE_QP, TE_BASE64
-} transfer_encoding;
 
 static struct strbuf charset = STRBUF_INIT;
 
@@ -214,14 +214,15 @@ static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
 		mi->message_id = strdup(line->buf);
 }
 
-static void handle_content_transfer_encoding(const struct strbuf *line)
+static void handle_content_transfer_encoding(struct mailinfo *mi,
+					     const struct strbuf *line)
 {
 	if (strcasestr(line->buf, "base64"))
-		transfer_encoding = TE_BASE64;
+		mi->transfer_encoding = TE_BASE64;
 	else if (strcasestr(line->buf, "quoted-printable"))
-		transfer_encoding = TE_QP;
+		mi->transfer_encoding = TE_QP;
 	else
-		transfer_encoding = TE_DONTCARE;
+		mi->transfer_encoding = TE_DONTCARE;
 }
 
 static int is_multipart_boundary(const struct strbuf *line)
@@ -356,7 +357,7 @@ static int check_header(struct mailinfo *mi,
 		len = strlen("Content-Transfer-Encoding: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(mi, &sb);
-		handle_content_transfer_encoding(&sb);
+		handle_content_transfer_encoding(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -615,11 +616,11 @@ release_return:
 	strbuf_release(&piecebuf);
 }
 
-static void decode_transfer_encoding(struct strbuf *line)
+static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *ret;
 
-	switch (transfer_encoding) {
+	switch (mi->transfer_encoding) {
 	case TE_QP:
 		ret = decode_q_segment(line, 0);
 		break;
@@ -867,7 +868,7 @@ again:
 	}
 
 	/* set some defaults */
-	transfer_encoding = TE_DONTCARE;
+	mi->transfer_encoding = TE_DONTCARE;
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
@@ -905,9 +906,9 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 		}
 
 		/* Unwrap transfer encoding */
-		decode_transfer_encoding(line);
+		decode_transfer_encoding(mi, line);
 
-		switch (transfer_encoding) {
+		switch (mi->transfer_encoding) {
 		case TE_BASE64:
 		case TE_QP:
 		{
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 17/26] mailinfo: move charset to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (15 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 16/26] mailinfo: move transfer_encoding " Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 18/26] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index fbfa27e..a51b2c5 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -22,6 +22,7 @@ struct mailinfo {
 	int use_inbody_headers; /* defaults to 1 */
 	const char *metainfo_charset;
 
+	struct strbuf charset;
 	char *message_id;
 	enum  {
 		TE_DONTCARE, TE_QP, TE_BASE64
@@ -31,9 +32,6 @@ struct mailinfo {
 	int header_stage; /* still checking in-body headers? */
 };
 
-
-static struct strbuf charset = STRBUF_INIT;
-
 static struct strbuf **p_hdr_data, **s_hdr_data;
 
 #define MAX_HDR_PARSED 10
@@ -186,7 +184,7 @@ static struct strbuf *content[MAX_BOUNDARIES];
 
 static struct strbuf **content_top = content;
 
-static void handle_content_type(struct strbuf *line)
+static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
 	strbuf_init(boundary, line->len);
@@ -200,7 +198,7 @@ static void handle_content_type(struct strbuf *line)
 		*content_top = boundary;
 		boundary = NULL;
 	}
-	slurp_attr(line->buf, "charset=", &charset);
+	slurp_attr(line->buf, "charset=", &mi->charset);
 
 	if (boundary) {
 		strbuf_release(boundary);
@@ -349,7 +347,7 @@ static int check_header(struct mailinfo *mi,
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(mi, &sb);
 		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(&sb);
+		handle_content_type(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -774,7 +772,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	convert_to_utf8(mi, line, charset.buf);
+	convert_to_utf8(mi, line, mi->charset.buf);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -869,7 +867,7 @@ again:
 
 	/* set some defaults */
 	mi->transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&charset);
+	strbuf_reset(&mi->charset);
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
@@ -1054,6 +1052,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	memset(mi, 0, sizeof(*mi));
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
+	strbuf_init(&mi->charset, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
 	git_config(git_mailinfo_config, &mi);
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 18/26] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (16 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 17/26] mailinfo: move charset " Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 19/26] mailinfo: move cmitmsg and patchfile to struct mailinfo Junio C Hamano
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

There is a strange "if (!cmitmsg) return 0" at the very beginning of
handle_commit_msg(), but the condition should never trigger,
because:

 * The only place cmitmsg is set to NULL is after this function sees
   a patch break, closes the FILE * to write the commit log message
   and returns 1.  This function returns non-zero only from that
   codepath.

 * The caller of this function, upon seeing a non-zero return,
   increments filter_stage, starts treating the input as patch text
   and will never call handle_commit_msg() again.

Replace it with an assert(!mi->filter_stage).

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index a51b2c5..256d04a 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -731,8 +731,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	 */
 	int is_empty_line;
 
-	if (!cmitmsg)
-		return 0;
+	assert(!mi->filter_stage);
 
 	is_empty_line = (!line->len || (line->len == 1 && line->buf[0] == '\n'));
 	if (mi->header_stage == 1) {
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 19/26] mailinfo: move cmitmsg and patchfile to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (17 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 18/26] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-14  1:37   ` [PATCH 27/26] mailinfo: close patchfile Junio C Hamano
  2015-10-13 23:16 ` [PATCH 20/26] mailinfo: move [ps]_hdr_data to struct mailinfo Junio C Hamano
                   ` (7 subsequent siblings)
  26 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 256d04a..b591a2f 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,11 +7,11 @@
 #include "utf8.h"
 #include "strbuf.h"
 
-static FILE *cmitmsg, *patchfile;
-
 struct mailinfo {
 	FILE *input;
 	FILE *output;
+	FILE *cmitmsg;
+	FILE *patchfile;
 
 	struct strbuf name;
 	struct strbuf email;
@@ -775,9 +775,9 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-		if (fseek(cmitmsg, 0L, SEEK_SET))
+		if (fseek(mi->cmitmsg, 0L, SEEK_SET))
 			die_errno("Could not rewind output message file");
-		if (ftruncate(fileno(cmitmsg), 0))
+		if (ftruncate(fileno(mi->cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
 		mi->header_stage = 1;
 
@@ -795,19 +795,19 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (patchbreak(line)) {
 		if (mi->message_id)
-			fprintf(cmitmsg, "Message-Id: %s\n", mi->message_id);
-		fclose(cmitmsg);
-		cmitmsg = NULL;
+			fprintf(mi->cmitmsg, "Message-Id: %s\n", mi->message_id);
+		fclose(mi->cmitmsg);
+		mi->cmitmsg = NULL;
 		return 1;
 	}
 
-	fputs(line->buf, cmitmsg);
+	fputs(line->buf, mi->cmitmsg);
 	return 0;
 }
 
 static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
 {
-	fwrite(line->buf, 1, line->len, patchfile);
+	fwrite(line->buf, 1, line->len, mi->patchfile);
 	mi->patch_lines++;
 }
 
@@ -1002,15 +1002,15 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	cmitmsg = fopen(msg, "w");
-	if (!cmitmsg) {
+	mi->cmitmsg = fopen(msg, "w");
+	if (!mi->cmitmsg) {
 		perror(msg);
 		return -1;
 	}
-	patchfile = fopen(patch, "w");
-	if (!patchfile) {
+	mi->patchfile = fopen(patch, "w");
+	if (!mi->patchfile) {
 		perror(patch);
-		fclose(cmitmsg);
+		fclose(mi->cmitmsg);
 		return -1;
 	}
 
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 20/26] mailinfo: move [ps]_hdr_data to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (18 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 19/26] mailinfo: move cmitmsg and patchfile to struct mailinfo Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 21/26] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index b591a2f..c9629c8 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -30,10 +30,10 @@ struct mailinfo {
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
+	struct strbuf **p_hdr_data;
+	struct strbuf **s_hdr_data;
 };
 
-static struct strbuf **p_hdr_data, **s_hdr_data;
-
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
 
@@ -743,7 +743,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (mi->use_inbody_headers && mi->header_stage) {
-		int is_known_header = check_header(mi, line, s_hdr_data, 0);
+		int is_known_header = check_header(mi, line, mi->s_hdr_data, 0);
 
 		if (mi->header_stage == 2) {
 			/*
@@ -786,9 +786,9 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		 * them to give ourselves a clean restart.
 		 */
 		for (i = 0; header[i]; i++) {
-			if (s_hdr_data[i])
-				strbuf_release(s_hdr_data[i]);
-			s_hdr_data[i] = NULL;
+			if (mi->s_hdr_data[i])
+				strbuf_release(mi->s_hdr_data[i]);
+			mi->s_hdr_data[i] = NULL;
 		}
 		return 0;
 	}
@@ -870,7 +870,7 @@ again:
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
-		check_header(mi, line, p_hdr_data, 0);
+		check_header(mi, line, mi->p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
@@ -971,10 +971,10 @@ static void handle_info(struct mailinfo *mi)
 
 	for (i = 0; header[i]; i++) {
 		/* only print inbody headers if we output a patch file */
-		if (mi->patch_lines && s_hdr_data[i])
-			hdr = s_hdr_data[i];
-		else if (p_hdr_data[i])
-			hdr = p_hdr_data[i];
+		if (mi->patch_lines && mi->s_hdr_data[i])
+			hdr = mi->s_hdr_data[i];
+		else if (mi->p_hdr_data[i])
+			hdr = mi->p_hdr_data[i];
 		else
 			continue;
 
@@ -1014,8 +1014,8 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 		return -1;
 	}
 
-	p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*p_hdr_data));
-	s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
+	mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+	mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
 
 	do {
 		peek = fgetc(mi->input);
@@ -1024,7 +1024,7 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
 	/* process the email header */
 	while (read_one_header_line(&line, mi->input))
-		check_header(mi, &line, p_hdr_data, 1);
+		check_header(mi, &line, mi->p_hdr_data, 1);
 
 	handle_body(mi, &line);
 	handle_info(mi);
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 21/26] mailinfo: keep the parsed log message in a strbuf
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (19 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 20/26] mailinfo: move [ps]_hdr_data to struct mailinfo Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 22/26] mailinfo: move content/content_top to struct mailinfo Junio C Hamano
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

When mailinfo() is eventually libified, the calling "git am" still
will have to write out the log message in the "msg" file for hooks
and other users of the information, but at least it does not have to
reopen and reread what was written back if the function kept it in a
strbuf so that the caller can read it from there.

This also removes the need for seeking and truncating the output
file when we see a scissors mark in the input, which in turn reduces
two callsites of die_errno().

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 41 ++++++++++++++++++++++++++++-------------
 1 file changed, 28 insertions(+), 13 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index c9629c8..d38d716 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -10,7 +10,6 @@
 struct mailinfo {
 	FILE *input;
 	FILE *output;
-	FILE *cmitmsg;
 	FILE *patchfile;
 
 	struct strbuf name;
@@ -32,6 +31,8 @@ struct mailinfo {
 	int header_stage; /* still checking in-body headers? */
 	struct strbuf **p_hdr_data;
 	struct strbuf **s_hdr_data;
+
+	struct strbuf log_message;
 };
 
 #define MAX_HDR_PARSED 10
@@ -775,10 +776,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-		if (fseek(mi->cmitmsg, 0L, SEEK_SET))
-			die_errno("Could not rewind output message file");
-		if (ftruncate(fileno(mi->cmitmsg), 0))
-			die_errno("Could not truncate output message file at scissors");
+
+		strbuf_setlen(&mi->log_message, 0);
 		mi->header_stage = 1;
 
 		/*
@@ -795,13 +794,12 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (patchbreak(line)) {
 		if (mi->message_id)
-			fprintf(mi->cmitmsg, "Message-Id: %s\n", mi->message_id);
-		fclose(mi->cmitmsg);
-		mi->cmitmsg = NULL;
+			strbuf_addf(&mi->log_message,
+				    "Message-Id: %s\n", mi->message_id);
 		return 1;
 	}
 
-	fputs(line->buf, mi->cmitmsg);
+	strbuf_addbuf(&mi->log_message, line);
 	return 0;
 }
 
@@ -999,18 +997,19 @@ static void handle_info(struct mailinfo *mi)
 
 static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 {
+	FILE *cmitmsg;
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	mi->cmitmsg = fopen(msg, "w");
-	if (!mi->cmitmsg) {
+	cmitmsg = fopen(msg, "w");
+	if (!cmitmsg) {
 		perror(msg);
 		return -1;
 	}
 	mi->patchfile = fopen(patch, "w");
 	if (!mi->patchfile) {
 		perror(patch);
-		fclose(mi->cmitmsg);
+		fclose(cmitmsg);
 		return -1;
 	}
 
@@ -1029,6 +1028,9 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 	handle_body(mi, &line);
 	handle_info(mi);
 
+	fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+	fclose(cmitmsg);
+
 	return 0;
 }
 
@@ -1052,11 +1054,20 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
 	strbuf_init(&mi->charset, 0);
+	strbuf_init(&mi->log_message, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
 	git_config(git_mailinfo_config, &mi);
 }
 
+static void clear_mailinfo(struct mailinfo *mi)
+{
+	strbuf_release(&mi->name);
+	strbuf_release(&mi->email);
+	strbuf_release(&mi->charset);
+	strbuf_release(&mi->log_message);
+}
+
 static const char mailinfo_usage[] =
 	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
 
@@ -1064,6 +1075,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
 	const char *def_charset;
 	struct mailinfo mi;
+	int status;
 
 	/* NEEDSWORK: might want to do the optional .git/ directory
 	 * discovery
@@ -1102,5 +1114,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 
 	mi.input = stdin;
 	mi.output = stdout;
-	return !!mailinfo(&mi, argv[1], argv[2]);
+	status = !!mailinfo(&mi, argv[1], argv[2]);
+	clear_mailinfo(&mi);
+
+	return status;
 }
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 22/26] mailinfo: move content/content_top to struct mailinfo
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (20 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 21/26] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 23/26] mailinfo: handle errors found in decode_header() better Junio C Hamano
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 45 ++++++++++++++++++++++++++-------------------
 1 file changed, 26 insertions(+), 19 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index d38d716..4b9b1cc 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,6 +7,8 @@
 #include "utf8.h"
 #include "strbuf.h"
 
+#define MAX_BOUNDARIES 5
+
 struct mailinfo {
 	FILE *input;
 	FILE *output;
@@ -21,6 +23,8 @@ struct mailinfo {
 	int use_inbody_headers; /* defaults to 1 */
 	const char *metainfo_charset;
 
+	struct strbuf *content[MAX_BOUNDARIES];
+	struct strbuf **content_top;
 	struct strbuf charset;
 	char *message_id;
 	enum  {
@@ -36,7 +40,6 @@ struct mailinfo {
 };
 
 #define MAX_HDR_PARSED 10
-#define MAX_BOUNDARIES 5
 
 static void cleanup_space(struct strbuf *sb);
 
@@ -181,10 +184,6 @@ static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
 	return 1;
 }
 
-static struct strbuf *content[MAX_BOUNDARIES];
-
-static struct strbuf **content_top = content;
-
 static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
@@ -192,11 +191,11 @@ static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
-		if (++content_top >= &content[MAX_BOUNDARIES]) {
+		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
 			fprintf(stderr, "Too many boundaries to handle\n");
 			exit(1);
 		}
-		*content_top = boundary;
+		*(mi->content_top) = boundary;
 		boundary = NULL;
 	}
 	slurp_attr(line->buf, "charset=", &mi->charset);
@@ -224,10 +223,12 @@ static void handle_content_transfer_encoding(struct mailinfo *mi,
 		mi->transfer_encoding = TE_DONTCARE;
 }
 
-static int is_multipart_boundary(const struct strbuf *line)
+static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
 {
-	return (((*content_top)->len <= line->len) &&
-		!memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
+	struct strbuf *content_top = *(mi->content_top);
+
+	return ((content_top->len <= line->len) &&
+		!memcmp(line->buf, content_top->buf, content_top->len));
 }
 
 static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
@@ -825,7 +826,7 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 {
 	while (!strbuf_getline(line, mi->input, '\n')) {
-		if (*content_top && is_multipart_boundary(line))
+		if (*(mi->content_top) && is_multipart_boundary(mi, line))
 			return 1;
 	}
 	return 0;
@@ -837,18 +838,18 @@ static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
 
 	strbuf_addch(&newline, '\n');
 again:
-	if (line->len >= (*content_top)->len + 2 &&
-	    !memcmp(line->buf + (*content_top)->len, "--", 2)) {
+	if (line->len >= (*(mi->content_top))->len + 2 &&
+	    !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
 		/* we hit an end boundary */
 		/* pop the current boundary off the stack */
-		strbuf_release(*content_top);
-		free(*content_top);
-		*content_top = NULL;
+		strbuf_release(*(mi->content_top));
+		free(*(mi->content_top));
+		*(mi->content_top) = NULL;
 
 		/* technically won't happen as is_multipart_boundary()
 		   will fail first.  But just in case..
 		 */
-		if (--content_top < content) {
+		if (--mi->content_top < mi->content) {
 			fprintf(stderr, "Detected mismatched boundaries, "
 					"can't recover\n");
 			exit(1);
@@ -883,14 +884,14 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 	struct strbuf prev = STRBUF_INIT;
 
 	/* Skip up to the first boundary */
-	if (*content_top) {
+	if (*(mi->content_top)) {
 		if (!find_boundary(mi, line))
 			goto handle_body_out;
 	}
 
 	do {
 		/* process any boundary lines */
-		if (*content_top && is_multipart_boundary(line)) {
+		if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
 			/* flush any leftover */
 			if (prev.len) {
 				handle_filter(mi, &prev);
@@ -1057,6 +1058,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->log_message, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
+	mi->content_top = mi->content;
 	git_config(git_mailinfo_config, &mi);
 }
 
@@ -1066,6 +1068,11 @@ static void clear_mailinfo(struct mailinfo *mi)
 	strbuf_release(&mi->email);
 	strbuf_release(&mi->charset);
 	strbuf_release(&mi->log_message);
+
+	while (mi->content < mi->content_top) {
+		free(*(mi->content_top));
+		mi->content_top--;
+	}
 }
 
 static const char mailinfo_usage[] =
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 23/26] mailinfo: handle errors found in decode_header() better
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (21 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 22/26] mailinfo: move content/content_top to struct mailinfo Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 24/26] mailinfo: handle charset conversion errors in the caller Junio C Hamano
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

The decode_header() function tries to unwrap RFC2047-style quoted
strings but punts when it finds anything it cannot parse.  Allow the
function to report if it punted to the caller, and use the resulting
string in the caller only when the function says it parsed the input
successfully.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 4b9b1cc..6452a4c 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -293,7 +293,7 @@ static void cleanup_space(struct strbuf *sb)
 	}
 }
 
-static void decode_header(struct mailinfo *mi, struct strbuf *line);
+static int decode_header(struct mailinfo *mi, struct strbuf *line);
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
@@ -336,8 +336,8 @@ static int check_header(struct mailinfo *mi,
 			 * normalize the meta information to utf8.
 			 */
 			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(mi, &sb);
-			handle_header(&hdr_data[i], &sb);
+			if (!decode_header(mi, &sb))
+				handle_header(&hdr_data[i], &sb);
 			ret = 1;
 			goto check_header_out;
 		}
@@ -347,25 +347,26 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Content-Type")) {
 		len = strlen("Content-Type: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(mi, &sb);
+		if (!decode_header(mi, &sb)) {
+			strbuf_insert(&sb, 0, "Content-Type: ", len);
+			handle_content_type(mi, &sb);
+		}
 		ret = 1;
 		goto check_header_out;
 	}
 	if (cmp_header(line, "Content-Transfer-Encoding")) {
 		len = strlen("Content-Transfer-Encoding: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_content_transfer_encoding(mi, &sb);
+		if (!decode_header(mi, &sb))
+			handle_content_transfer_encoding(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
 	if (cmp_header(line, "Message-Id")) {
 		len = strlen("Message-Id: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_message_id(mi, &sb);
+		if (!decode_header(mi, &sb))
+			handle_message_id(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -537,11 +538,12 @@ static void convert_to_utf8(struct mailinfo *mi,
 	strbuf_attach(line, out, strlen(out), strlen(out));
 }
 
-static void decode_header(struct mailinfo *mi, struct strbuf *it)
+static int decode_header(struct mailinfo *mi, struct strbuf *it)
 {
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
 	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+	int found_error = -1; /* pessimism */
 
 	in = it->buf;
 	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
@@ -610,10 +612,13 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
 	strbuf_addstr(&outbuf, in);
 	strbuf_reset(it);
 	strbuf_addbuf(it, &outbuf);
+	found_error = 0;
 release_return:
 	strbuf_release(&outbuf);
 	strbuf_release(&charset_q);
 	strbuf_release(&piecebuf);
+
+	return found_error;
 }
 
 static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 24/26] mailinfo: handle charset conversion errors in the caller
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (22 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 23/26] mailinfo: handle errors found in decode_header() better Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 25/26] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

Instead of dying in convert_to_utf8(), just report an error and let
the callers handle it.  Between the two callers:

 - decode_header() knows how to handle malformed line by reporting
   the breakage to the caller, so let it follow the pattern it
   already knows about;

 - handle_commit_msg() doesn't cope with a malformed line well, so
   die there for now.  We'll lift this even higher in later changes
   in this series.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 6452a4c..f14c504 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -521,21 +521,22 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
 	return out;
 }
 
-static void convert_to_utf8(struct mailinfo *mi,
-			    struct strbuf *line, const char *charset)
+static int convert_to_utf8(struct mailinfo *mi,
+			   struct strbuf *line, const char *charset)
 {
 	char *out;
 
 	if (!mi->metainfo_charset || !charset || !*charset)
-		return;
+		return 0;
 
 	if (same_encoding(mi->metainfo_charset, charset))
-		return;
+		return 0;
 	out = reencode_string(line->buf, mi->metainfo_charset, charset);
 	if (!out)
-		die("cannot convert from %s to %s",
-		    charset, mi->metainfo_charset);
+		return error("cannot convert from %s to %s",
+			     charset, mi->metainfo_charset);
 	strbuf_attach(line, out, strlen(out), strlen(out));
+	return 0;
 }
 
 static int decode_header(struct mailinfo *mi, struct strbuf *it)
@@ -602,7 +603,8 @@ static int decode_header(struct mailinfo *mi, struct strbuf *it)
 			dec = decode_q_segment(&piecebuf, 1);
 			break;
 		}
-		convert_to_utf8(mi, dec, charset_q.buf);
+		if (convert_to_utf8(mi, dec, charset_q.buf))
+			goto release_return;
 
 		strbuf_addbuf(&outbuf, dec);
 		strbuf_release(dec);
@@ -778,7 +780,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	convert_to_utf8(mi, line, mi->charset.buf);
+	if (convert_to_utf8(mi, line, mi->charset.buf))
+		exit(128);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 25/26] mailinfo: remove calls to exit() and die() deep in the callchain
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (23 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 24/26] mailinfo: handle charset conversion errors in the caller Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-13 23:16 ` [PATCH 26/26] mailinfo: libify the whole thing Junio C Hamano
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

The top-level mailinfo() would instead punt when the code in the
deeper part of the callchain detects an unrecoverable error in the
input.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 36 +++++++++++++++++++++++++-----------
 1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index f14c504..eb9ff96 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -37,6 +37,7 @@ struct mailinfo {
 	struct strbuf **s_hdr_data;
 
 	struct strbuf log_message;
+	int input_error;
 };
 
 #define MAX_HDR_PARSED 10
@@ -192,8 +193,10 @@ static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
 		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
-			fprintf(stderr, "Too many boundaries to handle\n");
-			exit(1);
+			error("Too many boundaries to handle");
+			mi->input_error = -1;
+			mi->content_top = mi->content + MAX_BOUNDARIES - 1;
+			return;
 		}
 		*(mi->content_top) = boundary;
 		boundary = NULL;
@@ -532,9 +535,11 @@ static int convert_to_utf8(struct mailinfo *mi,
 	if (same_encoding(mi->metainfo_charset, charset))
 		return 0;
 	out = reencode_string(line->buf, mi->metainfo_charset, charset);
-	if (!out)
+	if (!out) {
+		mi->input_error = -1;
 		return error("cannot convert from %s to %s",
 			     charset, mi->metainfo_charset);
+	}
 	strbuf_attach(line, out, strlen(out), strlen(out));
 	return 0;
 }
@@ -781,7 +786,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	/* normalize the log message to UTF-8. */
 	if (convert_to_utf8(mi, line, mi->charset.buf))
-		exit(128);
+		return 0; /* the caller will reject this input */
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -820,6 +825,9 @@ static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
 
 static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 {
+	if (mi->input_error)
+		return;
+
 	switch (mi->filter_stage) {
 	case 0:
 		if (!handle_commit_msg(mi, line))
@@ -858,12 +866,15 @@ again:
 		   will fail first.  But just in case..
 		 */
 		if (--mi->content_top < mi->content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
+			error("Detected mismatched boundaries, can't recover");
+			mi->input_error = -1;
+			mi->content_top = mi->content;
+			return 0;
 		}
 		handle_filter(mi, &newline);
 		strbuf_release(&newline);
+		if (mi->input_error)
+			return 0; /* punt to the end */
 
 		/* skip to the next boundary */
 		if (!find_boundary(mi, line))
@@ -948,6 +959,8 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 			handle_filter(mi, line);
 		}
 
+		if (mi->input_error)
+			break;
 	} while (!strbuf_getwholeline(line, mi->input, '\n'));
 
 handle_body_out:
@@ -1035,12 +1048,13 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 		check_header(mi, &line, mi->p_hdr_data, 1);
 
 	handle_body(mi, &line);
-	handle_info(mi);
-
-	fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+	if (!mi->input_error) {
+		handle_info(mi);
+		fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+	}
 	fclose(cmitmsg);
 
-	return 0;
+	return mi->input_error;
 }
 
 static int git_mailinfo_config(const char *var, const char *value, void *mi_)
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 26/26] mailinfo: libify the whole thing
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (24 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 25/26] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
@ 2015-10-13 23:16 ` Junio C Hamano
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
  26 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-13 23:16 UTC (permalink / raw)
  To: git

With this, the builtin "git am" can hopefully make an internal call
to the mailinfo() function without going through the run_command()
interface, whose overhead is heavyweight compared to what mailinfo()
itself does on some platforms.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Makefile           |    1 +
 builtin/mailinfo.c | 1091 +---------------------------------------------------
 mailinfo.c         | 1058 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mailinfo.h         |   41 ++
 4 files changed, 1101 insertions(+), 1090 deletions(-)
 create mode 100644 mailinfo.c
 create mode 100644 mailinfo.h

diff --git a/Makefile b/Makefile
index 8d5df7e..7dd3bff 100644
--- a/Makefile
+++ b/Makefile
@@ -726,6 +726,7 @@ LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge.o
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index eb9ff96..f6df274 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -6,1096 +6,7 @@
 #include "builtin.h"
 #include "utf8.h"
 #include "strbuf.h"
-
-#define MAX_BOUNDARIES 5
-
-struct mailinfo {
-	FILE *input;
-	FILE *output;
-	FILE *patchfile;
-
-	struct strbuf name;
-	struct strbuf email;
-	int keep_subject;
-	int keep_non_patch_brackets_in_subject;
-	int add_message_id;
-	int use_scissors;
-	int use_inbody_headers; /* defaults to 1 */
-	const char *metainfo_charset;
-
-	struct strbuf *content[MAX_BOUNDARIES];
-	struct strbuf **content_top;
-	struct strbuf charset;
-	char *message_id;
-	enum  {
-		TE_DONTCARE, TE_QP, TE_BASE64
-	} transfer_encoding;
-	int patch_lines;
-	int filter_stage; /* still reading log or are we copying patch? */
-	int header_stage; /* still checking in-body headers? */
-	struct strbuf **p_hdr_data;
-	struct strbuf **s_hdr_data;
-
-	struct strbuf log_message;
-	int input_error;
-};
-
-#define MAX_HDR_PARSED 10
-
-static void cleanup_space(struct strbuf *sb);
-
-
-static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
-{
-	struct strbuf *src = name;
-	if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
-		strchr(name->buf, '<') || strchr(name->buf, '>'))
-		src = email;
-	else if (name == out)
-		return;
-	strbuf_reset(out);
-	strbuf_addbuf(out, src);
-}
-
-static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
-{
-	/* John Doe <johndoe> */
-
-	char *bra, *ket;
-	/* This is fallback, so do not bother if we already have an
-	 * e-mail address.
-	 */
-	if (mi->email.len)
-		return;
-
-	bra = strchr(line->buf, '<');
-	if (!bra)
-		return;
-	ket = strchr(bra, '>');
-	if (!ket)
-		return;
-
-	strbuf_reset(&mi->email);
-	strbuf_add(&mi->email, bra + 1, ket - bra - 1);
-
-	strbuf_reset(&mi->name);
-	strbuf_add(&mi->name, line->buf, bra - line->buf);
-	strbuf_trim(&mi->name);
-	get_sane_name(&mi->name, &mi->name, &mi->email);
-}
-
-static void handle_from(struct mailinfo *mi, const struct strbuf *from)
-{
-	char *at;
-	size_t el;
-	struct strbuf f;
-
-	strbuf_init(&f, from->len);
-	strbuf_addbuf(&f, from);
-
-	at = strchr(f.buf, '@');
-	if (!at) {
-		parse_bogus_from(mi, from);
-		return;
-	}
-
-	/*
-	 * If we already have one email, don't take any confusing lines
-	 */
-	if (mi->email.len && strchr(at + 1, '@')) {
-		strbuf_release(&f);
-		return;
-	}
-
-	/* Pick up the string around '@', possibly delimited with <>
-	 * pair; that is the email part.
-	 */
-	while (at > f.buf) {
-		char c = at[-1];
-		if (isspace(c))
-			break;
-		if (c == '<') {
-			at[-1] = ' ';
-			break;
-		}
-		at--;
-	}
-	el = strcspn(at, " \n\t\r\v\f>");
-	strbuf_reset(&mi->email);
-	strbuf_add(&mi->email, at, el);
-	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
-
-	/* The remainder is name.  It could be
-	 *
-	 * - "John Doe <john.doe@xz>"			(a), or
-	 * - "john.doe@xz (John Doe)"			(b), or
-	 * - "John (zzz) Doe <john.doe@xz> (Comment)"	(c)
-	 *
-	 * but we have removed the email part, so
-	 *
-	 * - remove extra spaces which could stay after email (case 'c'), and
-	 * - trim from both ends, possibly removing the () pair at the end
-	 *   (cases 'a' and 'b').
-	 */
-	cleanup_space(&f);
-	strbuf_trim(&f);
-	if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
-		strbuf_remove(&f, 0, 1);
-		strbuf_setlen(&f, f.len - 1);
-	}
-
-	get_sane_name(&mi->name, &f, &mi->email);
-	strbuf_release(&f);
-}
-
-static void handle_header(struct strbuf **out, const struct strbuf *line)
-{
-	if (!*out) {
-		*out = xmalloc(sizeof(struct strbuf));
-		strbuf_init(*out, line->len);
-	} else
-		strbuf_reset(*out);
-
-	strbuf_addbuf(*out, line);
-}
-
-/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
- * to have enough heuristics to grok MIME encoded patches often found
- * on our mailing lists.  For example, we do not even treat header lines
- * case insensitively.
- */
-
-static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
-{
-	const char *ends, *ap = strcasestr(line, name);
-	size_t sz;
-
-	strbuf_setlen(attr, 0);
-	if (!ap)
-		return 0;
-	ap += strlen(name);
-	if (*ap == '"') {
-		ap++;
-		ends = "\"";
-	}
-	else
-		ends = "; \t";
-	sz = strcspn(ap, ends);
-	strbuf_add(attr, ap, sz);
-	return 1;
-}
-
-static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
-	strbuf_init(boundary, line->len);
-
-	if (slurp_attr(line->buf, "boundary=", boundary)) {
-		strbuf_insert(boundary, 0, "--", 2);
-		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
-			error("Too many boundaries to handle");
-			mi->input_error = -1;
-			mi->content_top = mi->content + MAX_BOUNDARIES - 1;
-			return;
-		}
-		*(mi->content_top) = boundary;
-		boundary = NULL;
-	}
-	slurp_attr(line->buf, "charset=", &mi->charset);
-
-	if (boundary) {
-		strbuf_release(boundary);
-		free(boundary);
-	}
-}
-
-static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
-{
-	if (mi->add_message_id)
-		mi->message_id = strdup(line->buf);
-}
-
-static void handle_content_transfer_encoding(struct mailinfo *mi,
-					     const struct strbuf *line)
-{
-	if (strcasestr(line->buf, "base64"))
-		mi->transfer_encoding = TE_BASE64;
-	else if (strcasestr(line->buf, "quoted-printable"))
-		mi->transfer_encoding = TE_QP;
-	else
-		mi->transfer_encoding = TE_DONTCARE;
-}
-
-static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
-{
-	struct strbuf *content_top = *(mi->content_top);
-
-	return ((content_top->len <= line->len) &&
-		!memcmp(line->buf, content_top->buf, content_top->len));
-}
-
-static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
-{
-	size_t at = 0;
-
-	while (at < subject->len) {
-		char *pos;
-		size_t remove;
-
-		switch (subject->buf[at]) {
-		case 'r': case 'R':
-			if (subject->len <= at + 3)
-				break;
-			if ((subject->buf[at + 1] == 'e' ||
-			     subject->buf[at + 1] == 'E') &&
-			    subject->buf[at + 2] == ':') {
-				strbuf_remove(subject, at, 3);
-				continue;
-			}
-			at++;
-			break;
-		case ' ': case '\t': case ':':
-			strbuf_remove(subject, at, 1);
-			continue;
-		case '[':
-			pos = strchr(subject->buf + at, ']');
-			if (!pos)
-				break;
-			remove = pos - subject->buf + at + 1;
-			if (!mi->keep_non_patch_brackets_in_subject ||
-			    (7 <= remove &&
-			     memmem(subject->buf + at, remove, "PATCH", 5)))
-				strbuf_remove(subject, at, remove);
-			else {
-				at += remove;
-				/*
-				 * If the input had a space after the ], keep
-				 * it.  We don't bother with finding the end of
-				 * the space, since we later normalize it
-				 * anyway.
-				 */
-				if (isspace(subject->buf[at]))
-					at += 1;
-			}
-			continue;
-		}
-		break;
-	}
-	strbuf_trim(subject);
-}
-
-static void cleanup_space(struct strbuf *sb)
-{
-	size_t pos, cnt;
-	for (pos = 0; pos < sb->len; pos++) {
-		if (isspace(sb->buf[pos])) {
-			sb->buf[pos] = ' ';
-			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
-			strbuf_remove(sb, pos + 1, cnt);
-		}
-	}
-}
-
-static int decode_header(struct mailinfo *mi, struct strbuf *line);
-static const char *header[MAX_HDR_PARSED] = {
-	"From","Subject","Date",
-};
-
-static inline int cmp_header(const struct strbuf *line, const char *hdr)
-{
-	int len = strlen(hdr);
-	return !strncasecmp(line->buf, hdr, len) && line->len > len &&
-			line->buf[len] == ':' && isspace(line->buf[len + 1]);
-}
-
-static int is_format_patch_separator(const char *line, int len)
-{
-	static const char SAMPLE[] =
-		"From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
-	const char *cp;
-
-	if (len != strlen(SAMPLE))
-		return 0;
-	if (!skip_prefix(line, "From ", &cp))
-		return 0;
-	if (strspn(cp, "0123456789abcdef") != 40)
-		return 0;
-	cp += 40;
-	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
-}
-
-static int check_header(struct mailinfo *mi,
-			const struct strbuf *line,
-			struct strbuf *hdr_data[], int overwrite)
-{
-	int i, ret = 0, len;
-	struct strbuf sb = STRBUF_INIT;
-
-	/* search for the interesting parts */
-	for (i = 0; header[i]; i++) {
-		int len = strlen(header[i]);
-		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
-			/* Unwrap inline B and Q encoding, and optionally
-			 * normalize the meta information to utf8.
-			 */
-			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			if (!decode_header(mi, &sb))
-				handle_header(&hdr_data[i], &sb);
-			ret = 1;
-			goto check_header_out;
-		}
-	}
-
-	/* Content stuff */
-	if (cmp_header(line, "Content-Type")) {
-		len = strlen("Content-Type: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		if (!decode_header(mi, &sb)) {
-			strbuf_insert(&sb, 0, "Content-Type: ", len);
-			handle_content_type(mi, &sb);
-		}
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Content-Transfer-Encoding")) {
-		len = strlen("Content-Transfer-Encoding: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		if (!decode_header(mi, &sb))
-			handle_content_transfer_encoding(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Message-Id")) {
-		len = strlen("Message-Id: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		if (!decode_header(mi, &sb))
-			handle_message_id(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-
-	/* for inbody stuff */
-	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
-		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
-		goto check_header_out;
-	}
-	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-		for (i = 0; header[i]; i++) {
-			if (!strcmp("Subject", header[i])) {
-				handle_header(&hdr_data[i], line);
-				ret = 1;
-				goto check_header_out;
-			}
-		}
-	}
-
-check_header_out:
-	strbuf_release(&sb);
-	return ret;
-}
-
-static int is_rfc2822_header(const struct strbuf *line)
-{
-	/*
-	 * The section that defines the loosest possible
-	 * field name is "3.6.8 Optional fields".
-	 *
-	 * optional-field = field-name ":" unstructured CRLF
-	 * field-name = 1*ftext
-	 * ftext = %d33-57 / %59-126
-	 */
-	int ch;
-	char *cp = line->buf;
-
-	/* Count mbox From headers as headers */
-	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
-		return 1;
-
-	while ((ch = *cp++)) {
-		if (ch == ':')
-			return 1;
-		if ((33 <= ch && ch <= 57) ||
-		    (59 <= ch && ch <= 126))
-			continue;
-		break;
-	}
-	return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
-	/* Get the first part of the line. */
-	if (strbuf_getline(line, in, '\n'))
-		return 0;
-
-	/*
-	 * Is it an empty line or not a valid rfc2822 header?
-	 * If so, stop here, and return false ("not a header")
-	 */
-	strbuf_rtrim(line);
-	if (!line->len || !is_rfc2822_header(line)) {
-		/* Re-add the newline */
-		strbuf_addch(line, '\n');
-		return 0;
-	}
-
-	/*
-	 * Now we need to eat all the continuation lines..
-	 * Yuck, 2822 header "folding"
-	 */
-	for (;;) {
-		int peek;
-		struct strbuf continuation = STRBUF_INIT;
-
-		peek = fgetc(in); ungetc(peek, in);
-		if (peek != ' ' && peek != '\t')
-			break;
-		if (strbuf_getline(&continuation, in, '\n'))
-			break;
-		continuation.buf[0] = ' ';
-		strbuf_rtrim(&continuation);
-		strbuf_addbuf(line, &continuation);
-	}
-
-	return 1;
-}
-
-static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
-{
-	const char *in = q_seg->buf;
-	int c;
-	struct strbuf *out = xmalloc(sizeof(struct strbuf));
-	strbuf_init(out, q_seg->len);
-
-	while ((c = *in++) != 0) {
-		if (c == '=') {
-			int d = *in++;
-			if (d == '\n' || !d)
-				break; /* drop trailing newline */
-			strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
-			continue;
-		}
-		if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
-			c = 0x20;
-		strbuf_addch(out, c);
-	}
-	return out;
-}
-
-static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
-{
-	/* Decode in..ep, possibly in-place to ot */
-	int c, pos = 0, acc = 0;
-	const char *in = b_seg->buf;
-	struct strbuf *out = xmalloc(sizeof(struct strbuf));
-	strbuf_init(out, b_seg->len);
-
-	while ((c = *in++) != 0) {
-		if (c == '+')
-			c = 62;
-		else if (c == '/')
-			c = 63;
-		else if ('A' <= c && c <= 'Z')
-			c -= 'A';
-		else if ('a' <= c && c <= 'z')
-			c -= 'a' - 26;
-		else if ('0' <= c && c <= '9')
-			c -= '0' - 52;
-		else
-			continue; /* garbage */
-		switch (pos++) {
-		case 0:
-			acc = (c << 2);
-			break;
-		case 1:
-			strbuf_addch(out, (acc | (c >> 4)));
-			acc = (c & 15) << 4;
-			break;
-		case 2:
-			strbuf_addch(out, (acc | (c >> 2)));
-			acc = (c & 3) << 6;
-			break;
-		case 3:
-			strbuf_addch(out, (acc | c));
-			acc = pos = 0;
-			break;
-		}
-	}
-	return out;
-}
-
-static int convert_to_utf8(struct mailinfo *mi,
-			   struct strbuf *line, const char *charset)
-{
-	char *out;
-
-	if (!mi->metainfo_charset || !charset || !*charset)
-		return 0;
-
-	if (same_encoding(mi->metainfo_charset, charset))
-		return 0;
-	out = reencode_string(line->buf, mi->metainfo_charset, charset);
-	if (!out) {
-		mi->input_error = -1;
-		return error("cannot convert from %s to %s",
-			     charset, mi->metainfo_charset);
-	}
-	strbuf_attach(line, out, strlen(out), strlen(out));
-	return 0;
-}
-
-static int decode_header(struct mailinfo *mi, struct strbuf *it)
-{
-	char *in, *ep, *cp;
-	struct strbuf outbuf = STRBUF_INIT, *dec;
-	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
-	int found_error = -1; /* pessimism */
-
-	in = it->buf;
-	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
-		int encoding;
-		strbuf_reset(&charset_q);
-		strbuf_reset(&piecebuf);
-
-		if (in != ep) {
-			/*
-			 * We are about to process an encoded-word
-			 * that begins at ep, but there is something
-			 * before the encoded word.
-			 */
-			char *scan;
-			for (scan = in; scan < ep; scan++)
-				if (!isspace(*scan))
-					break;
-
-			if (scan != ep || in == it->buf) {
-				/*
-				 * We should not lose that "something",
-				 * unless we have just processed an
-				 * encoded-word, and there is only LWS
-				 * before the one we are about to process.
-				 */
-				strbuf_add(&outbuf, in, ep - in);
-			}
-		}
-		/* E.g.
-		 * ep : "=?iso-2022-jp?B?GyR...?= foo"
-		 * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
-		 */
-		ep += 2;
-
-		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
-			goto release_return;
-
-		if (cp + 3 - it->buf > it->len)
-			goto release_return;
-		strbuf_add(&charset_q, ep, cp - ep);
-
-		encoding = cp[1];
-		if (!encoding || cp[2] != '?')
-			goto release_return;
-		ep = strstr(cp + 3, "?=");
-		if (!ep)
-			goto release_return;
-		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
-		switch (tolower(encoding)) {
-		default:
-			goto release_return;
-		case 'b':
-			dec = decode_b_segment(&piecebuf);
-			break;
-		case 'q':
-			dec = decode_q_segment(&piecebuf, 1);
-			break;
-		}
-		if (convert_to_utf8(mi, dec, charset_q.buf))
-			goto release_return;
-
-		strbuf_addbuf(&outbuf, dec);
-		strbuf_release(dec);
-		free(dec);
-		in = ep + 2;
-	}
-	strbuf_addstr(&outbuf, in);
-	strbuf_reset(it);
-	strbuf_addbuf(it, &outbuf);
-	found_error = 0;
-release_return:
-	strbuf_release(&outbuf);
-	strbuf_release(&charset_q);
-	strbuf_release(&piecebuf);
-
-	return found_error;
-}
-
-static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf *ret;
-
-	switch (mi->transfer_encoding) {
-	case TE_QP:
-		ret = decode_q_segment(line, 0);
-		break;
-	case TE_BASE64:
-		ret = decode_b_segment(line);
-		break;
-	case TE_DONTCARE:
-	default:
-		return;
-	}
-	strbuf_reset(line);
-	strbuf_addbuf(line, ret);
-	strbuf_release(ret);
-	free(ret);
-}
-
-static inline int patchbreak(const struct strbuf *line)
-{
-	size_t i;
-
-	/* Beginning of a "diff -" header? */
-	if (starts_with(line->buf, "diff -"))
-		return 1;
-
-	/* CVS "Index: " line? */
-	if (starts_with(line->buf, "Index: "))
-		return 1;
-
-	/*
-	 * "--- <filename>" starts patches without headers
-	 * "---<sp>*" is a manual separator
-	 */
-	if (line->len < 4)
-		return 0;
-
-	if (starts_with(line->buf, "---")) {
-		/* space followed by a filename? */
-		if (line->buf[3] == ' ' && !isspace(line->buf[4]))
-			return 1;
-		/* Just whitespace? */
-		for (i = 3; i < line->len; i++) {
-			unsigned char c = line->buf[i];
-			if (c == '\n')
-				return 1;
-			if (!isspace(c))
-				break;
-		}
-		return 0;
-	}
-	return 0;
-}
-
-static int is_scissors_line(const struct strbuf *line)
-{
-	size_t i, len = line->len;
-	int scissors = 0, gap = 0;
-	int first_nonblank = -1;
-	int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
-	const char *buf = line->buf;
-
-	for (i = 0; i < len; i++) {
-		if (isspace(buf[i])) {
-			if (in_perforation) {
-				perforation++;
-				gap++;
-			}
-			continue;
-		}
-		last_nonblank = i;
-		if (first_nonblank < 0)
-			first_nonblank = i;
-		if (buf[i] == '-') {
-			in_perforation = 1;
-			perforation++;
-			continue;
-		}
-		if (i + 1 < len &&
-		    (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
-		     !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
-			in_perforation = 1;
-			perforation += 2;
-			scissors += 2;
-			i++;
-			continue;
-		}
-		in_perforation = 0;
-	}
-
-	/*
-	 * The mark must be at least 8 bytes long (e.g. "-- >8 --").
-	 * Even though there can be arbitrary cruft on the same line
-	 * (e.g. "cut here"), in order to avoid misidentification, the
-	 * perforation must occupy more than a third of the visible
-	 * width of the line, and dashes and scissors must occupy more
-	 * than half of the perforation.
-	 */
-
-	visible = last_nonblank - first_nonblank + 1;
-	return (scissors && 8 <= visible &&
-		visible < perforation * 3 &&
-		gap * 2 < perforation);
-}
-
-static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
-{
-	/*
-	 * Are we still scanning and discarding in-body headers?
-	 * It is initially set to 1, set to 2 when we do see a
-	 * valid in-body header.
-	 */
-	int is_empty_line;
-
-	assert(!mi->filter_stage);
-
-	is_empty_line = (!line->len || (line->len == 1 && line->buf[0] == '\n'));
-	if (mi->header_stage == 1) {
-		/*
-		 * Haven't seen a known in-body header; discard an empty line.
-		 */
-		if (is_empty_line)
-			return 0;
-	}
-
-	if (mi->use_inbody_headers && mi->header_stage) {
-		int is_known_header = check_header(mi, line, mi->s_hdr_data, 0);
-
-		if (mi->header_stage == 2) {
-			/*
-			 * an empty line after the in-body header block,
-			 * or a line obviously not an attempt to invent
-			 * an unsupported in-body header.
-			 */
-			if (is_empty_line || !is_rfc2822_header(line))
-				mi->header_stage = 0;
-			if (is_empty_line)
-				return 0;
-			/* otherwise do not discard the line, but keep going */
-		} else if (is_known_header) {
-			mi->header_stage = 2;
-		} else if (mi->header_stage != 2) {
-			mi->header_stage = 0;
-		}
-
-		if (mi->header_stage)
-			return 0;
-	} else
-		/* Only trim the first (blank) line of the commit message
-		 * when ignoring in-body headers.
-		 */
-		mi->header_stage = 0;
-
-	/* normalize the log message to UTF-8. */
-	if (convert_to_utf8(mi, line, mi->charset.buf))
-		return 0; /* the caller will reject this input */
-
-	if (mi->use_scissors && is_scissors_line(line)) {
-		int i;
-
-		strbuf_setlen(&mi->log_message, 0);
-		mi->header_stage = 1;
-
-		/*
-		 * We may have already read "secondary headers"; purge
-		 * them to give ourselves a clean restart.
-		 */
-		for (i = 0; header[i]; i++) {
-			if (mi->s_hdr_data[i])
-				strbuf_release(mi->s_hdr_data[i]);
-			mi->s_hdr_data[i] = NULL;
-		}
-		return 0;
-	}
-
-	if (patchbreak(line)) {
-		if (mi->message_id)
-			strbuf_addf(&mi->log_message,
-				    "Message-Id: %s\n", mi->message_id);
-		return 1;
-	}
-
-	strbuf_addbuf(&mi->log_message, line);
-	return 0;
-}
-
-static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
-{
-	fwrite(line->buf, 1, line->len, mi->patchfile);
-	mi->patch_lines++;
-}
-
-static void handle_filter(struct mailinfo *mi, struct strbuf *line)
-{
-	if (mi->input_error)
-		return;
-
-	switch (mi->filter_stage) {
-	case 0:
-		if (!handle_commit_msg(mi, line))
-			break;
-		mi->filter_stage++;
-	case 1:
-		handle_patch(mi, line);
-		break;
-	}
-}
-
-static int find_boundary(struct mailinfo *mi, struct strbuf *line)
-{
-	while (!strbuf_getline(line, mi->input, '\n')) {
-		if (*(mi->content_top) && is_multipart_boundary(mi, line))
-			return 1;
-	}
-	return 0;
-}
-
-static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf newline = STRBUF_INIT;
-
-	strbuf_addch(&newline, '\n');
-again:
-	if (line->len >= (*(mi->content_top))->len + 2 &&
-	    !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
-		/* we hit an end boundary */
-		/* pop the current boundary off the stack */
-		strbuf_release(*(mi->content_top));
-		free(*(mi->content_top));
-		*(mi->content_top) = NULL;
-
-		/* technically won't happen as is_multipart_boundary()
-		   will fail first.  But just in case..
-		 */
-		if (--mi->content_top < mi->content) {
-			error("Detected mismatched boundaries, can't recover");
-			mi->input_error = -1;
-			mi->content_top = mi->content;
-			return 0;
-		}
-		handle_filter(mi, &newline);
-		strbuf_release(&newline);
-		if (mi->input_error)
-			return 0; /* punt to the end */
-
-		/* skip to the next boundary */
-		if (!find_boundary(mi, line))
-			return 0;
-		goto again;
-	}
-
-	/* set some defaults */
-	mi->transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&mi->charset);
-
-	/* slurp in this section's info */
-	while (read_one_header_line(line, mi->input))
-		check_header(mi, line, mi->p_hdr_data, 0);
-
-	strbuf_release(&newline);
-	/* replenish line */
-	if (strbuf_getline(line, mi->input, '\n'))
-		return 0;
-	strbuf_addch(line, '\n');
-	return 1;
-}
-
-static void handle_body(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf prev = STRBUF_INIT;
-
-	/* Skip up to the first boundary */
-	if (*(mi->content_top)) {
-		if (!find_boundary(mi, line))
-			goto handle_body_out;
-	}
-
-	do {
-		/* process any boundary lines */
-		if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
-			/* flush any leftover */
-			if (prev.len) {
-				handle_filter(mi, &prev);
-				strbuf_reset(&prev);
-			}
-			if (!handle_boundary(mi, line))
-				goto handle_body_out;
-		}
-
-		/* Unwrap transfer encoding */
-		decode_transfer_encoding(mi, line);
-
-		switch (mi->transfer_encoding) {
-		case TE_BASE64:
-		case TE_QP:
-		{
-			struct strbuf **lines, **it, *sb;
-
-			/* Prepend any previous partial lines */
-			strbuf_insert(line, 0, prev.buf, prev.len);
-			strbuf_reset(&prev);
-
-			/*
-			 * This is a decoded line that may contain
-			 * multiple new lines.  Pass only one chunk
-			 * at a time to handle_filter()
-			 */
-			lines = strbuf_split(line, '\n');
-			for (it = lines; (sb = *it); it++) {
-				if (*(it + 1) == NULL) /* The last line */
-					if (sb->buf[sb->len - 1] != '\n') {
-						/* Partial line, save it for later. */
-						strbuf_addbuf(&prev, sb);
-						break;
-					}
-				handle_filter(mi, sb);
-			}
-			/*
-			 * The partial chunk is saved in "prev" and will be
-			 * appended by the next iteration of read_line_with_nul().
-			 */
-			strbuf_list_free(lines);
-			break;
-		}
-		default:
-			handle_filter(mi, line);
-		}
-
-		if (mi->input_error)
-			break;
-	} while (!strbuf_getwholeline(line, mi->input, '\n'));
-
-handle_body_out:
-	strbuf_release(&prev);
-}
-
-static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
-{
-	const char *sp = data->buf;
-	while (1) {
-		char *ep = strchr(sp, '\n');
-		int len;
-		if (!ep)
-			len = strlen(sp);
-		else
-			len = ep - sp;
-		fprintf(fout, "%s: %.*s\n", hdr, len, sp);
-		if (!ep)
-			break;
-		sp = ep + 1;
-	}
-}
-
-static void handle_info(struct mailinfo *mi)
-{
-	struct strbuf *hdr;
-	int i;
-
-	for (i = 0; header[i]; i++) {
-		/* only print inbody headers if we output a patch file */
-		if (mi->patch_lines && mi->s_hdr_data[i])
-			hdr = mi->s_hdr_data[i];
-		else if (mi->p_hdr_data[i])
-			hdr = mi->p_hdr_data[i];
-		else
-			continue;
-
-		if (!strcmp(header[i], "Subject")) {
-			if (!mi->keep_subject) {
-				cleanup_subject(mi, hdr);
-				cleanup_space(hdr);
-			}
-			output_header_lines(mi->output, "Subject", hdr);
-		} else if (!strcmp(header[i], "From")) {
-			cleanup_space(hdr);
-			handle_from(mi, hdr);
-			fprintf(mi->output, "Author: %s\n", mi->name.buf);
-			fprintf(mi->output, "Email: %s\n", mi->email.buf);
-		} else {
-			cleanup_space(hdr);
-			fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
-		}
-	}
-	fprintf(mi->output, "\n");
-}
-
-static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
-{
-	FILE *cmitmsg;
-	int peek;
-	struct strbuf line = STRBUF_INIT;
-
-	cmitmsg = fopen(msg, "w");
-	if (!cmitmsg) {
-		perror(msg);
-		return -1;
-	}
-	mi->patchfile = fopen(patch, "w");
-	if (!mi->patchfile) {
-		perror(patch);
-		fclose(cmitmsg);
-		return -1;
-	}
-
-	mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
-	mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
-
-	do {
-		peek = fgetc(mi->input);
-	} while (isspace(peek));
-	ungetc(peek, mi->input);
-
-	/* process the email header */
-	while (read_one_header_line(&line, mi->input))
-		check_header(mi, &line, mi->p_hdr_data, 1);
-
-	handle_body(mi, &line);
-	if (!mi->input_error) {
-		handle_info(mi);
-		fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
-	}
-	fclose(cmitmsg);
-
-	return mi->input_error;
-}
-
-static int git_mailinfo_config(const char *var, const char *value, void *mi_)
-{
-	struct mailinfo *mi = mi_;
-
-	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, NULL);
-	if (!strcmp(var, "mailinfo.scissors")) {
-		mi->use_scissors = git_config_bool(var, value);
-		return 0;
-	}
-	/* perhaps others here */
-	return 0;
-}
-
-static void setup_mailinfo(struct mailinfo *mi)
-{
-	memset(mi, 0, sizeof(*mi));
-	strbuf_init(&mi->name, 0);
-	strbuf_init(&mi->email, 0);
-	strbuf_init(&mi->charset, 0);
-	strbuf_init(&mi->log_message, 0);
-	mi->header_stage = 1;
-	mi->use_inbody_headers = 1;
-	mi->content_top = mi->content;
-	git_config(git_mailinfo_config, &mi);
-}
-
-static void clear_mailinfo(struct mailinfo *mi)
-{
-	strbuf_release(&mi->name);
-	strbuf_release(&mi->email);
-	strbuf_release(&mi->charset);
-	strbuf_release(&mi->log_message);
-
-	while (mi->content < mi->content_top) {
-		free(*(mi->content_top));
-		mi->content_top--;
-	}
-}
+#include "mailinfo.h"
 
 static const char mailinfo_usage[] =
 	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
diff --git a/mailinfo.c b/mailinfo.c
new file mode 100644
index 0000000..ca40030
--- /dev/null
+++ b/mailinfo.c
@@ -0,0 +1,1058 @@
+#include "cache.h"
+#include "builtin.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "mailinfo.h"
+
+static void cleanup_space(struct strbuf *sb)
+{
+	size_t pos, cnt;
+	for (pos = 0; pos < sb->len; pos++) {
+		if (isspace(sb->buf[pos])) {
+			sb->buf[pos] = ' ';
+			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+			strbuf_remove(sb, pos + 1, cnt);
+		}
+	}
+}
+
+static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
+{
+	struct strbuf *src = name;
+	if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
+		strchr(name->buf, '<') || strchr(name->buf, '>'))
+		src = email;
+	else if (name == out)
+		return;
+	strbuf_reset(out);
+	strbuf_addbuf(out, src);
+}
+
+static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
+{
+	/* John Doe <johndoe> */
+
+	char *bra, *ket;
+	/* This is fallback, so do not bother if we already have an
+	 * e-mail address.
+	 */
+	if (mi->email.len)
+		return;
+
+	bra = strchr(line->buf, '<');
+	if (!bra)
+		return;
+	ket = strchr(bra, '>');
+	if (!ket)
+		return;
+
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, bra + 1, ket - bra - 1);
+
+	strbuf_reset(&mi->name);
+	strbuf_add(&mi->name, line->buf, bra - line->buf);
+	strbuf_trim(&mi->name);
+	get_sane_name(&mi->name, &mi->name, &mi->email);
+}
+
+static void handle_from(struct mailinfo *mi, const struct strbuf *from)
+{
+	char *at;
+	size_t el;
+	struct strbuf f;
+
+	strbuf_init(&f, from->len);
+	strbuf_addbuf(&f, from);
+
+	at = strchr(f.buf, '@');
+	if (!at) {
+		parse_bogus_from(mi, from);
+		return;
+	}
+
+	/*
+	 * If we already have one email, don't take any confusing lines
+	 */
+	if (mi->email.len && strchr(at + 1, '@')) {
+		strbuf_release(&f);
+		return;
+	}
+
+	/* Pick up the string around '@', possibly delimited with <>
+	 * pair; that is the email part.
+	 */
+	while (at > f.buf) {
+		char c = at[-1];
+		if (isspace(c))
+			break;
+		if (c == '<') {
+			at[-1] = ' ';
+			break;
+		}
+		at--;
+	}
+	el = strcspn(at, " \n\t\r\v\f>");
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, at, el);
+	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
+
+	/* The remainder is name.  It could be
+	 *
+	 * - "John Doe <john.doe@xz>"			(a), or
+	 * - "john.doe@xz (John Doe)"			(b), or
+	 * - "John (zzz) Doe <john.doe@xz> (Comment)"	(c)
+	 *
+	 * but we have removed the email part, so
+	 *
+	 * - remove extra spaces which could stay after email (case 'c'), and
+	 * - trim from both ends, possibly removing the () pair at the end
+	 *   (cases 'a' and 'b').
+	 */
+	cleanup_space(&f);
+	strbuf_trim(&f);
+	if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
+		strbuf_remove(&f, 0, 1);
+		strbuf_setlen(&f, f.len - 1);
+	}
+
+	get_sane_name(&mi->name, &f, &mi->email);
+	strbuf_release(&f);
+}
+
+static void handle_header(struct strbuf **out, const struct strbuf *line)
+{
+	if (!*out) {
+		*out = xmalloc(sizeof(struct strbuf));
+		strbuf_init(*out, line->len);
+	} else
+		strbuf_reset(*out);
+
+	strbuf_addbuf(*out, line);
+}
+
+/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
+ * to have enough heuristics to grok MIME encoded patches often found
+ * on our mailing lists.  For example, we do not even treat header lines
+ * case insensitively.
+ */
+
+static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
+{
+	const char *ends, *ap = strcasestr(line, name);
+	size_t sz;
+
+	strbuf_setlen(attr, 0);
+	if (!ap)
+		return 0;
+	ap += strlen(name);
+	if (*ap == '"') {
+		ap++;
+		ends = "\"";
+	}
+	else
+		ends = "; \t";
+	sz = strcspn(ap, ends);
+	strbuf_add(attr, ap, sz);
+	return 1;
+}
+
+static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
+{
+	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
+	strbuf_init(boundary, line->len);
+
+	if (slurp_attr(line->buf, "boundary=", boundary)) {
+		strbuf_insert(boundary, 0, "--", 2);
+		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
+			error("Too many boundaries to handle");
+			mi->input_error = -1;
+			mi->content_top = mi->content + MAX_BOUNDARIES - 1;
+			return;
+		}
+		*(mi->content_top) = boundary;
+		boundary = NULL;
+	}
+	slurp_attr(line->buf, "charset=", &mi->charset);
+
+	if (boundary) {
+		strbuf_release(boundary);
+		free(boundary);
+	}
+}
+
+static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
+{
+	if (mi->add_message_id)
+		mi->message_id = strdup(line->buf);
+}
+
+static void handle_content_transfer_encoding(struct mailinfo *mi,
+					     const struct strbuf *line)
+{
+	if (strcasestr(line->buf, "base64"))
+		mi->transfer_encoding = TE_BASE64;
+	else if (strcasestr(line->buf, "quoted-printable"))
+		mi->transfer_encoding = TE_QP;
+	else
+		mi->transfer_encoding = TE_DONTCARE;
+}
+
+static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
+{
+	struct strbuf *content_top = *(mi->content_top);
+
+	return ((content_top->len <= line->len) &&
+		!memcmp(line->buf, content_top->buf, content_top->len));
+}
+
+static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
+{
+	size_t at = 0;
+
+	while (at < subject->len) {
+		char *pos;
+		size_t remove;
+
+		switch (subject->buf[at]) {
+		case 'r': case 'R':
+			if (subject->len <= at + 3)
+				break;
+			if ((subject->buf[at + 1] == 'e' ||
+			     subject->buf[at + 1] == 'E') &&
+			    subject->buf[at + 2] == ':') {
+				strbuf_remove(subject, at, 3);
+				continue;
+			}
+			at++;
+			break;
+		case ' ': case '\t': case ':':
+			strbuf_remove(subject, at, 1);
+			continue;
+		case '[':
+			pos = strchr(subject->buf + at, ']');
+			if (!pos)
+				break;
+			remove = pos - subject->buf + at + 1;
+			if (!mi->keep_non_patch_brackets_in_subject ||
+			    (7 <= remove &&
+			     memmem(subject->buf + at, remove, "PATCH", 5)))
+				strbuf_remove(subject, at, remove);
+			else {
+				at += remove;
+				/*
+				 * If the input had a space after the ], keep
+				 * it.  We don't bother with finding the end of
+				 * the space, since we later normalize it
+				 * anyway.
+				 */
+				if (isspace(subject->buf[at]))
+					at += 1;
+			}
+			continue;
+		}
+		break;
+	}
+	strbuf_trim(subject);
+}
+
+#define MAX_HDR_PARSED 10
+
+static const char *header[MAX_HDR_PARSED] = {
+	"From","Subject","Date",
+};
+
+static inline int cmp_header(const struct strbuf *line, const char *hdr)
+{
+	int len = strlen(hdr);
+	return !strncasecmp(line->buf, hdr, len) && line->len > len &&
+			line->buf[len] == ':' && isspace(line->buf[len + 1]);
+}
+
+static int is_format_patch_separator(const char *line, int len)
+{
+	static const char SAMPLE[] =
+		"From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
+	const char *cp;
+
+	if (len != strlen(SAMPLE))
+		return 0;
+	if (!skip_prefix(line, "From ", &cp))
+		return 0;
+	if (strspn(cp, "0123456789abcdef") != 40)
+		return 0;
+	cp += 40;
+	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
+}
+
+static int is_rfc2822_header(const struct strbuf *line)
+{
+	/*
+	 * The section that defines the loosest possible
+	 * field name is "3.6.8 Optional fields".
+	 *
+	 * optional-field = field-name ":" unstructured CRLF
+	 * field-name = 1*ftext
+	 * ftext = %d33-57 / %59-126
+	 */
+	int ch;
+	char *cp = line->buf;
+
+	/* Count mbox From headers as headers */
+	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
+		return 1;
+
+	while ((ch = *cp++)) {
+		if (ch == ':')
+			return 1;
+		if ((33 <= ch && ch <= 57) ||
+		    (59 <= ch && ch <= 126))
+			continue;
+		break;
+	}
+	return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+	/* Get the first part of the line. */
+	if (strbuf_getline(line, in, '\n'))
+		return 0;
+
+	/*
+	 * Is it an empty line or not a valid rfc2822 header?
+	 * If so, stop here, and return false ("not a header")
+	 */
+	strbuf_rtrim(line);
+	if (!line->len || !is_rfc2822_header(line)) {
+		/* Re-add the newline */
+		strbuf_addch(line, '\n');
+		return 0;
+	}
+
+	/*
+	 * Now we need to eat all the continuation lines..
+	 * Yuck, 2822 header "folding"
+	 */
+	for (;;) {
+		int peek;
+		struct strbuf continuation = STRBUF_INIT;
+
+		peek = fgetc(in); ungetc(peek, in);
+		if (peek != ' ' && peek != '\t')
+			break;
+		if (strbuf_getline(&continuation, in, '\n'))
+			break;
+		continuation.buf[0] = ' ';
+		strbuf_rtrim(&continuation);
+		strbuf_addbuf(line, &continuation);
+	}
+
+	return 1;
+}
+
+static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
+{
+	const char *in = q_seg->buf;
+	int c;
+	struct strbuf *out = xmalloc(sizeof(struct strbuf));
+	strbuf_init(out, q_seg->len);
+
+	while ((c = *in++) != 0) {
+		if (c == '=') {
+			int d = *in++;
+			if (d == '\n' || !d)
+				break; /* drop trailing newline */
+			strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
+			continue;
+		}
+		if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
+			c = 0x20;
+		strbuf_addch(out, c);
+	}
+	return out;
+}
+
+static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
+{
+	/* Decode in..ep, possibly in-place to ot */
+	int c, pos = 0, acc = 0;
+	const char *in = b_seg->buf;
+	struct strbuf *out = xmalloc(sizeof(struct strbuf));
+	strbuf_init(out, b_seg->len);
+
+	while ((c = *in++) != 0) {
+		if (c == '+')
+			c = 62;
+		else if (c == '/')
+			c = 63;
+		else if ('A' <= c && c <= 'Z')
+			c -= 'A';
+		else if ('a' <= c && c <= 'z')
+			c -= 'a' - 26;
+		else if ('0' <= c && c <= '9')
+			c -= '0' - 52;
+		else
+			continue; /* garbage */
+		switch (pos++) {
+		case 0:
+			acc = (c << 2);
+			break;
+		case 1:
+			strbuf_addch(out, (acc | (c >> 4)));
+			acc = (c & 15) << 4;
+			break;
+		case 2:
+			strbuf_addch(out, (acc | (c >> 2)));
+			acc = (c & 3) << 6;
+			break;
+		case 3:
+			strbuf_addch(out, (acc | c));
+			acc = pos = 0;
+			break;
+		}
+	}
+	return out;
+}
+
+static int convert_to_utf8(struct mailinfo *mi,
+			   struct strbuf *line, const char *charset)
+{
+	char *out;
+
+	if (!mi->metainfo_charset || !charset || !*charset)
+		return 0;
+
+	if (same_encoding(mi->metainfo_charset, charset))
+		return 0;
+	out = reencode_string(line->buf, mi->metainfo_charset, charset);
+	if (!out) {
+		mi->input_error = -1;
+		return error("cannot convert from %s to %s",
+			     charset, mi->metainfo_charset);
+	}
+	strbuf_attach(line, out, strlen(out), strlen(out));
+	return 0;
+}
+
+static int decode_header(struct mailinfo *mi, struct strbuf *it)
+{
+	char *in, *ep, *cp;
+	struct strbuf outbuf = STRBUF_INIT, *dec;
+	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+	int found_error = -1; /* pessimism */
+
+	in = it->buf;
+	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
+		int encoding;
+		strbuf_reset(&charset_q);
+		strbuf_reset(&piecebuf);
+
+		if (in != ep) {
+			/*
+			 * We are about to process an encoded-word
+			 * that begins at ep, but there is something
+			 * before the encoded word.
+			 */
+			char *scan;
+			for (scan = in; scan < ep; scan++)
+				if (!isspace(*scan))
+					break;
+
+			if (scan != ep || in == it->buf) {
+				/*
+				 * We should not lose that "something",
+				 * unless we have just processed an
+				 * encoded-word, and there is only LWS
+				 * before the one we are about to process.
+				 */
+				strbuf_add(&outbuf, in, ep - in);
+			}
+		}
+		/* E.g.
+		 * ep : "=?iso-2022-jp?B?GyR...?= foo"
+		 * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
+		 */
+		ep += 2;
+
+		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
+			goto release_return;
+
+		if (cp + 3 - it->buf > it->len)
+			goto release_return;
+		strbuf_add(&charset_q, ep, cp - ep);
+
+		encoding = cp[1];
+		if (!encoding || cp[2] != '?')
+			goto release_return;
+		ep = strstr(cp + 3, "?=");
+		if (!ep)
+			goto release_return;
+		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
+		switch (tolower(encoding)) {
+		default:
+			goto release_return;
+		case 'b':
+			dec = decode_b_segment(&piecebuf);
+			break;
+		case 'q':
+			dec = decode_q_segment(&piecebuf, 1);
+			break;
+		}
+		if (convert_to_utf8(mi, dec, charset_q.buf))
+			goto release_return;
+
+		strbuf_addbuf(&outbuf, dec);
+		strbuf_release(dec);
+		free(dec);
+		in = ep + 2;
+	}
+	strbuf_addstr(&outbuf, in);
+	strbuf_reset(it);
+	strbuf_addbuf(it, &outbuf);
+	found_error = 0;
+release_return:
+	strbuf_release(&outbuf);
+	strbuf_release(&charset_q);
+	strbuf_release(&piecebuf);
+
+	return found_error;
+}
+
+static int check_header(struct mailinfo *mi,
+			const struct strbuf *line,
+			struct strbuf *hdr_data[], int overwrite)
+{
+	int i, ret = 0, len;
+	struct strbuf sb = STRBUF_INIT;
+
+	/* search for the interesting parts */
+	for (i = 0; header[i]; i++) {
+		int len = strlen(header[i]);
+		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+			/* Unwrap inline B and Q encoding, and optionally
+			 * normalize the meta information to utf8.
+			 */
+			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+			if (!decode_header(mi, &sb))
+				handle_header(&hdr_data[i], &sb);
+			ret = 1;
+			goto check_header_out;
+		}
+	}
+
+	/* Content stuff */
+	if (cmp_header(line, "Content-Type")) {
+		len = strlen("Content-Type: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		if (!decode_header(mi, &sb)) {
+			strbuf_insert(&sb, 0, "Content-Type: ", len);
+			handle_content_type(mi, &sb);
+		}
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Content-Transfer-Encoding")) {
+		len = strlen("Content-Transfer-Encoding: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		if (!decode_header(mi, &sb))
+			handle_content_transfer_encoding(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Message-Id")) {
+		len = strlen("Message-Id: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		if (!decode_header(mi, &sb))
+			handle_message_id(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+
+	/* for inbody stuff */
+	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
+		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
+		goto check_header_out;
+	}
+	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+		for (i = 0; header[i]; i++) {
+			if (!strcmp("Subject", header[i])) {
+				handle_header(&hdr_data[i], line);
+				ret = 1;
+				goto check_header_out;
+			}
+		}
+	}
+
+check_header_out:
+	strbuf_release(&sb);
+	return ret;
+}
+
+static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
+{
+	struct strbuf *ret;
+
+	switch (mi->transfer_encoding) {
+	case TE_QP:
+		ret = decode_q_segment(line, 0);
+		break;
+	case TE_BASE64:
+		ret = decode_b_segment(line);
+		break;
+	case TE_DONTCARE:
+	default:
+		return;
+	}
+	strbuf_reset(line);
+	strbuf_addbuf(line, ret);
+	strbuf_release(ret);
+	free(ret);
+}
+
+static inline int patchbreak(const struct strbuf *line)
+{
+	size_t i;
+
+	/* Beginning of a "diff -" header? */
+	if (starts_with(line->buf, "diff -"))
+		return 1;
+
+	/* CVS "Index: " line? */
+	if (starts_with(line->buf, "Index: "))
+		return 1;
+
+	/*
+	 * "--- <filename>" starts patches without headers
+	 * "---<sp>*" is a manual separator
+	 */
+	if (line->len < 4)
+		return 0;
+
+	if (starts_with(line->buf, "---")) {
+		/* space followed by a filename? */
+		if (line->buf[3] == ' ' && !isspace(line->buf[4]))
+			return 1;
+		/* Just whitespace? */
+		for (i = 3; i < line->len; i++) {
+			unsigned char c = line->buf[i];
+			if (c == '\n')
+				return 1;
+			if (!isspace(c))
+				break;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+static int is_scissors_line(const struct strbuf *line)
+{
+	size_t i, len = line->len;
+	int scissors = 0, gap = 0;
+	int first_nonblank = -1;
+	int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
+	const char *buf = line->buf;
+
+	for (i = 0; i < len; i++) {
+		if (isspace(buf[i])) {
+			if (in_perforation) {
+				perforation++;
+				gap++;
+			}
+			continue;
+		}
+		last_nonblank = i;
+		if (first_nonblank < 0)
+			first_nonblank = i;
+		if (buf[i] == '-') {
+			in_perforation = 1;
+			perforation++;
+			continue;
+		}
+		if (i + 1 < len &&
+		    (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
+		     !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
+			in_perforation = 1;
+			perforation += 2;
+			scissors += 2;
+			i++;
+			continue;
+		}
+		in_perforation = 0;
+	}
+
+	/*
+	 * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+	 * Even though there can be arbitrary cruft on the same line
+	 * (e.g. "cut here"), in order to avoid misidentification, the
+	 * perforation must occupy more than a third of the visible
+	 * width of the line, and dashes and scissors must occupy more
+	 * than half of the perforation.
+	 */
+
+	visible = last_nonblank - first_nonblank + 1;
+	return (scissors && 8 <= visible &&
+		visible < perforation * 3 &&
+		gap * 2 < perforation);
+}
+
+static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
+{
+	/*
+	 * Are we still scanning and discarding in-body headers?
+	 * It is initially set to 1, set to 2 when we do see a
+	 * valid in-body header.
+	 */
+	int is_empty_line;
+
+	assert(!mi->filter_stage);
+
+	is_empty_line = (!line->len || (line->len == 1 && line->buf[0] == '\n'));
+	if (mi->header_stage == 1) {
+		/*
+		 * Haven't seen a known in-body header; discard an empty line.
+		 */
+		if (is_empty_line)
+			return 0;
+	}
+
+	if (mi->use_inbody_headers && mi->header_stage) {
+		int is_known_header = check_header(mi, line, mi->s_hdr_data, 0);
+
+		if (mi->header_stage == 2) {
+			/*
+			 * an empty line after the in-body header block,
+			 * or a line obviously not an attempt to invent
+			 * an unsupported in-body header.
+			 */
+			if (is_empty_line || !is_rfc2822_header(line))
+				mi->header_stage = 0;
+			if (is_empty_line)
+				return 0;
+			/* otherwise do not discard the line, but keep going */
+		} else if (is_known_header) {
+			mi->header_stage = 2;
+		} else if (mi->header_stage != 2) {
+			mi->header_stage = 0;
+		}
+
+		if (mi->header_stage)
+			return 0;
+	} else
+		/* Only trim the first (blank) line of the commit message
+		 * when ignoring in-body headers.
+		 */
+		mi->header_stage = 0;
+
+	/* normalize the log message to UTF-8. */
+	if (convert_to_utf8(mi, line, mi->charset.buf))
+		return 0; /* the caller will reject this input */
+
+	if (mi->use_scissors && is_scissors_line(line)) {
+		int i;
+
+		strbuf_setlen(&mi->log_message, 0);
+		mi->header_stage = 1;
+
+		/*
+		 * We may have already read "secondary headers"; purge
+		 * them to give ourselves a clean restart.
+		 */
+		for (i = 0; header[i]; i++) {
+			if (mi->s_hdr_data[i])
+				strbuf_release(mi->s_hdr_data[i]);
+			mi->s_hdr_data[i] = NULL;
+		}
+		return 0;
+	}
+
+	if (patchbreak(line)) {
+		if (mi->message_id)
+			strbuf_addf(&mi->log_message,
+				    "Message-Id: %s\n", mi->message_id);
+		return 1;
+	}
+
+	strbuf_addbuf(&mi->log_message, line);
+	return 0;
+}
+
+static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
+{
+	fwrite(line->buf, 1, line->len, mi->patchfile);
+	mi->patch_lines++;
+}
+
+static void handle_filter(struct mailinfo *mi, struct strbuf *line)
+{
+	if (mi->input_error)
+		return;
+
+	switch (mi->filter_stage) {
+	case 0:
+		if (!handle_commit_msg(mi, line))
+			break;
+		mi->filter_stage++;
+	case 1:
+		handle_patch(mi, line);
+		break;
+	}
+}
+
+static int find_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+	while (!strbuf_getline(line, mi->input, '\n')) {
+		if (*(mi->content_top) && is_multipart_boundary(mi, line))
+			return 1;
+	}
+	return 0;
+}
+
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+	struct strbuf newline = STRBUF_INIT;
+
+	strbuf_addch(&newline, '\n');
+again:
+	if (line->len >= (*(mi->content_top))->len + 2 &&
+	    !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
+		/* we hit an end boundary */
+		/* pop the current boundary off the stack */
+		strbuf_release(*(mi->content_top));
+		free(*(mi->content_top));
+		*(mi->content_top) = NULL;
+
+		/* technically won't happen as is_multipart_boundary()
+		   will fail first.  But just in case..
+		 */
+		if (--mi->content_top < mi->content) {
+			error("Detected mismatched boundaries, can't recover");
+			mi->input_error = -1;
+			mi->content_top = mi->content;
+			return 0;
+		}
+		handle_filter(mi, &newline);
+		strbuf_release(&newline);
+		if (mi->input_error)
+			return 0; /* punt to the end */
+
+		/* skip to the next boundary */
+		if (!find_boundary(mi, line))
+			return 0;
+		goto again;
+	}
+
+	/* set some defaults */
+	mi->transfer_encoding = TE_DONTCARE;
+	strbuf_reset(&mi->charset);
+
+	/* slurp in this section's info */
+	while (read_one_header_line(line, mi->input))
+		check_header(mi, line, mi->p_hdr_data, 0);
+
+	strbuf_release(&newline);
+	/* replenish line */
+	if (strbuf_getline(line, mi->input, '\n'))
+		return 0;
+	strbuf_addch(line, '\n');
+	return 1;
+}
+
+static void handle_body(struct mailinfo *mi, struct strbuf *line)
+{
+	struct strbuf prev = STRBUF_INIT;
+
+	/* Skip up to the first boundary */
+	if (*(mi->content_top)) {
+		if (!find_boundary(mi, line))
+			goto handle_body_out;
+	}
+
+	do {
+		/* process any boundary lines */
+		if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
+			/* flush any leftover */
+			if (prev.len) {
+				handle_filter(mi, &prev);
+				strbuf_reset(&prev);
+			}
+			if (!handle_boundary(mi, line))
+				goto handle_body_out;
+		}
+
+		/* Unwrap transfer encoding */
+		decode_transfer_encoding(mi, line);
+
+		switch (mi->transfer_encoding) {
+		case TE_BASE64:
+		case TE_QP:
+		{
+			struct strbuf **lines, **it, *sb;
+
+			/* Prepend any previous partial lines */
+			strbuf_insert(line, 0, prev.buf, prev.len);
+			strbuf_reset(&prev);
+
+			/*
+			 * This is a decoded line that may contain
+			 * multiple new lines.  Pass only one chunk
+			 * at a time to handle_filter()
+			 */
+			lines = strbuf_split(line, '\n');
+			for (it = lines; (sb = *it); it++) {
+				if (*(it + 1) == NULL) /* The last line */
+					if (sb->buf[sb->len - 1] != '\n') {
+						/* Partial line, save it for later. */
+						strbuf_addbuf(&prev, sb);
+						break;
+					}
+				handle_filter(mi, sb);
+			}
+			/*
+			 * The partial chunk is saved in "prev" and will be
+			 * appended by the next iteration of read_line_with_nul().
+			 */
+			strbuf_list_free(lines);
+			break;
+		}
+		default:
+			handle_filter(mi, line);
+		}
+
+		if (mi->input_error)
+			break;
+	} while (!strbuf_getwholeline(line, mi->input, '\n'));
+
+handle_body_out:
+	strbuf_release(&prev);
+}
+
+static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
+{
+	const char *sp = data->buf;
+	while (1) {
+		char *ep = strchr(sp, '\n');
+		int len;
+		if (!ep)
+			len = strlen(sp);
+		else
+			len = ep - sp;
+		fprintf(fout, "%s: %.*s\n", hdr, len, sp);
+		if (!ep)
+			break;
+		sp = ep + 1;
+	}
+}
+
+static void handle_info(struct mailinfo *mi)
+{
+	struct strbuf *hdr;
+	int i;
+
+	for (i = 0; header[i]; i++) {
+		/* only print inbody headers if we output a patch file */
+		if (mi->patch_lines && mi->s_hdr_data[i])
+			hdr = mi->s_hdr_data[i];
+		else if (mi->p_hdr_data[i])
+			hdr = mi->p_hdr_data[i];
+		else
+			continue;
+
+		if (!strcmp(header[i], "Subject")) {
+			if (!mi->keep_subject) {
+				cleanup_subject(mi, hdr);
+				cleanup_space(hdr);
+			}
+			output_header_lines(mi->output, "Subject", hdr);
+		} else if (!strcmp(header[i], "From")) {
+			cleanup_space(hdr);
+			handle_from(mi, hdr);
+			fprintf(mi->output, "Author: %s\n", mi->name.buf);
+			fprintf(mi->output, "Email: %s\n", mi->email.buf);
+		} else {
+			cleanup_space(hdr);
+			fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
+		}
+	}
+	fprintf(mi->output, "\n");
+}
+
+int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
+{
+	FILE *cmitmsg;
+	int peek;
+	struct strbuf line = STRBUF_INIT;
+
+	cmitmsg = fopen(msg, "w");
+	if (!cmitmsg) {
+		perror(msg);
+		return -1;
+	}
+	mi->patchfile = fopen(patch, "w");
+	if (!mi->patchfile) {
+		perror(patch);
+		fclose(cmitmsg);
+		return -1;
+	}
+
+	mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+	mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
+
+	do {
+		peek = fgetc(mi->input);
+	} while (isspace(peek));
+	ungetc(peek, mi->input);
+
+	/* process the email header */
+	while (read_one_header_line(&line, mi->input))
+		check_header(mi, &line, mi->p_hdr_data, 1);
+
+	handle_body(mi, &line);
+	if (!mi->input_error) {
+		handle_info(mi);
+		fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+	}
+	fclose(cmitmsg);
+
+	return mi->input_error;
+}
+
+static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+{
+	struct mailinfo *mi = mi_;
+
+	if (!starts_with(var, "mailinfo."))
+		return git_default_config(var, value, NULL);
+	if (!strcmp(var, "mailinfo.scissors")) {
+		mi->use_scissors = git_config_bool(var, value);
+		return 0;
+	}
+	/* perhaps others here */
+	return 0;
+}
+
+void setup_mailinfo(struct mailinfo *mi)
+{
+	memset(mi, 0, sizeof(*mi));
+	strbuf_init(&mi->name, 0);
+	strbuf_init(&mi->email, 0);
+	strbuf_init(&mi->charset, 0);
+	strbuf_init(&mi->log_message, 0);
+	mi->header_stage = 1;
+	mi->use_inbody_headers = 1;
+	mi->content_top = mi->content;
+	git_config(git_mailinfo_config, &mi);
+}
+
+void clear_mailinfo(struct mailinfo *mi)
+{
+	strbuf_release(&mi->name);
+	strbuf_release(&mi->email);
+	strbuf_release(&mi->charset);
+	strbuf_release(&mi->log_message);
+
+	while (mi->content < mi->content_top) {
+		free(*(mi->content_top));
+		mi->content_top--;
+	}
+}
diff --git a/mailinfo.h b/mailinfo.h
new file mode 100644
index 0000000..e15e0de
--- /dev/null
+++ b/mailinfo.h
@@ -0,0 +1,41 @@
+#ifndef MAILINFO_H
+#define MAILINFO_H
+
+#define MAX_BOUNDARIES 5
+
+struct mailinfo {
+	FILE *input;
+	FILE *output;
+	FILE *patchfile;
+
+	struct strbuf name;
+	struct strbuf email;
+	int keep_subject;
+	int keep_non_patch_brackets_in_subject;
+	int add_message_id;
+	int use_scissors;
+	int use_inbody_headers; /* defaults to 1 */
+	const char *metainfo_charset;
+
+	struct strbuf *content[MAX_BOUNDARIES];
+	struct strbuf **content_top;
+	struct strbuf charset;
+	char *message_id;
+	enum  {
+		TE_DONTCARE, TE_QP, TE_BASE64
+	} transfer_encoding;
+	int patch_lines;
+	int filter_stage; /* still reading log or are we copying patch? */
+	int header_stage; /* still checking in-body headers? */
+	struct strbuf **p_hdr_data;
+	struct strbuf **s_hdr_data;
+
+	struct strbuf log_message;
+	int input_error;
+};
+
+extern void setup_mailinfo(struct mailinfo *);
+extern void clear_mailinfo(struct mailinfo *);
+extern int mailinfo(struct mailinfo *, const char *msg, const char *patch);
+
+#endif /* MAILINFO_H */
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 27/26] mailinfo: close patchfile
  2015-10-13 23:16 ` [PATCH 19/26] mailinfo: move cmitmsg and patchfile to struct mailinfo Junio C Hamano
@ 2015-10-14  1:37   ` Junio C Hamano
  2015-10-14  1:46     ` [PATCH 28/26] am: make direct call to mailinfo Junio C Hamano
  0 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14  1:37 UTC (permalink / raw)
  To: git

"git mailinfo" itself did not need this, as open file handles are
flushed and closed upon process exit, but the libified version needs
to clean up after itself when it is done.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 mailinfo.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mailinfo.c b/mailinfo.c
index ca40030..00e2ea4 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -1013,7 +1013,7 @@ int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 		fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
 	}
 	fclose(cmitmsg);
-
+	fclose(mi->patchfile);
 	return mi->input_error;
 }
 
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 28/26] am: make direct call to mailinfo
  2015-10-14  1:37   ` [PATCH 27/26] mailinfo: close patchfile Junio C Hamano
@ 2015-10-14  1:46     ` Junio C Hamano
  0 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14  1:46 UTC (permalink / raw)
  To: git

And finally the endgame.  Instead of spawning "git mailinfo" via the
run_command() API the same number of times as there are incoming
patches, make direct internal call to the libified mailinfo() from
"git am" to reduce the spawning overhead, which would matter on some
platforms.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 This is to be applied on top of a merge across

  - jc/am-3-fallback-regression-fix
  - jc/mailinfo-lib (i.e. the 27-patch series)

 As I do not have ready access to such a platform with slow
 run_command(), it would be nice to see some numbers produced on
 such platform by other people.

 Thanks.

 builtin/am.c | 38 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index c869796..0471355 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -27,6 +27,7 @@
 #include "notes-utils.h"
 #include "rerere.h"
 #include "prompt.h"
+#include "mailinfo.h"
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -1258,58 +1259,57 @@ static void am_append_signoff(struct am_state *state)
 static int parse_mail(struct am_state *state, const char *mail)
 {
 	FILE *fp;
-	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf msg = STRBUF_INIT;
 	struct strbuf author_name = STRBUF_INIT;
 	struct strbuf author_date = STRBUF_INIT;
 	struct strbuf author_email = STRBUF_INIT;
 	int ret = 0;
+	struct mailinfo mi;
 
-	cp.git_cmd = 1;
-	cp.in = xopen(mail, O_RDONLY, 0);
-	cp.out = xopen(am_path(state, "info"), O_WRONLY | O_CREAT, 0777);
+	setup_mailinfo(&mi);
 
-	argv_array_push(&cp.args, "mailinfo");
-	argv_array_push(&cp.args, state->utf8 ? "-u" : "-n");
+	if (state->utf8)
+		mi.metainfo_charset = get_commit_output_encoding();
+	else
+		mi.metainfo_charset = NULL;
 
 	switch (state->keep) {
 	case KEEP_FALSE:
 		break;
 	case KEEP_TRUE:
-		argv_array_push(&cp.args, "-k");
+		mi.keep_subject = 1;
 		break;
 	case KEEP_NON_PATCH:
-		argv_array_push(&cp.args, "-b");
+		mi.keep_non_patch_brackets_in_subject = 1;
 		break;
 	default:
 		die("BUG: invalid value for state->keep");
 	}
-
 	if (state->message_id)
-		argv_array_push(&cp.args, "-m");
+		mi.add_message_id = 1;
 
 	switch (state->scissors) {
 	case SCISSORS_UNSET:
 		break;
 	case SCISSORS_FALSE:
-		argv_array_push(&cp.args, "--no-scissors");
+		mi.use_scissors = 0;
 		break;
 	case SCISSORS_TRUE:
-		argv_array_push(&cp.args, "--scissors");
+		mi.use_scissors = 1;
 		break;
 	default:
 		die("BUG: invalid value for state->scissors");
 	}
 
-	argv_array_push(&cp.args, am_path(state, "msg"));
-	argv_array_push(&cp.args, am_path(state, "patch"));
+	mi.input = fopen(mail, "r");
+	mi.output = fopen(am_path(state, "info"), "w");
 
-	if (run_command(&cp) < 0)
+	if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
 		die("could not parse patch");
 
-	close(cp.in);
-	close(cp.out);
+	fclose(mi.input);
+	fclose(mi.output);
 
 	/* Extract message and author information */
 	fp = xfopen(am_path(state, "info"), "r");
@@ -1341,8 +1341,7 @@ static int parse_mail(struct am_state *state, const char *mail)
 	}
 
 	strbuf_addstr(&msg, "\n\n");
-	if (strbuf_read_file(&msg, am_path(state, "msg"), 0) < 0)
-		die_errno(_("could not read '%s'"), am_path(state, "msg"));
+	strbuf_addbuf(&msg, &mi.log_message);
 	stripspace(&msg, 0);
 
 	if (state->signoff)
@@ -1366,6 +1365,7 @@ finish:
 	strbuf_release(&author_email);
 	strbuf_release(&author_name);
 	strbuf_release(&sb);
+	clear_mailinfo(&mi);
 	return ret;
 }
 
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/26] mailinfo: fix for off-by-one error in boundary stack
  2015-10-13 23:16 ` [PATCH 02/26] mailinfo: fix for off-by-one error in boundary stack Junio C Hamano
@ 2015-10-14 20:12   ` Stefan Beller
  2015-10-14 20:28     ` Junio C Hamano
  0 siblings, 1 reply; 124+ messages in thread
From: Stefan Beller @ 2015-10-14 20:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Oct 13, 2015 at 4:16 PM, Junio C Hamano <gitster@pobox.com> wrote:
> We pre-increment the pointer that we will use to store something at,
> so the pointer is already beyond the end of the array if it points
> at content[MAX_BOUNDARIES].
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>
>  As always, I am very bad at checking and fixing off-by-one errors.
>  A few extra sets of eyeballs are very much appreciated.

some relevant lines from the file:
    #define MAX_BOUNDARIES 5
    static struct strbuf *content[MAX_BOUNDARIES];
    static struct strbuf **content_top = content;

content has slots 0..4, so checking content[5] is out of range,
but was allowed in the original code.

So this patch looks good to me, though looking at
289796dd29dd656734cfd59b657deb943a71cf6a,
makes me wonder if we forget the other spot where it's
decremented in that patch.

Also looking at the patch, makes we wonder if we rather want to change
-    static struct strbuf *content[MAX_BOUNDARIES];
+    static struct strbuf *content[MAX_BOUNDARIES + 1];

instead? That would actually increase the allocated memory,
and allow to write one more content line, but reading the code is
easier as we don't need to reason about > or >=.

Another way would be to rewrite this part to use an index instead
of content_top being a pointer. And the index could be compared
to strictly less than MAX_BOUNDARIES.

> ---
>  builtin/mailinfo.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
> index 330bef4..2742d0d 100644
> --- a/builtin/mailinfo.c
> +++ b/builtin/mailinfo.c
> @@ -185,7 +185,7 @@ static void handle_content_type(struct strbuf *line)
>
>         if (slurp_attr(line->buf, "boundary=", boundary)) {
>                 strbuf_insert(boundary, 0, "--", 2);
> -               if (++content_top > &content[MAX_BOUNDARIES]) {
> +               if (++content_top >= &content[MAX_BOUNDARIES]) {
>                         fprintf(stderr, "Too many boundaries to handle\n");
>                         exit(1);
>                 }
> --
> 2.6.1-320-g86a1181
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 06/26] mailinfo: always pass "line" as an argument
  2015-10-13 23:16 ` [PATCH 06/26] mailinfo: always pass "line" as an argument Junio C Hamano
@ 2015-10-14 20:22   ` Stefan Beller
  2015-10-14 20:27     ` Junio C Hamano
  0 siblings, 1 reply; 124+ messages in thread
From: Stefan Beller @ 2015-10-14 20:22 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Oct 13, 2015 at 4:16 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Some functions in this module accessed the global "struct strbuf
> line" while many others used a strbuf passed as an argument.
> Convert the former to ensure that nobody deeper in the callchains
> relies on the global one.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  builtin/mailinfo.c | 48 ++++++++++++++++++++++++------------------------
>  1 file changed, 24 insertions(+), 24 deletions(-)
>
> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
> index 5a74811..c3c7d67 100644
> --- a/builtin/mailinfo.c
> +++ b/builtin/mailinfo.c
> @@ -12,7 +12,7 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
>  static int keep_subject;
>  static int keep_non_patch_brackets_in_subject;
>  static const char *metainfo_charset;
> -static struct strbuf line = STRBUF_INIT;
> +static struct strbuf line_global = STRBUF_INIT;
>  static struct strbuf name = STRBUF_INIT;
>  static struct strbuf email = STRBUF_INIT;
>  static char *message_id;
> @@ -817,23 +817,23 @@ static void handle_filter(struct strbuf *line, int *filter_stage, int *header_st
>         }
>  }
>
> -static int find_boundary(void)
> +static int find_boundary(struct strbuf *line)
>  {
> -       while (!strbuf_getline(&line, fin, '\n')) {
> -               if (*content_top && is_multipart_boundary(&line))
> +       while (!strbuf_getline(line, fin, '\n')) {
> +               if (*content_top && is_multipart_boundary(line))
>                         return 1;
>         }
>         return 0;
>  }
>
> -static int handle_boundary(int *filter_stage, int *header_stage)
> +static int handle_boundary(struct strbuf *line, int *filter_stage, int *header_stage)
>  {
>         struct strbuf newline = STRBUF_INIT;
>
>         strbuf_addch(&newline, '\n');
>  again:
> -       if (line.len >= (*content_top)->len + 2 &&
> -           !memcmp(line.buf + (*content_top)->len, "--", 2)) {
> +       if (line->len >= (*content_top)->len + 2 &&
> +           !memcmp(line->buf + (*content_top)->len, "--", 2)) {
>                 /* we hit an end boundary */
>                 /* pop the current boundary off the stack */
>                 strbuf_release(*content_top);
> @@ -852,7 +852,7 @@ again:
>                 strbuf_release(&newline);
>
>                 /* skip to the next boundary */
> -               if (!find_boundary())
> +               if (!find_boundary(line))
>                         return 0;
>                 goto again;
>         }
> @@ -862,18 +862,18 @@ again:
>         strbuf_reset(&charset);
>
>         /* slurp in this section's info */
> -       while (read_one_header_line(&line, fin))
> -               check_header(&line, p_hdr_data, 0);
> +       while (read_one_header_line(line, fin))
> +               check_header(line, p_hdr_data, 0);
>
>         strbuf_release(&newline);
>         /* replenish line */
> -       if (strbuf_getline(&line, fin, '\n'))
> +       if (strbuf_getline(line, fin, '\n'))
>                 return 0;
> -       strbuf_addch(&line, '\n');
> +       strbuf_addch(line, '\n');
>         return 1;
>  }
>
> -static void handle_body(void)
> +static void handle_body(struct strbuf *line)
>  {
>         struct strbuf prev = STRBUF_INIT;
>         int filter_stage = 0;
> @@ -881,24 +881,24 @@ static void handle_body(void)
>
>         /* Skip up to the first boundary */
>         if (*content_top) {
> -               if (!find_boundary())
> +               if (!find_boundary(line))
>                         goto handle_body_out;
>         }
>
>         do {
>                 /* process any boundary lines */
> -               if (*content_top && is_multipart_boundary(&line)) {
> +               if (*content_top && is_multipart_boundary(line)) {
>                         /* flush any leftover */
>                         if (prev.len) {
>                                 handle_filter(&prev, &filter_stage, &header_stage);
>                                 strbuf_reset(&prev);
>                         }
> -                       if (!handle_boundary(&filter_stage, &header_stage))
> +                       if (!handle_boundary(line, &filter_stage, &header_stage))
>                                 goto handle_body_out;
>                 }
>
>                 /* Unwrap transfer encoding */
> -               decode_transfer_encoding(&line);
> +               decode_transfer_encoding(line);
>
>                 switch (transfer_encoding) {
>                 case TE_BASE64:
> @@ -907,7 +907,7 @@ static void handle_body(void)
>                         struct strbuf **lines, **it, *sb;
>
>                         /* Prepend any previous partial lines */
> -                       strbuf_insert(&line, 0, prev.buf, prev.len);
> +                       strbuf_insert(line, 0, prev.buf, prev.len);
>                         strbuf_reset(&prev);
>
>                         /*
> @@ -915,7 +915,7 @@ static void handle_body(void)
>                          * multiple new lines.  Pass only one chunk
>                          * at a time to handle_filter()
>                          */
> -                       lines = strbuf_split(&line, '\n');
> +                       lines = strbuf_split(line, '\n');
>                         for (it = lines; (sb = *it); it++) {
>                                 if (*(it + 1) == NULL) /* The last line */
>                                         if (sb->buf[sb->len - 1] != '\n') {
> @@ -933,10 +933,10 @@ static void handle_body(void)
>                         break;
>                 }
>                 default:
> -                       handle_filter(&line, &filter_stage, &header_stage);
> +                       handle_filter(line, &filter_stage, &header_stage);
>                 }
>
> -       } while (!strbuf_getwholeline(&line, fin, '\n'));
> +       } while (!strbuf_getwholeline(line, fin, '\n'));
>
>  handle_body_out:
>         strbuf_release(&prev);
> @@ -1019,10 +1019,10 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
>         ungetc(peek, in);
>
>         /* process the email header */
> -       while (read_one_header_line(&line, fin))
> -               check_header(&line, p_hdr_data, 1);
> +       while (read_one_header_line(&line_global, fin))
> +               check_header(&line_global, p_hdr_data, 1);

This is the only function to use line_global if I see correctly.
The function is called only once, so no need to preserve state
outside the function. Would it make sense to remove line_global
completely and have a local variable in this function instead?

>
> -       handle_body();
> +       handle_body(&line_global);
>         handle_info();
>
>         return 0;
> --
> 2.6.1-320-g86a1181
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 06/26] mailinfo: always pass "line" as an argument
  2015-10-14 20:22   ` Stefan Beller
@ 2015-10-14 20:27     ` Junio C Hamano
  0 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:27 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git

Stefan Beller <sbeller@google.com> writes:

>> @@ -1019,10 +1019,10 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
>>         ungetc(peek, in);
>>
>>         /* process the email header */
>> -       while (read_one_header_line(&line, fin))
>> -               check_header(&line, p_hdr_data, 1);
>> +       while (read_one_header_line(&line_global, fin))
>> +               check_header(&line_global, p_hdr_data, 1);
>
> This is the only function to use line_global if I see correctly.
> The function is called only once, so no need to preserve state
> outside the function. Would it make sense to remove line_global
> completely and have a local variable in this function instead?

That is exactly the step that comes after it does, but if you squash
06 and 07 into one patch (i.e. take diff between the state after 05
and after 07), that realization will not easily come (well, at least
it didn't come to me and I wasn't convinced that the conversion is
correct myself until I split 06 and 07 into two separate steps).

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 07/26] mailinfo: move global "line" into mailinfo() function
  2015-10-13 23:16 ` [PATCH 07/26] mailinfo: move global "line" into mailinfo() function Junio C Hamano
@ 2015-10-14 20:27   ` Stefan Beller
  0 siblings, 0 replies; 124+ messages in thread
From: Stefan Beller @ 2015-10-14 20:27 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Oct 13, 2015 at 4:16 PM, Junio C Hamano <gitster@pobox.com> wrote:
> The mailinfo() function is the only one that wants the "line_global"
> to be directly touchable.  Note that handle_body() has to be passed
> this strbuf so that it sees the "first line of the input" after the
> loop in this function processes the headers.  It feels a bit dirty
> that handle_body() then keeps reusing this strbuf to read more lines
> and does its processing, but that is how the code is structured now.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ok, that is exactly what I thought about squashing into patch 6.
A separate patch will do too.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/26] mailinfo: fix for off-by-one error in boundary stack
  2015-10-14 20:12   ` Stefan Beller
@ 2015-10-14 20:28     ` Junio C Hamano
  0 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:28 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git

I'll be sending out v2 very soon, so you might want to hold off for
a few minutes.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* [PATCH v2 00/31] libify mailinfo and call it directly from am
  2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
                   ` (25 preceding siblings ...)
  2015-10-13 23:16 ` [PATCH 26/26] mailinfo: libify the whole thing Junio C Hamano
@ 2015-10-14 20:45 ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 01/31] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
                     ` (31 more replies)
  26 siblings, 32 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

During the discussion on the recent "git am" regression, I noticed
that the command reimplemented in C spawns one "mailsplit" and then
spawns "mailinfo" followed by "apply --index" to commit the changes
described in each message.  As there are platforms where spawning
subprocess via run_command() interface is heavy-weight, something
that is conceptually very simple like "mailinfo" is better called
directly inside the process---something that is lightweight and
frequently used is where the overhead of run_command() would be felt
most.

And here is that topic, slightly reordered and polished up, since
yesterday's version.

 - The series no longer depends on an unrelated "how about this" patch
   to mailinfo.

 - Fixes are moved to earlier spots in the series.

 - Error checking in the endgame patch has been tightened.

Junio C Hamano (31):
  mailinfo: remove a no-op call convert_to_utf8(it, "")
  mailinfo: fix for off-by-one error in boundary stack
  mailinfo: explicitly close file handle to the patch output
  mailinfo: fold decode_header_bq() into decode_header()
  mailinfo: move handle_boundary() lower
  mailinfo: get rid of function-local static states
  mailinfo: always pass "line" as an argument
  mailinfo: move global "line" into mailinfo() function
  mailinfo: introduce "struct mailinfo" to hold globals
  mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  mailinfo: move filter/header stage to struct mailinfo
  mailinfo: move patch_lines to struct mailinfo
  mailinfo: move add_message_id and message_id to struct mailinfo
  mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  mailinfo: move metainfo_charset to struct mailinfo
  mailinfo: move transfer_encoding to struct mailinfo
  mailinfo: move charset to struct mailinfo
  mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  mailinfo: move cmitmsg and patchfile to struct mailinfo
  mailinfo: move [ps]_hdr_data to struct mailinfo
  mailinfo: keep the parsed log message in a strbuf
  mailinfo: move content/content_top to struct mailinfo
  mailinfo: move read_one_header_line() closer to its callers
  mailinfo: move check_header() after the helpers it uses
  mailinfo: move cleanup_space() before its users
  mailinfo: move definition of MAX_HDR_PARSED to closer to its use
  mailinfo: libify
  mailinfo: handle charset conversion errors in the caller
  mailinfo: remove calls to exit() and die() deep in the callchain
  am: make direct call to mailinfo

 Makefile           |    1 +
 builtin/am.c       |   42 ++-
 builtin/mailinfo.c | 1055 +---------------------------------------------------
 mailinfo.c         | 1024 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mailinfo.h         |   41 ++
 5 files changed, 1109 insertions(+), 1054 deletions(-)
 create mode 100644 mailinfo.c
 create mode 100644 mailinfo.h

-- 
2.6.1-320-g86a1181

^ permalink raw reply	[flat|nested] 124+ messages in thread

* [PATCH v2 01/31] mailinfo: remove a no-op call convert_to_utf8(it, "")
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 02/31] mailinfo: fix for off-by-one error in boundary stack Junio C Hamano
                     ` (30 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

The called function checks if the second parameter is either a NULL
or an empty string at the very beginning and returns without doing
anything.  Remove the useless call.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 999a525..5a4ed75 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -612,11 +612,6 @@ static void decode_header(struct strbuf *it)
 {
 	if (decode_header_bq(it))
 		return;
-	/* otherwise "it" is a straight copy of the input.
-	 * This can be binary guck but there is no charset specified.
-	 */
-	if (metainfo_charset)
-		convert_to_utf8(it, "");
 }
 
 static void decode_transfer_encoding(struct strbuf *line)
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 02/31] mailinfo: fix for off-by-one error in boundary stack
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 01/31] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 03/31] mailinfo: explicitly close file handle to the patch output Junio C Hamano
                     ` (29 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

We pre-increment the pointer that we will use to store something at,
so the pointer is already beyond the end of the array if it points
at content[MAX_BOUNDARIES].

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 5a4ed75..d3756cc 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -185,7 +185,7 @@ static void handle_content_type(struct strbuf *line)
 
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
-		if (++content_top > &content[MAX_BOUNDARIES]) {
+		if (++content_top >= &content[MAX_BOUNDARIES]) {
 			fprintf(stderr, "Too many boundaries to handle\n");
 			exit(1);
 		}
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 03/31] mailinfo: explicitly close file handle to the patch output
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 01/31] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 02/31] mailinfo: fix for off-by-one error in boundary stack Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 04/31] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
                     ` (28 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

This does not make a difference within the context of "git mailinfo"
that runs once and exits, as flushing and closing would happen upon
process completion.  It however will matter when we eventually make
this function callable as an API function.

Besides, cleaning after yourself once you are done is a good hygiene.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index d3756cc..412a23b 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -1008,6 +1008,8 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 		check_header(&line, p_hdr_data, 1);
 
 	handle_body();
+	fclose(patchfile);
+
 	handle_info();
 
 	return 0;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 04/31] mailinfo: fold decode_header_bq() into decode_header()
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (2 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 03/31] mailinfo: explicitly close file handle to the patch output Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 05/31] mailinfo: move handle_boundary() lower Junio C Hamano
                     ` (27 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

In olden days we might have wanted to behave differently in
decode_header() if the header line was encoded with RFC2047, but we
apparently do not do so, hence this helper function can go, together
with its return value.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 412a23b..73be47c 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -525,19 +525,17 @@ static void convert_to_utf8(struct strbuf *line, const char *charset)
 	strbuf_attach(line, out, strlen(out), strlen(out));
 }
 
-static int decode_header_bq(struct strbuf *it)
+static void decode_header(struct strbuf *it)
 {
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
 	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
-	int rfc2047 = 0;
 
 	in = it->buf;
 	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
 		int encoding;
 		strbuf_reset(&charset_q);
 		strbuf_reset(&piecebuf);
-		rfc2047 = 1;
 
 		if (in != ep) {
 			/*
@@ -567,22 +565,22 @@ static int decode_header_bq(struct strbuf *it)
 		ep += 2;
 
 		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
-			goto decode_header_bq_out;
+			goto release_return;
 
 		if (cp + 3 - it->buf > it->len)
-			goto decode_header_bq_out;
+			goto release_return;
 		strbuf_add(&charset_q, ep, cp - ep);
 
 		encoding = cp[1];
 		if (!encoding || cp[2] != '?')
-			goto decode_header_bq_out;
+			goto release_return;
 		ep = strstr(cp + 3, "?=");
 		if (!ep)
-			goto decode_header_bq_out;
+			goto release_return;
 		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
 		switch (tolower(encoding)) {
 		default:
-			goto decode_header_bq_out;
+			goto release_return;
 		case 'b':
 			dec = decode_b_segment(&piecebuf);
 			break;
@@ -601,17 +599,10 @@ static int decode_header_bq(struct strbuf *it)
 	strbuf_addstr(&outbuf, in);
 	strbuf_reset(it);
 	strbuf_addbuf(it, &outbuf);
-decode_header_bq_out:
+release_return:
 	strbuf_release(&outbuf);
 	strbuf_release(&charset_q);
 	strbuf_release(&piecebuf);
-	return rfc2047;
-}
-
-static void decode_header(struct strbuf *it)
-{
-	if (decode_header_bq(it))
-		return;
 }
 
 static void decode_transfer_encoding(struct strbuf *line)
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 05/31] mailinfo: move handle_boundary() lower
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (3 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 04/31] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 06/31] mailinfo: get rid of function-local static states Junio C Hamano
                     ` (26 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

This function wants to call find_boundary() and is called only from
one place without any recursing, so it becomes easier to read if it
appears after the called function.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 114 ++++++++++++++++++++++++++---------------------------
 1 file changed, 56 insertions(+), 58 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 73be47c..2b7f97b 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -626,64 +626,6 @@ static void decode_transfer_encoding(struct strbuf *line)
 	free(ret);
 }
 
-static void handle_filter(struct strbuf *line);
-
-static int find_boundary(void)
-{
-	while (!strbuf_getline(&line, fin, '\n')) {
-		if (*content_top && is_multipart_boundary(&line))
-			return 1;
-	}
-	return 0;
-}
-
-static int handle_boundary(void)
-{
-	struct strbuf newline = STRBUF_INIT;
-
-	strbuf_addch(&newline, '\n');
-again:
-	if (line.len >= (*content_top)->len + 2 &&
-	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
-		/* we hit an end boundary */
-		/* pop the current boundary off the stack */
-		strbuf_release(*content_top);
-		free(*content_top);
-		*content_top = NULL;
-
-		/* technically won't happen as is_multipart_boundary()
-		   will fail first.  But just in case..
-		 */
-		if (--content_top < content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
-		}
-		handle_filter(&newline);
-		strbuf_release(&newline);
-
-		/* skip to the next boundary */
-		if (!find_boundary())
-			return 0;
-		goto again;
-	}
-
-	/* set some defaults */
-	transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&charset);
-
-	/* slurp in this section's info */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 0);
-
-	strbuf_release(&newline);
-	/* replenish line */
-	if (strbuf_getline(&line, fin, '\n'))
-		return 0;
-	strbuf_addch(&line, '\n');
-	return 1;
-}
-
 static inline int patchbreak(const struct strbuf *line)
 {
 	size_t i;
@@ -851,6 +793,62 @@ static void handle_filter(struct strbuf *line)
 	}
 }
 
+static int find_boundary(void)
+{
+	while (!strbuf_getline(&line, fin, '\n')) {
+		if (*content_top && is_multipart_boundary(&line))
+			return 1;
+	}
+	return 0;
+}
+
+static int handle_boundary(void)
+{
+	struct strbuf newline = STRBUF_INIT;
+
+	strbuf_addch(&newline, '\n');
+again:
+	if (line.len >= (*content_top)->len + 2 &&
+	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
+		/* we hit an end boundary */
+		/* pop the current boundary off the stack */
+		strbuf_release(*content_top);
+		free(*content_top);
+		*content_top = NULL;
+
+		/* technically won't happen as is_multipart_boundary()
+		   will fail first.  But just in case..
+		 */
+		if (--content_top < content) {
+			fprintf(stderr, "Detected mismatched boundaries, "
+					"can't recover\n");
+			exit(1);
+		}
+		handle_filter(&newline);
+		strbuf_release(&newline);
+
+		/* skip to the next boundary */
+		if (!find_boundary())
+			return 0;
+		goto again;
+	}
+
+	/* set some defaults */
+	transfer_encoding = TE_DONTCARE;
+	strbuf_reset(&charset);
+
+	/* slurp in this section's info */
+	while (read_one_header_line(&line, fin))
+		check_header(&line, p_hdr_data, 0);
+
+	strbuf_release(&newline);
+	/* replenish line */
+	if (strbuf_getline(&line, fin, '\n'))
+		return 0;
+	strbuf_addch(&line, '\n');
+	return 1;
+}
+
 static void handle_body(void)
 {
 	struct strbuf prev = STRBUF_INIT;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 06/31] mailinfo: get rid of function-local static states
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (4 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 05/31] mailinfo: move handle_boundary() lower Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 07/31] mailinfo: always pass "line" as an argument Junio C Hamano
                     ` (25 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Two helper functions use "static int" in their scope to keep track
of the state while repeatedly getting called once for each input
line.  Move these state variables their ultimate caller and pass
down pointers to them, as a small step in preparation for making
this entire callchain more reentrant.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 41 +++++++++++++++++++----------------------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 2b7f97b..1518708 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -713,27 +713,25 @@ static int is_scissors_line(const struct strbuf *line)
 		gap * 2 < perforation);
 }
 
-static int handle_commit_msg(struct strbuf *line)
+static int handle_commit_msg(struct strbuf *line, int *still_looking)
 {
-	static int still_looking = 1;
-
 	if (!cmitmsg)
 		return 0;
 
-	if (still_looking) {
+	if (*still_looking) {
 		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
 			return 0;
 	}
 
-	if (use_inbody_headers && still_looking) {
-		still_looking = check_header(line, s_hdr_data, 0);
-		if (still_looking)
+	if (use_inbody_headers && *still_looking) {
+		*still_looking = check_header(line, s_hdr_data, 0);
+		if (*still_looking)
 			return 0;
 	} else
 		/* Only trim the first (blank) line of the commit message
 		 * when ignoring in-body headers.
 		 */
-		still_looking = 0;
+		*still_looking = 0;
 
 	/* normalize the log message to UTF-8. */
 	if (metainfo_charset)
@@ -745,7 +743,7 @@ static int handle_commit_msg(struct strbuf *line)
 			die_errno("Could not rewind output message file");
 		if (ftruncate(fileno(cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
-		still_looking = 1;
+		*still_looking = 1;
 
 		/*
 		 * We may have already read "secondary headers"; purge
@@ -777,16 +775,13 @@ static void handle_patch(const struct strbuf *line)
 	patch_lines++;
 }
 
-static void handle_filter(struct strbuf *line)
+static void handle_filter(struct strbuf *line, int *filter_stage, int *header_stage)
 {
-	static int filter = 0;
-
-	/* filter tells us which part we left off on */
-	switch (filter) {
+	switch (*filter_stage) {
 	case 0:
-		if (!handle_commit_msg(line))
+		if (!handle_commit_msg(line, header_stage))
 			break;
-		filter++;
+		(*filter_stage)++;
 	case 1:
 		handle_patch(line);
 		break;
@@ -802,7 +797,7 @@ static int find_boundary(void)
 	return 0;
 }
 
-static int handle_boundary(void)
+static int handle_boundary(int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -824,7 +819,7 @@ again:
 					"can't recover\n");
 			exit(1);
 		}
-		handle_filter(&newline);
+		handle_filter(&newline, filter_stage, header_stage);
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
@@ -852,6 +847,8 @@ again:
 static void handle_body(void)
 {
 	struct strbuf prev = STRBUF_INIT;
+	int filter_stage = 0;
+	int header_stage = 1;
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
@@ -864,10 +861,10 @@ static void handle_body(void)
 		if (*content_top && is_multipart_boundary(&line)) {
 			/* flush any leftover */
 			if (prev.len) {
-				handle_filter(&prev);
+				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary())
+			if (!handle_boundary(&filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
@@ -897,7 +894,7 @@ static void handle_body(void)
 						strbuf_addbuf(&prev, sb);
 						break;
 					}
-				handle_filter(sb);
+				handle_filter(sb, &filter_stage, &header_stage);
 			}
 			/*
 			 * The partial chunk is saved in "prev" and will be
@@ -907,7 +904,7 @@ static void handle_body(void)
 			break;
 		}
 		default:
-			handle_filter(&line);
+			handle_filter(&line, &filter_stage, &header_stage);
 		}
 
 	} while (!strbuf_getwholeline(&line, fin, '\n'));
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 07/31] mailinfo: always pass "line" as an argument
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (5 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 06/31] mailinfo: get rid of function-local static states Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 08/31] mailinfo: move global "line" into mailinfo() function Junio C Hamano
                     ` (24 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Some functions in this module accessed the global "struct strbuf
line" while many others used a strbuf passed as an argument.
Convert the former to ensure that nobody deeper in the callchains
relies on the global one.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 48 ++++++++++++++++++++++++------------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 1518708..721d999 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,7 +12,7 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
-static struct strbuf line = STRBUF_INIT;
+static struct strbuf line_global = STRBUF_INIT;
 static struct strbuf name = STRBUF_INIT;
 static struct strbuf email = STRBUF_INIT;
 static char *message_id;
@@ -788,23 +788,23 @@ static void handle_filter(struct strbuf *line, int *filter_stage, int *header_st
 	}
 }
 
-static int find_boundary(void)
+static int find_boundary(struct strbuf *line)
 {
-	while (!strbuf_getline(&line, fin, '\n')) {
-		if (*content_top && is_multipart_boundary(&line))
+	while (!strbuf_getline(line, fin, '\n')) {
+		if (*content_top && is_multipart_boundary(line))
 			return 1;
 	}
 	return 0;
 }
 
-static int handle_boundary(int *filter_stage, int *header_stage)
+static int handle_boundary(struct strbuf *line, int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
 	strbuf_addch(&newline, '\n');
 again:
-	if (line.len >= (*content_top)->len + 2 &&
-	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
+	if (line->len >= (*content_top)->len + 2 &&
+	    !memcmp(line->buf + (*content_top)->len, "--", 2)) {
 		/* we hit an end boundary */
 		/* pop the current boundary off the stack */
 		strbuf_release(*content_top);
@@ -823,7 +823,7 @@ again:
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
-		if (!find_boundary())
+		if (!find_boundary(line))
 			return 0;
 		goto again;
 	}
@@ -833,18 +833,18 @@ again:
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 0);
+	while (read_one_header_line(line, fin))
+		check_header(line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
-	if (strbuf_getline(&line, fin, '\n'))
+	if (strbuf_getline(line, fin, '\n'))
 		return 0;
-	strbuf_addch(&line, '\n');
+	strbuf_addch(line, '\n');
 	return 1;
 }
 
-static void handle_body(void)
+static void handle_body(struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
 	int filter_stage = 0;
@@ -852,24 +852,24 @@ static void handle_body(void)
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
-		if (!find_boundary())
+		if (!find_boundary(line))
 			goto handle_body_out;
 	}
 
 	do {
 		/* process any boundary lines */
-		if (*content_top && is_multipart_boundary(&line)) {
+		if (*content_top && is_multipart_boundary(line)) {
 			/* flush any leftover */
 			if (prev.len) {
 				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(&filter_stage, &header_stage))
+			if (!handle_boundary(line, &filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
 		/* Unwrap transfer encoding */
-		decode_transfer_encoding(&line);
+		decode_transfer_encoding(line);
 
 		switch (transfer_encoding) {
 		case TE_BASE64:
@@ -878,7 +878,7 @@ static void handle_body(void)
 			struct strbuf **lines, **it, *sb;
 
 			/* Prepend any previous partial lines */
-			strbuf_insert(&line, 0, prev.buf, prev.len);
+			strbuf_insert(line, 0, prev.buf, prev.len);
 			strbuf_reset(&prev);
 
 			/*
@@ -886,7 +886,7 @@ static void handle_body(void)
 			 * multiple new lines.  Pass only one chunk
 			 * at a time to handle_filter()
 			 */
-			lines = strbuf_split(&line, '\n');
+			lines = strbuf_split(line, '\n');
 			for (it = lines; (sb = *it); it++) {
 				if (*(it + 1) == NULL) /* The last line */
 					if (sb->buf[sb->len - 1] != '\n') {
@@ -904,10 +904,10 @@ static void handle_body(void)
 			break;
 		}
 		default:
-			handle_filter(&line, &filter_stage, &header_stage);
+			handle_filter(line, &filter_stage, &header_stage);
 		}
 
-	} while (!strbuf_getwholeline(&line, fin, '\n'));
+	} while (!strbuf_getwholeline(line, fin, '\n'));
 
 handle_body_out:
 	strbuf_release(&prev);
@@ -990,10 +990,10 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 	ungetc(peek, in);
 
 	/* process the email header */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 1);
+	while (read_one_header_line(&line_global, fin))
+		check_header(&line_global, p_hdr_data, 1);
 
-	handle_body();
+	handle_body(&line_global);
 	fclose(patchfile);
 
 	handle_info();
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 08/31] mailinfo: move global "line" into mailinfo() function
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (6 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 07/31] mailinfo: always pass "line" as an argument Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 09/31] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
                     ` (23 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

With the pregvious step, it becomes clear that the mailinfo()
function is the only one that wants the "line_global" to be directly
touchable.

Logically, this strbuf belongs to handle_body().  It passes the line
to its helper functions for processing, and keeps one line at a time
into the variable in the loop to drive that process.  It would have
been even nicer looking if we could make the variable local to the
function.  But the function has to be passed its initial value
(i.e. the first line is read by its caller), and that is why its
sole caller owns it in the resulting code structure.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 721d999..c8dc73f 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,7 +12,6 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
-static struct strbuf line_global = STRBUF_INIT;
 static struct strbuf name = STRBUF_INIT;
 static struct strbuf email = STRBUF_INIT;
 static char *message_id;
@@ -966,6 +965,8 @@ static void handle_info(void)
 static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 {
 	int peek;
+	struct strbuf line = STRBUF_INIT;
+
 	fin = in;
 	fout = out;
 
@@ -990,10 +991,10 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 	ungetc(peek, in);
 
 	/* process the email header */
-	while (read_one_header_line(&line_global, fin))
-		check_header(&line_global, p_hdr_data, 1);
+	while (read_one_header_line(&line, fin))
+		check_header(&line, p_hdr_data, 1);
 
-	handle_body(&line_global);
+	handle_body(&line);
 	fclose(patchfile);
 
 	handle_info();
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 09/31] mailinfo: introduce "struct mailinfo" to hold globals
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (7 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 08/31] mailinfo: move global "line" into mailinfo() function Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 10/31] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
                     ` (22 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Initially have only 'email' and 'name' fields in there and remove
the corresponding globals.  In subsequent patches, more globals will
be moved to this and the structure will be passed around as a new
parameter to more functions.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 61 +++++++++++++++++++++++++++++++++---------------------
 1 file changed, 37 insertions(+), 24 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index c8dc73f..9e41a10 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,8 +12,11 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
-static struct strbuf name = STRBUF_INIT;
-static struct strbuf email = STRBUF_INIT;
+
+struct mailinfo {
+	struct strbuf name;
+	struct strbuf email;
+};
 static char *message_id;
 
 static enum  {
@@ -45,7 +48,7 @@ static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf
 	strbuf_addbuf(out, src);
 }
 
-static void parse_bogus_from(const struct strbuf *line)
+static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
 {
 	/* John Doe <johndoe> */
 
@@ -53,7 +56,7 @@ static void parse_bogus_from(const struct strbuf *line)
 	/* This is fallback, so do not bother if we already have an
 	 * e-mail address.
 	 */
-	if (email.len)
+	if (mi->email.len)
 		return;
 
 	bra = strchr(line->buf, '<');
@@ -63,16 +66,16 @@ static void parse_bogus_from(const struct strbuf *line)
 	if (!ket)
 		return;
 
-	strbuf_reset(&email);
-	strbuf_add(&email, bra + 1, ket - bra - 1);
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, bra + 1, ket - bra - 1);
 
-	strbuf_reset(&name);
-	strbuf_add(&name, line->buf, bra - line->buf);
-	strbuf_trim(&name);
-	get_sane_name(&name, &name, &email);
+	strbuf_reset(&mi->name);
+	strbuf_add(&mi->name, line->buf, bra - line->buf);
+	strbuf_trim(&mi->name);
+	get_sane_name(&mi->name, &mi->name, &mi->email);
 }
 
-static void handle_from(const struct strbuf *from)
+static void handle_from(struct mailinfo *mi, const struct strbuf *from)
 {
 	char *at;
 	size_t el;
@@ -83,14 +86,14 @@ static void handle_from(const struct strbuf *from)
 
 	at = strchr(f.buf, '@');
 	if (!at) {
-		parse_bogus_from(from);
+		parse_bogus_from(mi, from);
 		return;
 	}
 
 	/*
 	 * If we already have one email, don't take any confusing lines
 	 */
-	if (email.len && strchr(at + 1, '@')) {
+	if (mi->email.len && strchr(at + 1, '@')) {
 		strbuf_release(&f);
 		return;
 	}
@@ -109,8 +112,8 @@ static void handle_from(const struct strbuf *from)
 		at--;
 	}
 	el = strcspn(at, " \n\t\r\v\f>");
-	strbuf_reset(&email);
-	strbuf_add(&email, at, el);
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, at, el);
 	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
 
 	/* The remainder is name.  It could be
@@ -132,7 +135,7 @@ static void handle_from(const struct strbuf *from)
 		strbuf_setlen(&f, f.len - 1);
 	}
 
-	get_sane_name(&name, &f, &email);
+	get_sane_name(&mi->name, &f, &mi->email);
 	strbuf_release(&f);
 }
 
@@ -929,7 +932,7 @@ static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf
 	}
 }
 
-static void handle_info(void)
+static void handle_info(struct mailinfo *mi)
 {
 	struct strbuf *hdr;
 	int i;
@@ -951,9 +954,9 @@ static void handle_info(void)
 			output_header_lines(fout, "Subject", hdr);
 		} else if (!strcmp(header[i], "From")) {
 			cleanup_space(hdr);
-			handle_from(hdr);
-			fprintf(fout, "Author: %s\n", name.buf);
-			fprintf(fout, "Email: %s\n", email.buf);
+			handle_from(mi, hdr);
+			fprintf(fout, "Author: %s\n", mi->name.buf);
+			fprintf(fout, "Email: %s\n", mi->email.buf);
 		} else {
 			cleanup_space(hdr);
 			fprintf(fout, "%s: %s\n", header[i], hdr->buf);
@@ -962,7 +965,8 @@ static void handle_info(void)
 	fprintf(fout, "\n");
 }
 
-static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
+static int mailinfo(struct mailinfo *mi,
+		    FILE *in, FILE *out, const char *msg, const char *patch)
 {
 	int peek;
 	struct strbuf line = STRBUF_INIT;
@@ -997,7 +1001,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 	handle_body(&line);
 	fclose(patchfile);
 
-	handle_info();
+	handle_info(mi);
 
 	return 0;
 }
@@ -1014,17 +1018,26 @@ static int git_mailinfo_config(const char *var, const char *value, void *unused)
 	return 0;
 }
 
+static void setup_mailinfo(struct mailinfo *mi)
+{
+	memset(mi, 0, sizeof(*mi));
+	strbuf_init(&mi->name, 0);
+	strbuf_init(&mi->email, 0);
+	git_config(git_mailinfo_config, &mi);
+}
+
 static const char mailinfo_usage[] =
 	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
 
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
 	const char *def_charset;
+	struct mailinfo mi;
 
 	/* NEEDSWORK: might want to do the optional .git/ directory
 	 * discovery
 	 */
-	git_config(git_mailinfo_config, NULL);
+	setup_mailinfo(&mi);
 
 	def_charset = get_commit_output_encoding();
 	metainfo_charset = def_charset;
@@ -1056,5 +1069,5 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	if (argc != 3)
 		usage(mailinfo_usage);
 
-	return !!mailinfo(stdin, stdout, argv[1], argv[2]);
+	return !!mailinfo(&mi, stdin, stdout, argv[1], argv[2]);
 }
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 10/31] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (8 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 09/31] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 11/31] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
                     ` (21 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

These two are the only easy ones that do not require passing the
structure around to deep corners of the callchain.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 9e41a10..855d813 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -9,13 +9,13 @@
 
 static FILE *cmitmsg, *patchfile, *fin, *fout;
 
-static int keep_subject;
-static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
 
 struct mailinfo {
 	struct strbuf name;
 	struct strbuf email;
+	int keep_subject;
+	int keep_non_patch_brackets_in_subject;
 };
 static char *message_id;
 
@@ -224,7 +224,7 @@ static int is_multipart_boundary(const struct strbuf *line)
 		!memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
 }
 
-static void cleanup_subject(struct strbuf *subject)
+static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
 {
 	size_t at = 0;
 
@@ -252,7 +252,7 @@ static void cleanup_subject(struct strbuf *subject)
 			if (!pos)
 				break;
 			remove = pos - subject->buf + at + 1;
-			if (!keep_non_patch_brackets_in_subject ||
+			if (!mi->keep_non_patch_brackets_in_subject ||
 			    (7 <= remove &&
 			     memmem(subject->buf + at, remove, "PATCH", 5)))
 				strbuf_remove(subject, at, remove);
@@ -947,8 +947,8 @@ static void handle_info(struct mailinfo *mi)
 			continue;
 
 		if (!strcmp(header[i], "Subject")) {
-			if (!keep_subject) {
-				cleanup_subject(hdr);
+			if (!mi->keep_subject) {
+				cleanup_subject(mi, hdr);
 				cleanup_space(hdr);
 			}
 			output_header_lines(fout, "Subject", hdr);
@@ -1044,9 +1044,9 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 
 	while (1 < argc && argv[1][0] == '-') {
 		if (!strcmp(argv[1], "-k"))
-			keep_subject = 1;
+			mi.keep_subject = 1;
 		else if (!strcmp(argv[1], "-b"))
-			keep_non_patch_brackets_in_subject = 1;
+			mi.keep_non_patch_brackets_in_subject = 1;
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
 			add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 11/31] mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (9 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 10/31] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 12/31] mailinfo: move filter/header stage " Junio C Hamano
                     ` (20 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

This requires us to pass "struct mailinfo" to more functions
throughout the codepath that read input lines, which makes
later steps easier.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 54 ++++++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 26 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 855d813..68a085e 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,11 +7,14 @@
 #include "utf8.h"
 #include "strbuf.h"
 
-static FILE *cmitmsg, *patchfile, *fin, *fout;
+static FILE *cmitmsg, *patchfile;
 
 static const char *metainfo_charset;
 
 struct mailinfo {
+	FILE *input;
+	FILE *output;
+
 	struct strbuf name;
 	struct strbuf email;
 	int keep_subject;
@@ -790,16 +793,17 @@ static void handle_filter(struct strbuf *line, int *filter_stage, int *header_st
 	}
 }
 
-static int find_boundary(struct strbuf *line)
+static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 {
-	while (!strbuf_getline(line, fin, '\n')) {
+	while (!strbuf_getline(line, mi->input, '\n')) {
 		if (*content_top && is_multipart_boundary(line))
 			return 1;
 	}
 	return 0;
 }
 
-static int handle_boundary(struct strbuf *line, int *filter_stage, int *header_stage)
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line,
+			   int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -825,7 +829,7 @@ again:
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
-		if (!find_boundary(line))
+		if (!find_boundary(mi, line))
 			return 0;
 		goto again;
 	}
@@ -835,18 +839,18 @@ again:
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
-	while (read_one_header_line(line, fin))
+	while (read_one_header_line(line, mi->input))
 		check_header(line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
-	if (strbuf_getline(line, fin, '\n'))
+	if (strbuf_getline(line, mi->input, '\n'))
 		return 0;
 	strbuf_addch(line, '\n');
 	return 1;
 }
 
-static void handle_body(struct strbuf *line)
+static void handle_body(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
 	int filter_stage = 0;
@@ -854,7 +858,7 @@ static void handle_body(struct strbuf *line)
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
-		if (!find_boundary(line))
+		if (!find_boundary(mi, line))
 			goto handle_body_out;
 	}
 
@@ -866,7 +870,7 @@ static void handle_body(struct strbuf *line)
 				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(line, &filter_stage, &header_stage))
+			if (!handle_boundary(mi, line, &filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
@@ -909,7 +913,7 @@ static void handle_body(struct strbuf *line)
 			handle_filter(line, &filter_stage, &header_stage);
 		}
 
-	} while (!strbuf_getwholeline(line, fin, '\n'));
+	} while (!strbuf_getwholeline(line, mi->input, '\n'));
 
 handle_body_out:
 	strbuf_release(&prev);
@@ -951,29 +955,25 @@ static void handle_info(struct mailinfo *mi)
 				cleanup_subject(mi, hdr);
 				cleanup_space(hdr);
 			}
-			output_header_lines(fout, "Subject", hdr);
+			output_header_lines(mi->output, "Subject", hdr);
 		} else if (!strcmp(header[i], "From")) {
 			cleanup_space(hdr);
 			handle_from(mi, hdr);
-			fprintf(fout, "Author: %s\n", mi->name.buf);
-			fprintf(fout, "Email: %s\n", mi->email.buf);
+			fprintf(mi->output, "Author: %s\n", mi->name.buf);
+			fprintf(mi->output, "Email: %s\n", mi->email.buf);
 		} else {
 			cleanup_space(hdr);
-			fprintf(fout, "%s: %s\n", header[i], hdr->buf);
+			fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
 		}
 	}
-	fprintf(fout, "\n");
+	fprintf(mi->output, "\n");
 }
 
-static int mailinfo(struct mailinfo *mi,
-		    FILE *in, FILE *out, const char *msg, const char *patch)
+static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 {
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	fin = in;
-	fout = out;
-
 	cmitmsg = fopen(msg, "w");
 	if (!cmitmsg) {
 		perror(msg);
@@ -990,15 +990,15 @@ static int mailinfo(struct mailinfo *mi,
 	s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
 
 	do {
-		peek = fgetc(in);
+		peek = fgetc(mi->input);
 	} while (isspace(peek));
-	ungetc(peek, in);
+	ungetc(peek, mi->input);
 
 	/* process the email header */
-	while (read_one_header_line(&line, fin))
+	while (read_one_header_line(&line, mi->input))
 		check_header(&line, p_hdr_data, 1);
 
-	handle_body(&line);
+	handle_body(mi, &line);
 	fclose(patchfile);
 
 	handle_info(mi);
@@ -1069,5 +1069,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	if (argc != 3)
 		usage(mailinfo_usage);
 
-	return !!mailinfo(&mi, stdin, stdout, argv[1], argv[2]);
+	mi.input = stdin;
+	mi.output = stdout;
+	return !!mailinfo(&mi, argv[1], argv[2]);
 }
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 12/31] mailinfo: move filter/header stage to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (10 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 11/31] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 13/31] mailinfo: move patch_lines " Junio C Hamano
                     ` (19 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Earlier we got rid of two function-scope static variables that kept
track of the states of helper functions by making them extra arguments
that are passed throughout the callchain.  Now we have a convenient
place to store and pass them around in the form of "struct mailinfo",
change them into two fields in the struct.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 68a085e..5d2d88a 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -19,6 +19,9 @@ struct mailinfo {
 	struct strbuf email;
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
+
+	int filter_stage; /* still reading log or are we copying patch? */
+	int header_stage; /* still checking in-body headers? */
 };
 static char *message_id;
 
@@ -28,6 +31,7 @@ static enum  {
 
 static struct strbuf charset = STRBUF_INIT;
 static int patch_lines;
+
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
 static int add_message_id;
@@ -718,25 +722,25 @@ static int is_scissors_line(const struct strbuf *line)
 		gap * 2 < perforation);
 }
 
-static int handle_commit_msg(struct strbuf *line, int *still_looking)
+static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 {
 	if (!cmitmsg)
 		return 0;
 
-	if (*still_looking) {
+	if (mi->header_stage) {
 		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
 			return 0;
 	}
 
-	if (use_inbody_headers && *still_looking) {
-		*still_looking = check_header(line, s_hdr_data, 0);
-		if (*still_looking)
+	if (use_inbody_headers && mi->header_stage) {
+		mi->header_stage = check_header(line, s_hdr_data, 0);
+		if (mi->header_stage)
 			return 0;
 	} else
 		/* Only trim the first (blank) line of the commit message
 		 * when ignoring in-body headers.
 		 */
-		*still_looking = 0;
+		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
 	if (metainfo_charset)
@@ -748,7 +752,7 @@ static int handle_commit_msg(struct strbuf *line, int *still_looking)
 			die_errno("Could not rewind output message file");
 		if (ftruncate(fileno(cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
-		*still_looking = 1;
+		mi->header_stage = 1;
 
 		/*
 		 * We may have already read "secondary headers"; purge
@@ -780,13 +784,13 @@ static void handle_patch(const struct strbuf *line)
 	patch_lines++;
 }
 
-static void handle_filter(struct strbuf *line, int *filter_stage, int *header_stage)
+static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 {
-	switch (*filter_stage) {
+	switch (mi->filter_stage) {
 	case 0:
-		if (!handle_commit_msg(line, header_stage))
+		if (!handle_commit_msg(mi, line))
 			break;
-		(*filter_stage)++;
+		mi->filter_stage++;
 	case 1:
 		handle_patch(line);
 		break;
@@ -802,8 +806,7 @@ static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 	return 0;
 }
 
-static int handle_boundary(struct mailinfo *mi, struct strbuf *line,
-			   int *filter_stage, int *header_stage)
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -825,7 +828,7 @@ again:
 					"can't recover\n");
 			exit(1);
 		}
-		handle_filter(&newline, filter_stage, header_stage);
+		handle_filter(mi, &newline);
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
@@ -853,8 +856,6 @@ again:
 static void handle_body(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
-	int filter_stage = 0;
-	int header_stage = 1;
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
@@ -867,10 +868,10 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 		if (*content_top && is_multipart_boundary(line)) {
 			/* flush any leftover */
 			if (prev.len) {
-				handle_filter(&prev, &filter_stage, &header_stage);
+				handle_filter(mi, &prev);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(mi, line, &filter_stage, &header_stage))
+			if (!handle_boundary(mi, line))
 				goto handle_body_out;
 		}
 
@@ -900,7 +901,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 						strbuf_addbuf(&prev, sb);
 						break;
 					}
-				handle_filter(sb, &filter_stage, &header_stage);
+				handle_filter(mi, sb);
 			}
 			/*
 			 * The partial chunk is saved in "prev" and will be
@@ -910,7 +911,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 			break;
 		}
 		default:
-			handle_filter(line, &filter_stage, &header_stage);
+			handle_filter(mi, line);
 		}
 
 	} while (!strbuf_getwholeline(line, mi->input, '\n'));
@@ -1023,6 +1024,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	memset(mi, 0, sizeof(*mi));
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
+	mi->header_stage = 1;
 	git_config(git_mailinfo_config, &mi);
 }
 
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 13/31] mailinfo: move patch_lines to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (11 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 12/31] mailinfo: move filter/header stage " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 14/31] mailinfo: move add_message_id and message_id " Junio C Hamano
                     ` (18 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

This one is trivial thanks to previous steps that started passing
the structure throughout the input codepaths.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 5d2d88a..f451ba4 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -20,6 +20,7 @@ struct mailinfo {
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
 
+	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
@@ -30,7 +31,6 @@ static enum  {
 } transfer_encoding;
 
 static struct strbuf charset = STRBUF_INIT;
-static int patch_lines;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
@@ -778,10 +778,10 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	return 0;
 }
 
-static void handle_patch(const struct strbuf *line)
+static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
 {
 	fwrite(line->buf, 1, line->len, patchfile);
-	patch_lines++;
+	mi->patch_lines++;
 }
 
 static void handle_filter(struct mailinfo *mi, struct strbuf *line)
@@ -792,7 +792,7 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 			break;
 		mi->filter_stage++;
 	case 1:
-		handle_patch(line);
+		handle_patch(mi, line);
 		break;
 	}
 }
@@ -944,7 +944,7 @@ static void handle_info(struct mailinfo *mi)
 
 	for (i = 0; header[i]; i++) {
 		/* only print inbody headers if we output a patch file */
-		if (patch_lines && s_hdr_data[i])
+		if (mi->patch_lines && s_hdr_data[i])
 			hdr = s_hdr_data[i];
 		else if (p_hdr_data[i])
 			hdr = p_hdr_data[i];
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 14/31] mailinfo: move add_message_id and message_id to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (12 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 13/31] mailinfo: move patch_lines " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 15/31] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
                     ` (17 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

This requires us to pass the structure into check_header() codepath.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index f451ba4..6703453 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -19,12 +19,13 @@ struct mailinfo {
 	struct strbuf email;
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
+	int add_message_id;
 
+	char *message_id;
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
-static char *message_id;
 
 static enum  {
 	TE_DONTCARE, TE_QP, TE_BASE64
@@ -34,7 +35,6 @@ static struct strbuf charset = STRBUF_INIT;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
-static int add_message_id;
 static int use_inbody_headers = 1;
 
 #define MAX_HDR_PARSED 10
@@ -209,10 +209,10 @@ static void handle_content_type(struct strbuf *line)
 	}
 }
 
-static void handle_message_id(const struct strbuf *line)
+static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
 {
-	if (add_message_id)
-		message_id = strdup(line->buf);
+	if (mi->add_message_id)
+		mi->message_id = strdup(line->buf);
 }
 
 static void handle_content_transfer_encoding(const struct strbuf *line)
@@ -321,11 +321,13 @@ static int is_format_patch_separator(const char *line, int len)
 	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
 }
 
-static int check_header(const struct strbuf *line,
-				struct strbuf *hdr_data[], int overwrite)
+static int check_header(struct mailinfo *mi,
+			const struct strbuf *line,
+			struct strbuf *hdr_data[], int overwrite)
 {
 	int i, ret = 0, len;
 	struct strbuf sb = STRBUF_INIT;
+
 	/* search for the interesting parts */
 	for (i = 0; header[i]; i++) {
 		int len = strlen(header[i]);
@@ -363,7 +365,7 @@ static int check_header(const struct strbuf *line,
 		len = strlen("Message-Id: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(&sb);
-		handle_message_id(&sb);
+		handle_message_id(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -733,7 +735,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (use_inbody_headers && mi->header_stage) {
-		mi->header_stage = check_header(line, s_hdr_data, 0);
+		mi->header_stage = check_header(mi, line, s_hdr_data, 0);
 		if (mi->header_stage)
 			return 0;
 	} else
@@ -767,8 +769,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (patchbreak(line)) {
-		if (message_id)
-			fprintf(cmitmsg, "Message-Id: %s\n", message_id);
+		if (mi->message_id)
+			fprintf(cmitmsg, "Message-Id: %s\n", mi->message_id);
 		fclose(cmitmsg);
 		cmitmsg = NULL;
 		return 1;
@@ -843,7 +845,7 @@ again:
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
-		check_header(line, p_hdr_data, 0);
+		check_header(mi, line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
@@ -997,7 +999,7 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
 	/* process the email header */
 	while (read_one_header_line(&line, mi->input))
-		check_header(&line, p_hdr_data, 1);
+		check_header(mi, &line, p_hdr_data, 1);
 
 	handle_body(mi, &line);
 	fclose(patchfile);
@@ -1050,7 +1052,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-b"))
 			mi.keep_non_patch_brackets_in_subject = 1;
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
-			add_message_id = 1;
+			mi.add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
 			metainfo_charset = def_charset;
 		else if (!strcmp(argv[1], "-n"))
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 15/31] mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (13 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 14/31] mailinfo: move add_message_id and message_id " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 16/31] mailinfo: move metainfo_charset " Junio C Hamano
                     ` (16 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 6703453..80034a2 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -20,6 +20,8 @@ struct mailinfo {
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
 	int add_message_id;
+	int use_scissors;
+	int use_inbody_headers; /* defaults to 1 */
 
 	char *message_id;
 	int patch_lines;
@@ -34,8 +36,6 @@ static enum  {
 static struct strbuf charset = STRBUF_INIT;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
-static int use_scissors;
-static int use_inbody_headers = 1;
 
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
@@ -734,7 +734,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 			return 0;
 	}
 
-	if (use_inbody_headers && mi->header_stage) {
+	if (mi->use_inbody_headers && mi->header_stage) {
 		mi->header_stage = check_header(mi, line, s_hdr_data, 0);
 		if (mi->header_stage)
 			return 0;
@@ -748,7 +748,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	if (metainfo_charset)
 		convert_to_utf8(line, charset.buf);
 
-	if (use_scissors && is_scissors_line(line)) {
+	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
 		if (fseek(cmitmsg, 0L, SEEK_SET))
 			die_errno("Could not rewind output message file");
@@ -1009,12 +1009,14 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 	return 0;
 }
 
-static int git_mailinfo_config(const char *var, const char *value, void *unused)
+static int git_mailinfo_config(const char *var, const char *value, void *mi_)
 {
+	struct mailinfo *mi = mi_;
+
 	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, unused);
+		return git_default_config(var, value, NULL);
 	if (!strcmp(var, "mailinfo.scissors")) {
-		use_scissors = git_config_bool(var, value);
+		mi->use_scissors = git_config_bool(var, value);
 		return 0;
 	}
 	/* perhaps others here */
@@ -1027,6 +1029,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
 	mi->header_stage = 1;
+	mi->use_inbody_headers = 1;
 	git_config(git_mailinfo_config, &mi);
 }
 
@@ -1060,11 +1063,11 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (starts_with(argv[1], "--encoding="))
 			metainfo_charset = argv[1] + 11;
 		else if (!strcmp(argv[1], "--scissors"))
-			use_scissors = 1;
+			mi.use_scissors = 1;
 		else if (!strcmp(argv[1], "--no-scissors"))
-			use_scissors = 0;
+			mi.use_scissors = 0;
 		else if (!strcmp(argv[1], "--no-inbody-headers"))
-			use_inbody_headers = 0;
+			mi.use_inbody_headers = 0;
 		else
 			usage(mailinfo_usage);
 		argc--; argv++;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 16/31] mailinfo: move metainfo_charset to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (14 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 15/31] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-15 20:47     ` Eric Sunshine
  2015-10-14 20:45   ` [PATCH v2 17/31] mailinfo: move transfer_encoding " Junio C Hamano
                     ` (15 subsequent siblings)
  31 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

This requires us to pass the struct down to decode_header() and
convert_to_utf8() callchain.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 40 +++++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 80034a2..00b8b4b 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -9,8 +9,6 @@
 
 static FILE *cmitmsg, *patchfile;
 
-static const char *metainfo_charset;
-
 struct mailinfo {
 	FILE *input;
 	FILE *output;
@@ -22,6 +20,7 @@ struct mailinfo {
 	int add_message_id;
 	int use_scissors;
 	int use_inbody_headers; /* defaults to 1 */
+	const char *metainfo_charset;
 
 	char *message_id;
 	int patch_lines;
@@ -293,7 +292,7 @@ static void cleanup_space(struct strbuf *sb)
 	}
 }
 
-static void decode_header(struct strbuf *line);
+static void decode_header(struct mailinfo *mi, struct strbuf *line);
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
@@ -336,7 +335,7 @@ static int check_header(struct mailinfo *mi,
 			 * normalize the meta information to utf8.
 			 */
 			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(&sb);
+			decode_header(mi, &sb);
 			handle_header(&hdr_data[i], &sb);
 			ret = 1;
 			goto check_header_out;
@@ -347,7 +346,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Content-Type")) {
 		len = strlen("Content-Type: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		strbuf_insert(&sb, 0, "Content-Type: ", len);
 		handle_content_type(&sb);
 		ret = 1;
@@ -356,7 +355,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Content-Transfer-Encoding")) {
 		len = strlen("Content-Transfer-Encoding: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		handle_content_transfer_encoding(&sb);
 		ret = 1;
 		goto check_header_out;
@@ -364,7 +363,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Message-Id")) {
 		len = strlen("Message-Id: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		handle_message_id(mi, &sb);
 		ret = 1;
 		goto check_header_out;
@@ -520,23 +519,24 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
 	return out;
 }
 
-static void convert_to_utf8(struct strbuf *line, const char *charset)
+static void convert_to_utf8(struct mailinfo *mi,
+			    struct strbuf *line, const char *charset)
 {
 	char *out;
 
-	if (!charset || !*charset)
+	if (!mi->metainfo_charset || !charset || !*charset)
 		return;
 
-	if (same_encoding(metainfo_charset, charset))
+	if (same_encoding(mi->metainfo_charset, charset))
 		return;
-	out = reencode_string(line->buf, metainfo_charset, charset);
+	out = reencode_string(line->buf, mi->metainfo_charset, charset);
 	if (!out)
 		die("cannot convert from %s to %s",
-		    charset, metainfo_charset);
+		    charset, mi->metainfo_charset);
 	strbuf_attach(line, out, strlen(out), strlen(out));
 }
 
-static void decode_header(struct strbuf *it)
+static void decode_header(struct mailinfo *mi, struct strbuf *it)
 {
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
@@ -599,8 +599,7 @@ static void decode_header(struct strbuf *it)
 			dec = decode_q_segment(&piecebuf, 1);
 			break;
 		}
-		if (metainfo_charset)
-			convert_to_utf8(dec, charset_q.buf);
+		convert_to_utf8(mi, dec, charset_q.buf);
 
 		strbuf_addbuf(&outbuf, dec);
 		strbuf_release(dec);
@@ -745,8 +744,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	if (metainfo_charset)
-		convert_to_utf8(line, charset.buf);
+	convert_to_utf8(mi, line, charset.buf);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -1047,7 +1045,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	setup_mailinfo(&mi);
 
 	def_charset = get_commit_output_encoding();
-	metainfo_charset = def_charset;
+	mi.metainfo_charset = def_charset;
 
 	while (1 < argc && argv[1][0] == '-') {
 		if (!strcmp(argv[1], "-k"))
@@ -1057,11 +1055,11 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
 			mi.add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
-			metainfo_charset = def_charset;
+			mi.metainfo_charset = def_charset;
 		else if (!strcmp(argv[1], "-n"))
-			metainfo_charset = NULL;
+			mi.metainfo_charset = NULL;
 		else if (starts_with(argv[1], "--encoding="))
-			metainfo_charset = argv[1] + 11;
+			mi.metainfo_charset = argv[1] + 11;
 		else if (!strcmp(argv[1], "--scissors"))
 			mi.use_scissors = 1;
 		else if (!strcmp(argv[1], "--no-scissors"))
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 17/31] mailinfo: move transfer_encoding to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (15 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 16/31] mailinfo: move metainfo_charset " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 18/31] mailinfo: move charset " Junio C Hamano
                     ` (14 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 00b8b4b..140b31ee 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -23,14 +23,14 @@ struct mailinfo {
 	const char *metainfo_charset;
 
 	char *message_id;
+	enum  {
+		TE_DONTCARE, TE_QP, TE_BASE64
+	} transfer_encoding;
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
 
-static enum  {
-	TE_DONTCARE, TE_QP, TE_BASE64
-} transfer_encoding;
 
 static struct strbuf charset = STRBUF_INIT;
 
@@ -214,14 +214,15 @@ static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
 		mi->message_id = strdup(line->buf);
 }
 
-static void handle_content_transfer_encoding(const struct strbuf *line)
+static void handle_content_transfer_encoding(struct mailinfo *mi,
+					     const struct strbuf *line)
 {
 	if (strcasestr(line->buf, "base64"))
-		transfer_encoding = TE_BASE64;
+		mi->transfer_encoding = TE_BASE64;
 	else if (strcasestr(line->buf, "quoted-printable"))
-		transfer_encoding = TE_QP;
+		mi->transfer_encoding = TE_QP;
 	else
-		transfer_encoding = TE_DONTCARE;
+		mi->transfer_encoding = TE_DONTCARE;
 }
 
 static int is_multipart_boundary(const struct strbuf *line)
@@ -356,7 +357,7 @@ static int check_header(struct mailinfo *mi,
 		len = strlen("Content-Transfer-Encoding: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(mi, &sb);
-		handle_content_transfer_encoding(&sb);
+		handle_content_transfer_encoding(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -615,11 +616,11 @@ release_return:
 	strbuf_release(&piecebuf);
 }
 
-static void decode_transfer_encoding(struct strbuf *line)
+static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *ret;
 
-	switch (transfer_encoding) {
+	switch (mi->transfer_encoding) {
 	case TE_QP:
 		ret = decode_q_segment(line, 0);
 		break;
@@ -838,7 +839,7 @@ again:
 	}
 
 	/* set some defaults */
-	transfer_encoding = TE_DONTCARE;
+	mi->transfer_encoding = TE_DONTCARE;
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
@@ -876,9 +877,9 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 		}
 
 		/* Unwrap transfer encoding */
-		decode_transfer_encoding(line);
+		decode_transfer_encoding(mi, line);
 
-		switch (transfer_encoding) {
+		switch (mi->transfer_encoding) {
 		case TE_BASE64:
 		case TE_QP:
 		{
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 18/31] mailinfo: move charset to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (16 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 17/31] mailinfo: move transfer_encoding " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 19/31] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
                     ` (13 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 140b31ee..41f659d 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -22,6 +22,7 @@ struct mailinfo {
 	int use_inbody_headers; /* defaults to 1 */
 	const char *metainfo_charset;
 
+	struct strbuf charset;
 	char *message_id;
 	enum  {
 		TE_DONTCARE, TE_QP, TE_BASE64
@@ -31,9 +32,6 @@ struct mailinfo {
 	int header_stage; /* still checking in-body headers? */
 };
 
-
-static struct strbuf charset = STRBUF_INIT;
-
 static struct strbuf **p_hdr_data, **s_hdr_data;
 
 #define MAX_HDR_PARSED 10
@@ -186,7 +184,7 @@ static struct strbuf *content[MAX_BOUNDARIES];
 
 static struct strbuf **content_top = content;
 
-static void handle_content_type(struct strbuf *line)
+static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
 	strbuf_init(boundary, line->len);
@@ -200,7 +198,7 @@ static void handle_content_type(struct strbuf *line)
 		*content_top = boundary;
 		boundary = NULL;
 	}
-	slurp_attr(line->buf, "charset=", &charset);
+	slurp_attr(line->buf, "charset=", &mi->charset);
 
 	if (boundary) {
 		strbuf_release(boundary);
@@ -349,7 +347,7 @@ static int check_header(struct mailinfo *mi,
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(mi, &sb);
 		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(&sb);
+		handle_content_type(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -745,7 +743,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	convert_to_utf8(mi, line, charset.buf);
+	convert_to_utf8(mi, line, mi->charset.buf);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -840,7 +838,7 @@ again:
 
 	/* set some defaults */
 	mi->transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&charset);
+	strbuf_reset(&mi->charset);
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
@@ -1027,6 +1025,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	memset(mi, 0, sizeof(*mi));
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
+	strbuf_init(&mi->charset, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
 	git_config(git_mailinfo_config, &mi);
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 19/31] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (17 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 18/31] mailinfo: move charset " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 20/31] mailinfo: move cmitmsg and patchfile to struct mailinfo Junio C Hamano
                     ` (12 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

There is a strange "if (!cmitmsg) return 0" at the very beginning of
handle_commit_msg(), but the condition should never trigger,
because:

 * The only place cmitmsg is set to NULL is after this function sees
   a patch break, closes the FILE * to write the commit log message
   and returns 1.  This function returns non-zero only from that
   codepath.

 * The caller of this function, upon seeing a non-zero return,
   increments filter_stage, starts treating the input as patch text
   and will never call handle_commit_msg() again.

Replace it with an assert(!mi->filter_stage).

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 41f659d..f48a260 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -724,8 +724,7 @@ static int is_scissors_line(const struct strbuf *line)
 
 static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 {
-	if (!cmitmsg)
-		return 0;
+	assert(!mi->filter_stage);
 
 	if (mi->header_stage) {
 		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 20/31] mailinfo: move cmitmsg and patchfile to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (18 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 19/31] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 22:55     ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 21/31] mailinfo: move [ps]_hdr_data " Junio C Hamano
                     ` (11 subsequent siblings)
  31 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index f48a260..5809e13 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,11 +7,11 @@
 #include "utf8.h"
 #include "strbuf.h"
 
-static FILE *cmitmsg, *patchfile;
-
 struct mailinfo {
 	FILE *input;
 	FILE *output;
+	FILE *cmitmsg;
+	FILE *patchfile;
 
 	struct strbuf name;
 	struct strbuf email;
@@ -746,9 +746,9 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-		if (fseek(cmitmsg, 0L, SEEK_SET))
+		if (fseek(mi->cmitmsg, 0L, SEEK_SET))
 			die_errno("Could not rewind output message file");
-		if (ftruncate(fileno(cmitmsg), 0))
+		if (ftruncate(fileno(mi->cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
 		mi->header_stage = 1;
 
@@ -766,19 +766,19 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (patchbreak(line)) {
 		if (mi->message_id)
-			fprintf(cmitmsg, "Message-Id: %s\n", mi->message_id);
-		fclose(cmitmsg);
-		cmitmsg = NULL;
+			fprintf(mi->cmitmsg, "Message-Id: %s\n", mi->message_id);
+		fclose(mi->cmitmsg);
+		mi->cmitmsg = NULL;
 		return 1;
 	}
 
-	fputs(line->buf, cmitmsg);
+	fputs(line->buf, mi->cmitmsg);
 	return 0;
 }
 
 static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
 {
-	fwrite(line->buf, 1, line->len, patchfile);
+	fwrite(line->buf, 1, line->len, mi->patchfile);
 	mi->patch_lines++;
 }
 
@@ -973,15 +973,15 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	cmitmsg = fopen(msg, "w");
-	if (!cmitmsg) {
+	mi->cmitmsg = fopen(msg, "w");
+	if (!mi->cmitmsg) {
 		perror(msg);
 		return -1;
 	}
-	patchfile = fopen(patch, "w");
-	if (!patchfile) {
+	mi->patchfile = fopen(patch, "w");
+	if (!mi->patchfile) {
 		perror(patch);
-		fclose(cmitmsg);
+		fclose(mi->cmitmsg);
 		return -1;
 	}
 
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 21/31] mailinfo: move [ps]_hdr_data to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (19 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 20/31] mailinfo: move cmitmsg and patchfile to struct mailinfo Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 22:57     ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 22/31] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
                     ` (10 subsequent siblings)
  31 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 5809e13..aa17c77 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -30,10 +30,10 @@ struct mailinfo {
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
+	struct strbuf **p_hdr_data;
+	struct strbuf **s_hdr_data;
 };
 
-static struct strbuf **p_hdr_data, **s_hdr_data;
-
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
 
@@ -732,7 +732,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (mi->use_inbody_headers && mi->header_stage) {
-		mi->header_stage = check_header(mi, line, s_hdr_data, 0);
+		mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);
 		if (mi->header_stage)
 			return 0;
 	} else
@@ -757,9 +757,9 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		 * them to give ourselves a clean restart.
 		 */
 		for (i = 0; header[i]; i++) {
-			if (s_hdr_data[i])
-				strbuf_release(s_hdr_data[i]);
-			s_hdr_data[i] = NULL;
+			if (mi->s_hdr_data[i])
+				strbuf_release(mi->s_hdr_data[i]);
+			mi->s_hdr_data[i] = NULL;
 		}
 		return 0;
 	}
@@ -841,7 +841,7 @@ again:
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
-		check_header(mi, line, p_hdr_data, 0);
+		check_header(mi, line, mi->p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
@@ -942,10 +942,10 @@ static void handle_info(struct mailinfo *mi)
 
 	for (i = 0; header[i]; i++) {
 		/* only print inbody headers if we output a patch file */
-		if (mi->patch_lines && s_hdr_data[i])
-			hdr = s_hdr_data[i];
-		else if (p_hdr_data[i])
-			hdr = p_hdr_data[i];
+		if (mi->patch_lines && mi->s_hdr_data[i])
+			hdr = mi->s_hdr_data[i];
+		else if (mi->p_hdr_data[i])
+			hdr = mi->p_hdr_data[i];
 		else
 			continue;
 
@@ -985,8 +985,8 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 		return -1;
 	}
 
-	p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*p_hdr_data));
-	s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
+	mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+	mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
 
 	do {
 		peek = fgetc(mi->input);
@@ -995,10 +995,10 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
 	/* process the email header */
 	while (read_one_header_line(&line, mi->input))
-		check_header(mi, &line, p_hdr_data, 1);
+		check_header(mi, &line, mi->p_hdr_data, 1);
 
 	handle_body(mi, &line);
-	fclose(patchfile);
+	fclose(mi->patchfile);
 
 	handle_info(mi);
 
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 22/31] mailinfo: keep the parsed log message in a strbuf
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (20 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 21/31] mailinfo: move [ps]_hdr_data " Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 23/31] mailinfo: move content/content_top to struct mailinfo Junio C Hamano
                     ` (9 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

When mailinfo() is eventually libified, the calling "git am" still
will have to write out the log message in the "msg" file for hooks
and other users of the information, but at least it does not have to
reopen and reread what was written back if the function kept it in a
strbuf so that the caller can read it from there.

This also removes the need for seeking and truncating the output
file when we see a scissors mark in the input, which in turn reduces
two callsites of die_errno().

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 40 +++++++++++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 13 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index aa17c77..72668c9 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -10,7 +10,6 @@
 struct mailinfo {
 	FILE *input;
 	FILE *output;
-	FILE *cmitmsg;
 	FILE *patchfile;
 
 	struct strbuf name;
@@ -32,6 +31,8 @@ struct mailinfo {
 	int header_stage; /* still checking in-body headers? */
 	struct strbuf **p_hdr_data;
 	struct strbuf **s_hdr_data;
+
+	struct strbuf log_message;
 };
 
 #define MAX_HDR_PARSED 10
@@ -746,10 +747,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-		if (fseek(mi->cmitmsg, 0L, SEEK_SET))
-			die_errno("Could not rewind output message file");
-		if (ftruncate(fileno(mi->cmitmsg), 0))
-			die_errno("Could not truncate output message file at scissors");
+
+		strbuf_setlen(&mi->log_message, 0);
 		mi->header_stage = 1;
 
 		/*
@@ -766,13 +765,12 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (patchbreak(line)) {
 		if (mi->message_id)
-			fprintf(mi->cmitmsg, "Message-Id: %s\n", mi->message_id);
-		fclose(mi->cmitmsg);
-		mi->cmitmsg = NULL;
+			strbuf_addf(&mi->log_message,
+				    "Message-Id: %s\n", mi->message_id);
 		return 1;
 	}
 
-	fputs(line->buf, mi->cmitmsg);
+	strbuf_addbuf(&mi->log_message, line);
 	return 0;
 }
 
@@ -970,18 +968,19 @@ static void handle_info(struct mailinfo *mi)
 
 static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 {
+	FILE *cmitmsg;
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	mi->cmitmsg = fopen(msg, "w");
-	if (!mi->cmitmsg) {
+	cmitmsg = fopen(msg, "w");
+	if (!cmitmsg) {
 		perror(msg);
 		return -1;
 	}
 	mi->patchfile = fopen(patch, "w");
 	if (!mi->patchfile) {
 		perror(patch);
-		fclose(mi->cmitmsg);
+		fclose(cmitmsg);
 		return -1;
 	}
 
@@ -998,6 +997,8 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 		check_header(mi, &line, mi->p_hdr_data, 1);
 
 	handle_body(mi, &line);
+	fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+	fclose(cmitmsg);
 	fclose(mi->patchfile);
 
 	handle_info(mi);
@@ -1025,11 +1026,20 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
 	strbuf_init(&mi->charset, 0);
+	strbuf_init(&mi->log_message, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
 	git_config(git_mailinfo_config, &mi);
 }
 
+static void clear_mailinfo(struct mailinfo *mi)
+{
+	strbuf_release(&mi->name);
+	strbuf_release(&mi->email);
+	strbuf_release(&mi->charset);
+	strbuf_release(&mi->log_message);
+}
+
 static const char mailinfo_usage[] =
 	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
 
@@ -1037,6 +1047,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
 	const char *def_charset;
 	struct mailinfo mi;
+	int status;
 
 	/* NEEDSWORK: might want to do the optional .git/ directory
 	 * discovery
@@ -1075,5 +1086,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 
 	mi.input = stdin;
 	mi.output = stdout;
-	return !!mailinfo(&mi, argv[1], argv[2]);
+	status = !!mailinfo(&mi, argv[1], argv[2]);
+	clear_mailinfo(&mi);
+
+	return status;
 }
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 23/31] mailinfo: move content/content_top to struct mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (21 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 22/31] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 24/31] mailinfo: move read_one_header_line() closer to its callers Junio C Hamano
                     ` (8 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 45 ++++++++++++++++++++++++++-------------------
 1 file changed, 26 insertions(+), 19 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 72668c9..138ca3b 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,6 +7,8 @@
 #include "utf8.h"
 #include "strbuf.h"
 
+#define MAX_BOUNDARIES 5
+
 struct mailinfo {
 	FILE *input;
 	FILE *output;
@@ -21,6 +23,8 @@ struct mailinfo {
 	int use_inbody_headers; /* defaults to 1 */
 	const char *metainfo_charset;
 
+	struct strbuf *content[MAX_BOUNDARIES];
+	struct strbuf **content_top;
 	struct strbuf charset;
 	char *message_id;
 	enum  {
@@ -36,7 +40,6 @@ struct mailinfo {
 };
 
 #define MAX_HDR_PARSED 10
-#define MAX_BOUNDARIES 5
 
 static void cleanup_space(struct strbuf *sb);
 
@@ -181,10 +184,6 @@ static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
 	return 1;
 }
 
-static struct strbuf *content[MAX_BOUNDARIES];
-
-static struct strbuf **content_top = content;
-
 static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
@@ -192,11 +191,11 @@ static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
-		if (++content_top >= &content[MAX_BOUNDARIES]) {
+		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
 			fprintf(stderr, "Too many boundaries to handle\n");
 			exit(1);
 		}
-		*content_top = boundary;
+		*(mi->content_top) = boundary;
 		boundary = NULL;
 	}
 	slurp_attr(line->buf, "charset=", &mi->charset);
@@ -224,10 +223,12 @@ static void handle_content_transfer_encoding(struct mailinfo *mi,
 		mi->transfer_encoding = TE_DONTCARE;
 }
 
-static int is_multipart_boundary(const struct strbuf *line)
+static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
 {
-	return (((*content_top)->len <= line->len) &&
-		!memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
+	struct strbuf *content_top = *(mi->content_top);
+
+	return ((content_top->len <= line->len) &&
+		!memcmp(line->buf, content_top->buf, content_top->len));
 }
 
 static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
@@ -796,7 +797,7 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 {
 	while (!strbuf_getline(line, mi->input, '\n')) {
-		if (*content_top && is_multipart_boundary(line))
+		if (*(mi->content_top) && is_multipart_boundary(mi, line))
 			return 1;
 	}
 	return 0;
@@ -808,18 +809,18 @@ static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
 
 	strbuf_addch(&newline, '\n');
 again:
-	if (line->len >= (*content_top)->len + 2 &&
-	    !memcmp(line->buf + (*content_top)->len, "--", 2)) {
+	if (line->len >= (*(mi->content_top))->len + 2 &&
+	    !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
 		/* we hit an end boundary */
 		/* pop the current boundary off the stack */
-		strbuf_release(*content_top);
-		free(*content_top);
-		*content_top = NULL;
+		strbuf_release(*(mi->content_top));
+		free(*(mi->content_top));
+		*(mi->content_top) = NULL;
 
 		/* technically won't happen as is_multipart_boundary()
 		   will fail first.  But just in case..
 		 */
-		if (--content_top < content) {
+		if (--mi->content_top < mi->content) {
 			fprintf(stderr, "Detected mismatched boundaries, "
 					"can't recover\n");
 			exit(1);
@@ -854,14 +855,14 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 	struct strbuf prev = STRBUF_INIT;
 
 	/* Skip up to the first boundary */
-	if (*content_top) {
+	if (*(mi->content_top)) {
 		if (!find_boundary(mi, line))
 			goto handle_body_out;
 	}
 
 	do {
 		/* process any boundary lines */
-		if (*content_top && is_multipart_boundary(line)) {
+		if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
 			/* flush any leftover */
 			if (prev.len) {
 				handle_filter(mi, &prev);
@@ -1029,6 +1030,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->log_message, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
+	mi->content_top = mi->content;
 	git_config(git_mailinfo_config, &mi);
 }
 
@@ -1038,6 +1040,11 @@ static void clear_mailinfo(struct mailinfo *mi)
 	strbuf_release(&mi->email);
 	strbuf_release(&mi->charset);
 	strbuf_release(&mi->log_message);
+
+	while (mi->content < mi->content_top) {
+		free(*(mi->content_top));
+		mi->content_top--;
+	}
 }
 
 static const char mailinfo_usage[] =
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 24/31] mailinfo: move read_one_header_line() closer to its callers
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (22 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 23/31] mailinfo: move content/content_top to struct mailinfo Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 25/31] mailinfo: move check_header() after the helpers it uses Junio C Hamano
                     ` (7 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 132 ++++++++++++++++++++++++++---------------------------
 1 file changed, 66 insertions(+), 66 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 138ca3b..0b083d0 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -390,72 +390,6 @@ check_header_out:
 	return ret;
 }
 
-static int is_rfc2822_header(const struct strbuf *line)
-{
-	/*
-	 * The section that defines the loosest possible
-	 * field name is "3.6.8 Optional fields".
-	 *
-	 * optional-field = field-name ":" unstructured CRLF
-	 * field-name = 1*ftext
-	 * ftext = %d33-57 / %59-126
-	 */
-	int ch;
-	char *cp = line->buf;
-
-	/* Count mbox From headers as headers */
-	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
-		return 1;
-
-	while ((ch = *cp++)) {
-		if (ch == ':')
-			return 1;
-		if ((33 <= ch && ch <= 57) ||
-		    (59 <= ch && ch <= 126))
-			continue;
-		break;
-	}
-	return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
-	/* Get the first part of the line. */
-	if (strbuf_getline(line, in, '\n'))
-		return 0;
-
-	/*
-	 * Is it an empty line or not a valid rfc2822 header?
-	 * If so, stop here, and return false ("not a header")
-	 */
-	strbuf_rtrim(line);
-	if (!line->len || !is_rfc2822_header(line)) {
-		/* Re-add the newline */
-		strbuf_addch(line, '\n');
-		return 0;
-	}
-
-	/*
-	 * Now we need to eat all the continuation lines..
-	 * Yuck, 2822 header "folding"
-	 */
-	for (;;) {
-		int peek;
-		struct strbuf continuation = STRBUF_INIT;
-
-		peek = fgetc(in); ungetc(peek, in);
-		if (peek != ' ' && peek != '\t')
-			break;
-		if (strbuf_getline(&continuation, in, '\n'))
-			break;
-		continuation.buf[0] = ' ';
-		strbuf_rtrim(&continuation);
-		strbuf_addbuf(line, &continuation);
-	}
-
-	return 1;
-}
-
 static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
 {
 	const char *in = q_seg->buf;
@@ -794,6 +728,72 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 	}
 }
 
+static int is_rfc2822_header(const struct strbuf *line)
+{
+	/*
+	 * The section that defines the loosest possible
+	 * field name is "3.6.8 Optional fields".
+	 *
+	 * optional-field = field-name ":" unstructured CRLF
+	 * field-name = 1*ftext
+	 * ftext = %d33-57 / %59-126
+	 */
+	int ch;
+	char *cp = line->buf;
+
+	/* Count mbox From headers as headers */
+	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
+		return 1;
+
+	while ((ch = *cp++)) {
+		if (ch == ':')
+			return 1;
+		if ((33 <= ch && ch <= 57) ||
+		    (59 <= ch && ch <= 126))
+			continue;
+		break;
+	}
+	return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+	/* Get the first part of the line. */
+	if (strbuf_getline(line, in, '\n'))
+		return 0;
+
+	/*
+	 * Is it an empty line or not a valid rfc2822 header?
+	 * If so, stop here, and return false ("not a header")
+	 */
+	strbuf_rtrim(line);
+	if (!line->len || !is_rfc2822_header(line)) {
+		/* Re-add the newline */
+		strbuf_addch(line, '\n');
+		return 0;
+	}
+
+	/*
+	 * Now we need to eat all the continuation lines..
+	 * Yuck, 2822 header "folding"
+	 */
+	for (;;) {
+		int peek;
+		struct strbuf continuation = STRBUF_INIT;
+
+		peek = fgetc(in); ungetc(peek, in);
+		if (peek != ' ' && peek != '\t')
+			break;
+		if (strbuf_getline(&continuation, in, '\n'))
+			break;
+		continuation.buf[0] = ' ';
+		strbuf_rtrim(&continuation);
+		strbuf_addbuf(line, &continuation);
+	}
+
+	return 1;
+}
+
 static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 {
 	while (!strbuf_getline(line, mi->input, '\n')) {
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 25/31] mailinfo: move check_header() after the helpers it uses
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (23 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 24/31] mailinfo: move read_one_header_line() closer to its callers Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 26/31] mailinfo: move cleanup_space() before its users Junio C Hamano
                     ` (6 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

This way, we can lose a forward decl for decode_header().

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 139 ++++++++++++++++++++++++++---------------------------
 1 file changed, 69 insertions(+), 70 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 0b083d0..ee669b9 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -293,7 +293,6 @@ static void cleanup_space(struct strbuf *sb)
 	}
 }
 
-static void decode_header(struct mailinfo *mi, struct strbuf *line);
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
@@ -321,75 +320,6 @@ static int is_format_patch_separator(const char *line, int len)
 	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
 }
 
-static int check_header(struct mailinfo *mi,
-			const struct strbuf *line,
-			struct strbuf *hdr_data[], int overwrite)
-{
-	int i, ret = 0, len;
-	struct strbuf sb = STRBUF_INIT;
-
-	/* search for the interesting parts */
-	for (i = 0; header[i]; i++) {
-		int len = strlen(header[i]);
-		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
-			/* Unwrap inline B and Q encoding, and optionally
-			 * normalize the meta information to utf8.
-			 */
-			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(mi, &sb);
-			handle_header(&hdr_data[i], &sb);
-			ret = 1;
-			goto check_header_out;
-		}
-	}
-
-	/* Content stuff */
-	if (cmp_header(line, "Content-Type")) {
-		len = strlen("Content-Type: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Content-Transfer-Encoding")) {
-		len = strlen("Content-Transfer-Encoding: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_content_transfer_encoding(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Message-Id")) {
-		len = strlen("Message-Id: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_message_id(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-
-	/* for inbody stuff */
-	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
-		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
-		goto check_header_out;
-	}
-	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-		for (i = 0; header[i]; i++) {
-			if (!strcmp("Subject", header[i])) {
-				handle_header(&hdr_data[i], line);
-				ret = 1;
-				goto check_header_out;
-			}
-		}
-	}
-
-check_header_out:
-	strbuf_release(&sb);
-	return ret;
-}
-
 static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
 {
 	const char *in = q_seg->buf;
@@ -550,6 +480,75 @@ release_return:
 	strbuf_release(&piecebuf);
 }
 
+static int check_header(struct mailinfo *mi,
+			const struct strbuf *line,
+			struct strbuf *hdr_data[], int overwrite)
+{
+	int i, ret = 0, len;
+	struct strbuf sb = STRBUF_INIT;
+
+	/* search for the interesting parts */
+	for (i = 0; header[i]; i++) {
+		int len = strlen(header[i]);
+		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+			/* Unwrap inline B and Q encoding, and optionally
+			 * normalize the meta information to utf8.
+			 */
+			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+			decode_header(mi, &sb);
+			handle_header(&hdr_data[i], &sb);
+			ret = 1;
+			goto check_header_out;
+		}
+	}
+
+	/* Content stuff */
+	if (cmp_header(line, "Content-Type")) {
+		len = strlen("Content-Type: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		strbuf_insert(&sb, 0, "Content-Type: ", len);
+		handle_content_type(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Content-Transfer-Encoding")) {
+		len = strlen("Content-Transfer-Encoding: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		handle_content_transfer_encoding(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Message-Id")) {
+		len = strlen("Message-Id: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		handle_message_id(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+
+	/* for inbody stuff */
+	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
+		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
+		goto check_header_out;
+	}
+	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+		for (i = 0; header[i]; i++) {
+			if (!strcmp("Subject", header[i])) {
+				handle_header(&hdr_data[i], line);
+				ret = 1;
+				goto check_header_out;
+			}
+		}
+	}
+
+check_header_out:
+	strbuf_release(&sb);
+	return ret;
+}
+
 static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *ret;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 26/31] mailinfo: move cleanup_space() before its users
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (24 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 25/31] mailinfo: move check_header() after the helpers it uses Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 27/31] mailinfo: move definition of MAX_HDR_PARSED to closer to its use Junio C Hamano
                     ` (5 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 25 +++++++++++--------------
 1 file changed, 11 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index ee669b9..f4771ee 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -41,8 +41,17 @@ struct mailinfo {
 
 #define MAX_HDR_PARSED 10
 
-static void cleanup_space(struct strbuf *sb);
-
+static void cleanup_space(struct strbuf *sb)
+{
+	size_t pos, cnt;
+	for (pos = 0; pos < sb->len; pos++) {
+		if (isspace(sb->buf[pos])) {
+			sb->buf[pos] = ' ';
+			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+			strbuf_remove(sb, pos + 1, cnt);
+		}
+	}
+}
 
 static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
 {
@@ -281,18 +290,6 @@ static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
 	strbuf_trim(subject);
 }
 
-static void cleanup_space(struct strbuf *sb)
-{
-	size_t pos, cnt;
-	for (pos = 0; pos < sb->len; pos++) {
-		if (isspace(sb->buf[pos])) {
-			sb->buf[pos] = ' ';
-			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
-			strbuf_remove(sb, pos + 1, cnt);
-		}
-	}
-}
-
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 27/31] mailinfo: move definition of MAX_HDR_PARSED to closer to its use
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (25 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 26/31] mailinfo: move cleanup_space() before its users Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 28/31] mailinfo: libify Junio C Hamano
                     ` (4 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index f4771ee..de446ec 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -39,8 +39,6 @@ struct mailinfo {
 	struct strbuf log_message;
 };
 
-#define MAX_HDR_PARSED 10
-
 static void cleanup_space(struct strbuf *sb)
 {
 	size_t pos, cnt;
@@ -290,6 +288,7 @@ static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
 	strbuf_trim(subject);
 }
 
+#define MAX_HDR_PARSED 10
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 28/31] mailinfo: libify
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (26 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 27/31] mailinfo: move definition of MAX_HDR_PARSED to closer to its use Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 29/31] mailinfo: handle charset conversion errors in the caller Junio C Hamano
                     ` (3 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Move the bulk of the code from builtin/mailinfo.c to mailinfo.c
so that new callers can start calling mailinfo() directly.

Note that a few calls to exit() and die() need to be cleaned up
for the API to be truly useful, which will come in later steps.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Makefile           |    1 +
 builtin/mailinfo.c | 1036 +---------------------------------------------------
 mailinfo.c         | 1007 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mailinfo.h         |   40 ++
 4 files changed, 1049 insertions(+), 1035 deletions(-)
 create mode 100644 mailinfo.c
 create mode 100644 mailinfo.h

diff --git a/Makefile b/Makefile
index 8d5df7e..7dd3bff 100644
--- a/Makefile
+++ b/Makefile
@@ -726,6 +726,7 @@ LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge.o
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index de446ec..f6df274 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -6,1041 +6,7 @@
 #include "builtin.h"
 #include "utf8.h"
 #include "strbuf.h"
-
-#define MAX_BOUNDARIES 5
-
-struct mailinfo {
-	FILE *input;
-	FILE *output;
-	FILE *patchfile;
-
-	struct strbuf name;
-	struct strbuf email;
-	int keep_subject;
-	int keep_non_patch_brackets_in_subject;
-	int add_message_id;
-	int use_scissors;
-	int use_inbody_headers; /* defaults to 1 */
-	const char *metainfo_charset;
-
-	struct strbuf *content[MAX_BOUNDARIES];
-	struct strbuf **content_top;
-	struct strbuf charset;
-	char *message_id;
-	enum  {
-		TE_DONTCARE, TE_QP, TE_BASE64
-	} transfer_encoding;
-	int patch_lines;
-	int filter_stage; /* still reading log or are we copying patch? */
-	int header_stage; /* still checking in-body headers? */
-	struct strbuf **p_hdr_data;
-	struct strbuf **s_hdr_data;
-
-	struct strbuf log_message;
-};
-
-static void cleanup_space(struct strbuf *sb)
-{
-	size_t pos, cnt;
-	for (pos = 0; pos < sb->len; pos++) {
-		if (isspace(sb->buf[pos])) {
-			sb->buf[pos] = ' ';
-			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
-			strbuf_remove(sb, pos + 1, cnt);
-		}
-	}
-}
-
-static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
-{
-	struct strbuf *src = name;
-	if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
-		strchr(name->buf, '<') || strchr(name->buf, '>'))
-		src = email;
-	else if (name == out)
-		return;
-	strbuf_reset(out);
-	strbuf_addbuf(out, src);
-}
-
-static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
-{
-	/* John Doe <johndoe> */
-
-	char *bra, *ket;
-	/* This is fallback, so do not bother if we already have an
-	 * e-mail address.
-	 */
-	if (mi->email.len)
-		return;
-
-	bra = strchr(line->buf, '<');
-	if (!bra)
-		return;
-	ket = strchr(bra, '>');
-	if (!ket)
-		return;
-
-	strbuf_reset(&mi->email);
-	strbuf_add(&mi->email, bra + 1, ket - bra - 1);
-
-	strbuf_reset(&mi->name);
-	strbuf_add(&mi->name, line->buf, bra - line->buf);
-	strbuf_trim(&mi->name);
-	get_sane_name(&mi->name, &mi->name, &mi->email);
-}
-
-static void handle_from(struct mailinfo *mi, const struct strbuf *from)
-{
-	char *at;
-	size_t el;
-	struct strbuf f;
-
-	strbuf_init(&f, from->len);
-	strbuf_addbuf(&f, from);
-
-	at = strchr(f.buf, '@');
-	if (!at) {
-		parse_bogus_from(mi, from);
-		return;
-	}
-
-	/*
-	 * If we already have one email, don't take any confusing lines
-	 */
-	if (mi->email.len && strchr(at + 1, '@')) {
-		strbuf_release(&f);
-		return;
-	}
-
-	/* Pick up the string around '@', possibly delimited with <>
-	 * pair; that is the email part.
-	 */
-	while (at > f.buf) {
-		char c = at[-1];
-		if (isspace(c))
-			break;
-		if (c == '<') {
-			at[-1] = ' ';
-			break;
-		}
-		at--;
-	}
-	el = strcspn(at, " \n\t\r\v\f>");
-	strbuf_reset(&mi->email);
-	strbuf_add(&mi->email, at, el);
-	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
-
-	/* The remainder is name.  It could be
-	 *
-	 * - "John Doe <john.doe@xz>"			(a), or
-	 * - "john.doe@xz (John Doe)"			(b), or
-	 * - "John (zzz) Doe <john.doe@xz> (Comment)"	(c)
-	 *
-	 * but we have removed the email part, so
-	 *
-	 * - remove extra spaces which could stay after email (case 'c'), and
-	 * - trim from both ends, possibly removing the () pair at the end
-	 *   (cases 'a' and 'b').
-	 */
-	cleanup_space(&f);
-	strbuf_trim(&f);
-	if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
-		strbuf_remove(&f, 0, 1);
-		strbuf_setlen(&f, f.len - 1);
-	}
-
-	get_sane_name(&mi->name, &f, &mi->email);
-	strbuf_release(&f);
-}
-
-static void handle_header(struct strbuf **out, const struct strbuf *line)
-{
-	if (!*out) {
-		*out = xmalloc(sizeof(struct strbuf));
-		strbuf_init(*out, line->len);
-	} else
-		strbuf_reset(*out);
-
-	strbuf_addbuf(*out, line);
-}
-
-/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
- * to have enough heuristics to grok MIME encoded patches often found
- * on our mailing lists.  For example, we do not even treat header lines
- * case insensitively.
- */
-
-static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
-{
-	const char *ends, *ap = strcasestr(line, name);
-	size_t sz;
-
-	strbuf_setlen(attr, 0);
-	if (!ap)
-		return 0;
-	ap += strlen(name);
-	if (*ap == '"') {
-		ap++;
-		ends = "\"";
-	}
-	else
-		ends = "; \t";
-	sz = strcspn(ap, ends);
-	strbuf_add(attr, ap, sz);
-	return 1;
-}
-
-static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
-	strbuf_init(boundary, line->len);
-
-	if (slurp_attr(line->buf, "boundary=", boundary)) {
-		strbuf_insert(boundary, 0, "--", 2);
-		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
-			fprintf(stderr, "Too many boundaries to handle\n");
-			exit(1);
-		}
-		*(mi->content_top) = boundary;
-		boundary = NULL;
-	}
-	slurp_attr(line->buf, "charset=", &mi->charset);
-
-	if (boundary) {
-		strbuf_release(boundary);
-		free(boundary);
-	}
-}
-
-static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
-{
-	if (mi->add_message_id)
-		mi->message_id = strdup(line->buf);
-}
-
-static void handle_content_transfer_encoding(struct mailinfo *mi,
-					     const struct strbuf *line)
-{
-	if (strcasestr(line->buf, "base64"))
-		mi->transfer_encoding = TE_BASE64;
-	else if (strcasestr(line->buf, "quoted-printable"))
-		mi->transfer_encoding = TE_QP;
-	else
-		mi->transfer_encoding = TE_DONTCARE;
-}
-
-static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
-{
-	struct strbuf *content_top = *(mi->content_top);
-
-	return ((content_top->len <= line->len) &&
-		!memcmp(line->buf, content_top->buf, content_top->len));
-}
-
-static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
-{
-	size_t at = 0;
-
-	while (at < subject->len) {
-		char *pos;
-		size_t remove;
-
-		switch (subject->buf[at]) {
-		case 'r': case 'R':
-			if (subject->len <= at + 3)
-				break;
-			if ((subject->buf[at + 1] == 'e' ||
-			     subject->buf[at + 1] == 'E') &&
-			    subject->buf[at + 2] == ':') {
-				strbuf_remove(subject, at, 3);
-				continue;
-			}
-			at++;
-			break;
-		case ' ': case '\t': case ':':
-			strbuf_remove(subject, at, 1);
-			continue;
-		case '[':
-			pos = strchr(subject->buf + at, ']');
-			if (!pos)
-				break;
-			remove = pos - subject->buf + at + 1;
-			if (!mi->keep_non_patch_brackets_in_subject ||
-			    (7 <= remove &&
-			     memmem(subject->buf + at, remove, "PATCH", 5)))
-				strbuf_remove(subject, at, remove);
-			else {
-				at += remove;
-				/*
-				 * If the input had a space after the ], keep
-				 * it.  We don't bother with finding the end of
-				 * the space, since we later normalize it
-				 * anyway.
-				 */
-				if (isspace(subject->buf[at]))
-					at += 1;
-			}
-			continue;
-		}
-		break;
-	}
-	strbuf_trim(subject);
-}
-
-#define MAX_HDR_PARSED 10
-static const char *header[MAX_HDR_PARSED] = {
-	"From","Subject","Date",
-};
-
-static inline int cmp_header(const struct strbuf *line, const char *hdr)
-{
-	int len = strlen(hdr);
-	return !strncasecmp(line->buf, hdr, len) && line->len > len &&
-			line->buf[len] == ':' && isspace(line->buf[len + 1]);
-}
-
-static int is_format_patch_separator(const char *line, int len)
-{
-	static const char SAMPLE[] =
-		"From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
-	const char *cp;
-
-	if (len != strlen(SAMPLE))
-		return 0;
-	if (!skip_prefix(line, "From ", &cp))
-		return 0;
-	if (strspn(cp, "0123456789abcdef") != 40)
-		return 0;
-	cp += 40;
-	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
-}
-
-static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
-{
-	const char *in = q_seg->buf;
-	int c;
-	struct strbuf *out = xmalloc(sizeof(struct strbuf));
-	strbuf_init(out, q_seg->len);
-
-	while ((c = *in++) != 0) {
-		if (c == '=') {
-			int d = *in++;
-			if (d == '\n' || !d)
-				break; /* drop trailing newline */
-			strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
-			continue;
-		}
-		if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
-			c = 0x20;
-		strbuf_addch(out, c);
-	}
-	return out;
-}
-
-static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
-{
-	/* Decode in..ep, possibly in-place to ot */
-	int c, pos = 0, acc = 0;
-	const char *in = b_seg->buf;
-	struct strbuf *out = xmalloc(sizeof(struct strbuf));
-	strbuf_init(out, b_seg->len);
-
-	while ((c = *in++) != 0) {
-		if (c == '+')
-			c = 62;
-		else if (c == '/')
-			c = 63;
-		else if ('A' <= c && c <= 'Z')
-			c -= 'A';
-		else if ('a' <= c && c <= 'z')
-			c -= 'a' - 26;
-		else if ('0' <= c && c <= '9')
-			c -= '0' - 52;
-		else
-			continue; /* garbage */
-		switch (pos++) {
-		case 0:
-			acc = (c << 2);
-			break;
-		case 1:
-			strbuf_addch(out, (acc | (c >> 4)));
-			acc = (c & 15) << 4;
-			break;
-		case 2:
-			strbuf_addch(out, (acc | (c >> 2)));
-			acc = (c & 3) << 6;
-			break;
-		case 3:
-			strbuf_addch(out, (acc | c));
-			acc = pos = 0;
-			break;
-		}
-	}
-	return out;
-}
-
-static void convert_to_utf8(struct mailinfo *mi,
-			    struct strbuf *line, const char *charset)
-{
-	char *out;
-
-	if (!mi->metainfo_charset || !charset || !*charset)
-		return;
-
-	if (same_encoding(mi->metainfo_charset, charset))
-		return;
-	out = reencode_string(line->buf, mi->metainfo_charset, charset);
-	if (!out)
-		die("cannot convert from %s to %s",
-		    charset, mi->metainfo_charset);
-	strbuf_attach(line, out, strlen(out), strlen(out));
-}
-
-static void decode_header(struct mailinfo *mi, struct strbuf *it)
-{
-	char *in, *ep, *cp;
-	struct strbuf outbuf = STRBUF_INIT, *dec;
-	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
-
-	in = it->buf;
-	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
-		int encoding;
-		strbuf_reset(&charset_q);
-		strbuf_reset(&piecebuf);
-
-		if (in != ep) {
-			/*
-			 * We are about to process an encoded-word
-			 * that begins at ep, but there is something
-			 * before the encoded word.
-			 */
-			char *scan;
-			for (scan = in; scan < ep; scan++)
-				if (!isspace(*scan))
-					break;
-
-			if (scan != ep || in == it->buf) {
-				/*
-				 * We should not lose that "something",
-				 * unless we have just processed an
-				 * encoded-word, and there is only LWS
-				 * before the one we are about to process.
-				 */
-				strbuf_add(&outbuf, in, ep - in);
-			}
-		}
-		/* E.g.
-		 * ep : "=?iso-2022-jp?B?GyR...?= foo"
-		 * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
-		 */
-		ep += 2;
-
-		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
-			goto release_return;
-
-		if (cp + 3 - it->buf > it->len)
-			goto release_return;
-		strbuf_add(&charset_q, ep, cp - ep);
-
-		encoding = cp[1];
-		if (!encoding || cp[2] != '?')
-			goto release_return;
-		ep = strstr(cp + 3, "?=");
-		if (!ep)
-			goto release_return;
-		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
-		switch (tolower(encoding)) {
-		default:
-			goto release_return;
-		case 'b':
-			dec = decode_b_segment(&piecebuf);
-			break;
-		case 'q':
-			dec = decode_q_segment(&piecebuf, 1);
-			break;
-		}
-		convert_to_utf8(mi, dec, charset_q.buf);
-
-		strbuf_addbuf(&outbuf, dec);
-		strbuf_release(dec);
-		free(dec);
-		in = ep + 2;
-	}
-	strbuf_addstr(&outbuf, in);
-	strbuf_reset(it);
-	strbuf_addbuf(it, &outbuf);
-release_return:
-	strbuf_release(&outbuf);
-	strbuf_release(&charset_q);
-	strbuf_release(&piecebuf);
-}
-
-static int check_header(struct mailinfo *mi,
-			const struct strbuf *line,
-			struct strbuf *hdr_data[], int overwrite)
-{
-	int i, ret = 0, len;
-	struct strbuf sb = STRBUF_INIT;
-
-	/* search for the interesting parts */
-	for (i = 0; header[i]; i++) {
-		int len = strlen(header[i]);
-		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
-			/* Unwrap inline B and Q encoding, and optionally
-			 * normalize the meta information to utf8.
-			 */
-			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(mi, &sb);
-			handle_header(&hdr_data[i], &sb);
-			ret = 1;
-			goto check_header_out;
-		}
-	}
-
-	/* Content stuff */
-	if (cmp_header(line, "Content-Type")) {
-		len = strlen("Content-Type: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Content-Transfer-Encoding")) {
-		len = strlen("Content-Transfer-Encoding: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_content_transfer_encoding(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Message-Id")) {
-		len = strlen("Message-Id: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_message_id(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-
-	/* for inbody stuff */
-	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
-		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
-		goto check_header_out;
-	}
-	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-		for (i = 0; header[i]; i++) {
-			if (!strcmp("Subject", header[i])) {
-				handle_header(&hdr_data[i], line);
-				ret = 1;
-				goto check_header_out;
-			}
-		}
-	}
-
-check_header_out:
-	strbuf_release(&sb);
-	return ret;
-}
-
-static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf *ret;
-
-	switch (mi->transfer_encoding) {
-	case TE_QP:
-		ret = decode_q_segment(line, 0);
-		break;
-	case TE_BASE64:
-		ret = decode_b_segment(line);
-		break;
-	case TE_DONTCARE:
-	default:
-		return;
-	}
-	strbuf_reset(line);
-	strbuf_addbuf(line, ret);
-	strbuf_release(ret);
-	free(ret);
-}
-
-static inline int patchbreak(const struct strbuf *line)
-{
-	size_t i;
-
-	/* Beginning of a "diff -" header? */
-	if (starts_with(line->buf, "diff -"))
-		return 1;
-
-	/* CVS "Index: " line? */
-	if (starts_with(line->buf, "Index: "))
-		return 1;
-
-	/*
-	 * "--- <filename>" starts patches without headers
-	 * "---<sp>*" is a manual separator
-	 */
-	if (line->len < 4)
-		return 0;
-
-	if (starts_with(line->buf, "---")) {
-		/* space followed by a filename? */
-		if (line->buf[3] == ' ' && !isspace(line->buf[4]))
-			return 1;
-		/* Just whitespace? */
-		for (i = 3; i < line->len; i++) {
-			unsigned char c = line->buf[i];
-			if (c == '\n')
-				return 1;
-			if (!isspace(c))
-				break;
-		}
-		return 0;
-	}
-	return 0;
-}
-
-static int is_scissors_line(const struct strbuf *line)
-{
-	size_t i, len = line->len;
-	int scissors = 0, gap = 0;
-	int first_nonblank = -1;
-	int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
-	const char *buf = line->buf;
-
-	for (i = 0; i < len; i++) {
-		if (isspace(buf[i])) {
-			if (in_perforation) {
-				perforation++;
-				gap++;
-			}
-			continue;
-		}
-		last_nonblank = i;
-		if (first_nonblank < 0)
-			first_nonblank = i;
-		if (buf[i] == '-') {
-			in_perforation = 1;
-			perforation++;
-			continue;
-		}
-		if (i + 1 < len &&
-		    (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
-		     !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
-			in_perforation = 1;
-			perforation += 2;
-			scissors += 2;
-			i++;
-			continue;
-		}
-		in_perforation = 0;
-	}
-
-	/*
-	 * The mark must be at least 8 bytes long (e.g. "-- >8 --").
-	 * Even though there can be arbitrary cruft on the same line
-	 * (e.g. "cut here"), in order to avoid misidentification, the
-	 * perforation must occupy more than a third of the visible
-	 * width of the line, and dashes and scissors must occupy more
-	 * than half of the perforation.
-	 */
-
-	visible = last_nonblank - first_nonblank + 1;
-	return (scissors && 8 <= visible &&
-		visible < perforation * 3 &&
-		gap * 2 < perforation);
-}
-
-static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
-{
-	assert(!mi->filter_stage);
-
-	if (mi->header_stage) {
-		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
-			return 0;
-	}
-
-	if (mi->use_inbody_headers && mi->header_stage) {
-		mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);
-		if (mi->header_stage)
-			return 0;
-	} else
-		/* Only trim the first (blank) line of the commit message
-		 * when ignoring in-body headers.
-		 */
-		mi->header_stage = 0;
-
-	/* normalize the log message to UTF-8. */
-	convert_to_utf8(mi, line, mi->charset.buf);
-
-	if (mi->use_scissors && is_scissors_line(line)) {
-		int i;
-
-		strbuf_setlen(&mi->log_message, 0);
-		mi->header_stage = 1;
-
-		/*
-		 * We may have already read "secondary headers"; purge
-		 * them to give ourselves a clean restart.
-		 */
-		for (i = 0; header[i]; i++) {
-			if (mi->s_hdr_data[i])
-				strbuf_release(mi->s_hdr_data[i]);
-			mi->s_hdr_data[i] = NULL;
-		}
-		return 0;
-	}
-
-	if (patchbreak(line)) {
-		if (mi->message_id)
-			strbuf_addf(&mi->log_message,
-				    "Message-Id: %s\n", mi->message_id);
-		return 1;
-	}
-
-	strbuf_addbuf(&mi->log_message, line);
-	return 0;
-}
-
-static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
-{
-	fwrite(line->buf, 1, line->len, mi->patchfile);
-	mi->patch_lines++;
-}
-
-static void handle_filter(struct mailinfo *mi, struct strbuf *line)
-{
-	switch (mi->filter_stage) {
-	case 0:
-		if (!handle_commit_msg(mi, line))
-			break;
-		mi->filter_stage++;
-	case 1:
-		handle_patch(mi, line);
-		break;
-	}
-}
-
-static int is_rfc2822_header(const struct strbuf *line)
-{
-	/*
-	 * The section that defines the loosest possible
-	 * field name is "3.6.8 Optional fields".
-	 *
-	 * optional-field = field-name ":" unstructured CRLF
-	 * field-name = 1*ftext
-	 * ftext = %d33-57 / %59-126
-	 */
-	int ch;
-	char *cp = line->buf;
-
-	/* Count mbox From headers as headers */
-	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
-		return 1;
-
-	while ((ch = *cp++)) {
-		if (ch == ':')
-			return 1;
-		if ((33 <= ch && ch <= 57) ||
-		    (59 <= ch && ch <= 126))
-			continue;
-		break;
-	}
-	return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
-	/* Get the first part of the line. */
-	if (strbuf_getline(line, in, '\n'))
-		return 0;
-
-	/*
-	 * Is it an empty line or not a valid rfc2822 header?
-	 * If so, stop here, and return false ("not a header")
-	 */
-	strbuf_rtrim(line);
-	if (!line->len || !is_rfc2822_header(line)) {
-		/* Re-add the newline */
-		strbuf_addch(line, '\n');
-		return 0;
-	}
-
-	/*
-	 * Now we need to eat all the continuation lines..
-	 * Yuck, 2822 header "folding"
-	 */
-	for (;;) {
-		int peek;
-		struct strbuf continuation = STRBUF_INIT;
-
-		peek = fgetc(in); ungetc(peek, in);
-		if (peek != ' ' && peek != '\t')
-			break;
-		if (strbuf_getline(&continuation, in, '\n'))
-			break;
-		continuation.buf[0] = ' ';
-		strbuf_rtrim(&continuation);
-		strbuf_addbuf(line, &continuation);
-	}
-
-	return 1;
-}
-
-static int find_boundary(struct mailinfo *mi, struct strbuf *line)
-{
-	while (!strbuf_getline(line, mi->input, '\n')) {
-		if (*(mi->content_top) && is_multipart_boundary(mi, line))
-			return 1;
-	}
-	return 0;
-}
-
-static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf newline = STRBUF_INIT;
-
-	strbuf_addch(&newline, '\n');
-again:
-	if (line->len >= (*(mi->content_top))->len + 2 &&
-	    !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
-		/* we hit an end boundary */
-		/* pop the current boundary off the stack */
-		strbuf_release(*(mi->content_top));
-		free(*(mi->content_top));
-		*(mi->content_top) = NULL;
-
-		/* technically won't happen as is_multipart_boundary()
-		   will fail first.  But just in case..
-		 */
-		if (--mi->content_top < mi->content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
-		}
-		handle_filter(mi, &newline);
-		strbuf_release(&newline);
-
-		/* skip to the next boundary */
-		if (!find_boundary(mi, line))
-			return 0;
-		goto again;
-	}
-
-	/* set some defaults */
-	mi->transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&mi->charset);
-
-	/* slurp in this section's info */
-	while (read_one_header_line(line, mi->input))
-		check_header(mi, line, mi->p_hdr_data, 0);
-
-	strbuf_release(&newline);
-	/* replenish line */
-	if (strbuf_getline(line, mi->input, '\n'))
-		return 0;
-	strbuf_addch(line, '\n');
-	return 1;
-}
-
-static void handle_body(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf prev = STRBUF_INIT;
-
-	/* Skip up to the first boundary */
-	if (*(mi->content_top)) {
-		if (!find_boundary(mi, line))
-			goto handle_body_out;
-	}
-
-	do {
-		/* process any boundary lines */
-		if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
-			/* flush any leftover */
-			if (prev.len) {
-				handle_filter(mi, &prev);
-				strbuf_reset(&prev);
-			}
-			if (!handle_boundary(mi, line))
-				goto handle_body_out;
-		}
-
-		/* Unwrap transfer encoding */
-		decode_transfer_encoding(mi, line);
-
-		switch (mi->transfer_encoding) {
-		case TE_BASE64:
-		case TE_QP:
-		{
-			struct strbuf **lines, **it, *sb;
-
-			/* Prepend any previous partial lines */
-			strbuf_insert(line, 0, prev.buf, prev.len);
-			strbuf_reset(&prev);
-
-			/*
-			 * This is a decoded line that may contain
-			 * multiple new lines.  Pass only one chunk
-			 * at a time to handle_filter()
-			 */
-			lines = strbuf_split(line, '\n');
-			for (it = lines; (sb = *it); it++) {
-				if (*(it + 1) == NULL) /* The last line */
-					if (sb->buf[sb->len - 1] != '\n') {
-						/* Partial line, save it for later. */
-						strbuf_addbuf(&prev, sb);
-						break;
-					}
-				handle_filter(mi, sb);
-			}
-			/*
-			 * The partial chunk is saved in "prev" and will be
-			 * appended by the next iteration of read_line_with_nul().
-			 */
-			strbuf_list_free(lines);
-			break;
-		}
-		default:
-			handle_filter(mi, line);
-		}
-
-	} while (!strbuf_getwholeline(line, mi->input, '\n'));
-
-handle_body_out:
-	strbuf_release(&prev);
-}
-
-static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
-{
-	const char *sp = data->buf;
-	while (1) {
-		char *ep = strchr(sp, '\n');
-		int len;
-		if (!ep)
-			len = strlen(sp);
-		else
-			len = ep - sp;
-		fprintf(fout, "%s: %.*s\n", hdr, len, sp);
-		if (!ep)
-			break;
-		sp = ep + 1;
-	}
-}
-
-static void handle_info(struct mailinfo *mi)
-{
-	struct strbuf *hdr;
-	int i;
-
-	for (i = 0; header[i]; i++) {
-		/* only print inbody headers if we output a patch file */
-		if (mi->patch_lines && mi->s_hdr_data[i])
-			hdr = mi->s_hdr_data[i];
-		else if (mi->p_hdr_data[i])
-			hdr = mi->p_hdr_data[i];
-		else
-			continue;
-
-		if (!strcmp(header[i], "Subject")) {
-			if (!mi->keep_subject) {
-				cleanup_subject(mi, hdr);
-				cleanup_space(hdr);
-			}
-			output_header_lines(mi->output, "Subject", hdr);
-		} else if (!strcmp(header[i], "From")) {
-			cleanup_space(hdr);
-			handle_from(mi, hdr);
-			fprintf(mi->output, "Author: %s\n", mi->name.buf);
-			fprintf(mi->output, "Email: %s\n", mi->email.buf);
-		} else {
-			cleanup_space(hdr);
-			fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
-		}
-	}
-	fprintf(mi->output, "\n");
-}
-
-static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
-{
-	FILE *cmitmsg;
-	int peek;
-	struct strbuf line = STRBUF_INIT;
-
-	cmitmsg = fopen(msg, "w");
-	if (!cmitmsg) {
-		perror(msg);
-		return -1;
-	}
-	mi->patchfile = fopen(patch, "w");
-	if (!mi->patchfile) {
-		perror(patch);
-		fclose(cmitmsg);
-		return -1;
-	}
-
-	mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
-	mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
-
-	do {
-		peek = fgetc(mi->input);
-	} while (isspace(peek));
-	ungetc(peek, mi->input);
-
-	/* process the email header */
-	while (read_one_header_line(&line, mi->input))
-		check_header(mi, &line, mi->p_hdr_data, 1);
-
-	handle_body(mi, &line);
-	fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
-	fclose(cmitmsg);
-	fclose(mi->patchfile);
-
-	handle_info(mi);
-
-	return 0;
-}
-
-static int git_mailinfo_config(const char *var, const char *value, void *mi_)
-{
-	struct mailinfo *mi = mi_;
-
-	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, NULL);
-	if (!strcmp(var, "mailinfo.scissors")) {
-		mi->use_scissors = git_config_bool(var, value);
-		return 0;
-	}
-	/* perhaps others here */
-	return 0;
-}
-
-static void setup_mailinfo(struct mailinfo *mi)
-{
-	memset(mi, 0, sizeof(*mi));
-	strbuf_init(&mi->name, 0);
-	strbuf_init(&mi->email, 0);
-	strbuf_init(&mi->charset, 0);
-	strbuf_init(&mi->log_message, 0);
-	mi->header_stage = 1;
-	mi->use_inbody_headers = 1;
-	mi->content_top = mi->content;
-	git_config(git_mailinfo_config, &mi);
-}
-
-static void clear_mailinfo(struct mailinfo *mi)
-{
-	strbuf_release(&mi->name);
-	strbuf_release(&mi->email);
-	strbuf_release(&mi->charset);
-	strbuf_release(&mi->log_message);
-
-	while (mi->content < mi->content_top) {
-		free(*(mi->content_top));
-		mi->content_top--;
-	}
-}
+#include "mailinfo.h"
 
 static const char mailinfo_usage[] =
 	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
diff --git a/mailinfo.c b/mailinfo.c
new file mode 100644
index 0000000..59479f6
--- /dev/null
+++ b/mailinfo.c
@@ -0,0 +1,1007 @@
+#include "cache.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "mailinfo.h"
+
+static void cleanup_space(struct strbuf *sb)
+{
+	size_t pos, cnt;
+	for (pos = 0; pos < sb->len; pos++) {
+		if (isspace(sb->buf[pos])) {
+			sb->buf[pos] = ' ';
+			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+			strbuf_remove(sb, pos + 1, cnt);
+		}
+	}
+}
+
+static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
+{
+	struct strbuf *src = name;
+	if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
+		strchr(name->buf, '<') || strchr(name->buf, '>'))
+		src = email;
+	else if (name == out)
+		return;
+	strbuf_reset(out);
+	strbuf_addbuf(out, src);
+}
+
+static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
+{
+	/* John Doe <johndoe> */
+
+	char *bra, *ket;
+	/* This is fallback, so do not bother if we already have an
+	 * e-mail address.
+	 */
+	if (mi->email.len)
+		return;
+
+	bra = strchr(line->buf, '<');
+	if (!bra)
+		return;
+	ket = strchr(bra, '>');
+	if (!ket)
+		return;
+
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, bra + 1, ket - bra - 1);
+
+	strbuf_reset(&mi->name);
+	strbuf_add(&mi->name, line->buf, bra - line->buf);
+	strbuf_trim(&mi->name);
+	get_sane_name(&mi->name, &mi->name, &mi->email);
+}
+
+static void handle_from(struct mailinfo *mi, const struct strbuf *from)
+{
+	char *at;
+	size_t el;
+	struct strbuf f;
+
+	strbuf_init(&f, from->len);
+	strbuf_addbuf(&f, from);
+
+	at = strchr(f.buf, '@');
+	if (!at) {
+		parse_bogus_from(mi, from);
+		return;
+	}
+
+	/*
+	 * If we already have one email, don't take any confusing lines
+	 */
+	if (mi->email.len && strchr(at + 1, '@')) {
+		strbuf_release(&f);
+		return;
+	}
+
+	/* Pick up the string around '@', possibly delimited with <>
+	 * pair; that is the email part.
+	 */
+	while (at > f.buf) {
+		char c = at[-1];
+		if (isspace(c))
+			break;
+		if (c == '<') {
+			at[-1] = ' ';
+			break;
+		}
+		at--;
+	}
+	el = strcspn(at, " \n\t\r\v\f>");
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, at, el);
+	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
+
+	/* The remainder is name.  It could be
+	 *
+	 * - "John Doe <john.doe@xz>"			(a), or
+	 * - "john.doe@xz (John Doe)"			(b), or
+	 * - "John (zzz) Doe <john.doe@xz> (Comment)"	(c)
+	 *
+	 * but we have removed the email part, so
+	 *
+	 * - remove extra spaces which could stay after email (case 'c'), and
+	 * - trim from both ends, possibly removing the () pair at the end
+	 *   (cases 'a' and 'b').
+	 */
+	cleanup_space(&f);
+	strbuf_trim(&f);
+	if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
+		strbuf_remove(&f, 0, 1);
+		strbuf_setlen(&f, f.len - 1);
+	}
+
+	get_sane_name(&mi->name, &f, &mi->email);
+	strbuf_release(&f);
+}
+
+static void handle_header(struct strbuf **out, const struct strbuf *line)
+{
+	if (!*out) {
+		*out = xmalloc(sizeof(struct strbuf));
+		strbuf_init(*out, line->len);
+	} else
+		strbuf_reset(*out);
+
+	strbuf_addbuf(*out, line);
+}
+
+/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
+ * to have enough heuristics to grok MIME encoded patches often found
+ * on our mailing lists.  For example, we do not even treat header lines
+ * case insensitively.
+ */
+
+static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
+{
+	const char *ends, *ap = strcasestr(line, name);
+	size_t sz;
+
+	strbuf_setlen(attr, 0);
+	if (!ap)
+		return 0;
+	ap += strlen(name);
+	if (*ap == '"') {
+		ap++;
+		ends = "\"";
+	}
+	else
+		ends = "; \t";
+	sz = strcspn(ap, ends);
+	strbuf_add(attr, ap, sz);
+	return 1;
+}
+
+static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
+{
+	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
+	strbuf_init(boundary, line->len);
+
+	if (slurp_attr(line->buf, "boundary=", boundary)) {
+		strbuf_insert(boundary, 0, "--", 2);
+		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
+			fprintf(stderr, "Too many boundaries to handle\n");
+			exit(1);
+		}
+		*(mi->content_top) = boundary;
+		boundary = NULL;
+	}
+	slurp_attr(line->buf, "charset=", &mi->charset);
+
+	if (boundary) {
+		strbuf_release(boundary);
+		free(boundary);
+	}
+}
+
+static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
+{
+	if (mi->add_message_id)
+		mi->message_id = strdup(line->buf);
+}
+
+static void handle_content_transfer_encoding(struct mailinfo *mi,
+					     const struct strbuf *line)
+{
+	if (strcasestr(line->buf, "base64"))
+		mi->transfer_encoding = TE_BASE64;
+	else if (strcasestr(line->buf, "quoted-printable"))
+		mi->transfer_encoding = TE_QP;
+	else
+		mi->transfer_encoding = TE_DONTCARE;
+}
+
+static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
+{
+	struct strbuf *content_top = *(mi->content_top);
+
+	return ((content_top->len <= line->len) &&
+		!memcmp(line->buf, content_top->buf, content_top->len));
+}
+
+static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
+{
+	size_t at = 0;
+
+	while (at < subject->len) {
+		char *pos;
+		size_t remove;
+
+		switch (subject->buf[at]) {
+		case 'r': case 'R':
+			if (subject->len <= at + 3)
+				break;
+			if ((subject->buf[at + 1] == 'e' ||
+			     subject->buf[at + 1] == 'E') &&
+			    subject->buf[at + 2] == ':') {
+				strbuf_remove(subject, at, 3);
+				continue;
+			}
+			at++;
+			break;
+		case ' ': case '\t': case ':':
+			strbuf_remove(subject, at, 1);
+			continue;
+		case '[':
+			pos = strchr(subject->buf + at, ']');
+			if (!pos)
+				break;
+			remove = pos - subject->buf + at + 1;
+			if (!mi->keep_non_patch_brackets_in_subject ||
+			    (7 <= remove &&
+			     memmem(subject->buf + at, remove, "PATCH", 5)))
+				strbuf_remove(subject, at, remove);
+			else {
+				at += remove;
+				/*
+				 * If the input had a space after the ], keep
+				 * it.  We don't bother with finding the end of
+				 * the space, since we later normalize it
+				 * anyway.
+				 */
+				if (isspace(subject->buf[at]))
+					at += 1;
+			}
+			continue;
+		}
+		break;
+	}
+	strbuf_trim(subject);
+}
+
+#define MAX_HDR_PARSED 10
+static const char *header[MAX_HDR_PARSED] = {
+	"From","Subject","Date",
+};
+
+static inline int cmp_header(const struct strbuf *line, const char *hdr)
+{
+	int len = strlen(hdr);
+	return !strncasecmp(line->buf, hdr, len) && line->len > len &&
+			line->buf[len] == ':' && isspace(line->buf[len + 1]);
+}
+
+static int is_format_patch_separator(const char *line, int len)
+{
+	static const char SAMPLE[] =
+		"From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
+	const char *cp;
+
+	if (len != strlen(SAMPLE))
+		return 0;
+	if (!skip_prefix(line, "From ", &cp))
+		return 0;
+	if (strspn(cp, "0123456789abcdef") != 40)
+		return 0;
+	cp += 40;
+	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
+}
+
+static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
+{
+	const char *in = q_seg->buf;
+	int c;
+	struct strbuf *out = xmalloc(sizeof(struct strbuf));
+	strbuf_init(out, q_seg->len);
+
+	while ((c = *in++) != 0) {
+		if (c == '=') {
+			int d = *in++;
+			if (d == '\n' || !d)
+				break; /* drop trailing newline */
+			strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
+			continue;
+		}
+		if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
+			c = 0x20;
+		strbuf_addch(out, c);
+	}
+	return out;
+}
+
+static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
+{
+	/* Decode in..ep, possibly in-place to ot */
+	int c, pos = 0, acc = 0;
+	const char *in = b_seg->buf;
+	struct strbuf *out = xmalloc(sizeof(struct strbuf));
+	strbuf_init(out, b_seg->len);
+
+	while ((c = *in++) != 0) {
+		if (c == '+')
+			c = 62;
+		else if (c == '/')
+			c = 63;
+		else if ('A' <= c && c <= 'Z')
+			c -= 'A';
+		else if ('a' <= c && c <= 'z')
+			c -= 'a' - 26;
+		else if ('0' <= c && c <= '9')
+			c -= '0' - 52;
+		else
+			continue; /* garbage */
+		switch (pos++) {
+		case 0:
+			acc = (c << 2);
+			break;
+		case 1:
+			strbuf_addch(out, (acc | (c >> 4)));
+			acc = (c & 15) << 4;
+			break;
+		case 2:
+			strbuf_addch(out, (acc | (c >> 2)));
+			acc = (c & 3) << 6;
+			break;
+		case 3:
+			strbuf_addch(out, (acc | c));
+			acc = pos = 0;
+			break;
+		}
+	}
+	return out;
+}
+
+static void convert_to_utf8(struct mailinfo *mi,
+			    struct strbuf *line, const char *charset)
+{
+	char *out;
+
+	if (!mi->metainfo_charset || !charset || !*charset)
+		return;
+
+	if (same_encoding(mi->metainfo_charset, charset))
+		return;
+	out = reencode_string(line->buf, mi->metainfo_charset, charset);
+	if (!out)
+		die("cannot convert from %s to %s",
+		    charset, mi->metainfo_charset);
+	strbuf_attach(line, out, strlen(out), strlen(out));
+}
+
+static void decode_header(struct mailinfo *mi, struct strbuf *it)
+{
+	char *in, *ep, *cp;
+	struct strbuf outbuf = STRBUF_INIT, *dec;
+	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+
+	in = it->buf;
+	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
+		int encoding;
+		strbuf_reset(&charset_q);
+		strbuf_reset(&piecebuf);
+
+		if (in != ep) {
+			/*
+			 * We are about to process an encoded-word
+			 * that begins at ep, but there is something
+			 * before the encoded word.
+			 */
+			char *scan;
+			for (scan = in; scan < ep; scan++)
+				if (!isspace(*scan))
+					break;
+
+			if (scan != ep || in == it->buf) {
+				/*
+				 * We should not lose that "something",
+				 * unless we have just processed an
+				 * encoded-word, and there is only LWS
+				 * before the one we are about to process.
+				 */
+				strbuf_add(&outbuf, in, ep - in);
+			}
+		}
+		/* E.g.
+		 * ep : "=?iso-2022-jp?B?GyR...?= foo"
+		 * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
+		 */
+		ep += 2;
+
+		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
+			goto release_return;
+
+		if (cp + 3 - it->buf > it->len)
+			goto release_return;
+		strbuf_add(&charset_q, ep, cp - ep);
+
+		encoding = cp[1];
+		if (!encoding || cp[2] != '?')
+			goto release_return;
+		ep = strstr(cp + 3, "?=");
+		if (!ep)
+			goto release_return;
+		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
+		switch (tolower(encoding)) {
+		default:
+			goto release_return;
+		case 'b':
+			dec = decode_b_segment(&piecebuf);
+			break;
+		case 'q':
+			dec = decode_q_segment(&piecebuf, 1);
+			break;
+		}
+		convert_to_utf8(mi, dec, charset_q.buf);
+
+		strbuf_addbuf(&outbuf, dec);
+		strbuf_release(dec);
+		free(dec);
+		in = ep + 2;
+	}
+	strbuf_addstr(&outbuf, in);
+	strbuf_reset(it);
+	strbuf_addbuf(it, &outbuf);
+release_return:
+	strbuf_release(&outbuf);
+	strbuf_release(&charset_q);
+	strbuf_release(&piecebuf);
+}
+
+static int check_header(struct mailinfo *mi,
+			const struct strbuf *line,
+			struct strbuf *hdr_data[], int overwrite)
+{
+	int i, ret = 0, len;
+	struct strbuf sb = STRBUF_INIT;
+
+	/* search for the interesting parts */
+	for (i = 0; header[i]; i++) {
+		int len = strlen(header[i]);
+		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+			/* Unwrap inline B and Q encoding, and optionally
+			 * normalize the meta information to utf8.
+			 */
+			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+			decode_header(mi, &sb);
+			handle_header(&hdr_data[i], &sb);
+			ret = 1;
+			goto check_header_out;
+		}
+	}
+
+	/* Content stuff */
+	if (cmp_header(line, "Content-Type")) {
+		len = strlen("Content-Type: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		strbuf_insert(&sb, 0, "Content-Type: ", len);
+		handle_content_type(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Content-Transfer-Encoding")) {
+		len = strlen("Content-Transfer-Encoding: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		handle_content_transfer_encoding(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Message-Id")) {
+		len = strlen("Message-Id: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		handle_message_id(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+
+	/* for inbody stuff */
+	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
+		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
+		goto check_header_out;
+	}
+	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+		for (i = 0; header[i]; i++) {
+			if (!strcmp("Subject", header[i])) {
+				handle_header(&hdr_data[i], line);
+				ret = 1;
+				goto check_header_out;
+			}
+		}
+	}
+
+check_header_out:
+	strbuf_release(&sb);
+	return ret;
+}
+
+static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
+{
+	struct strbuf *ret;
+
+	switch (mi->transfer_encoding) {
+	case TE_QP:
+		ret = decode_q_segment(line, 0);
+		break;
+	case TE_BASE64:
+		ret = decode_b_segment(line);
+		break;
+	case TE_DONTCARE:
+	default:
+		return;
+	}
+	strbuf_reset(line);
+	strbuf_addbuf(line, ret);
+	strbuf_release(ret);
+	free(ret);
+}
+
+static inline int patchbreak(const struct strbuf *line)
+{
+	size_t i;
+
+	/* Beginning of a "diff -" header? */
+	if (starts_with(line->buf, "diff -"))
+		return 1;
+
+	/* CVS "Index: " line? */
+	if (starts_with(line->buf, "Index: "))
+		return 1;
+
+	/*
+	 * "--- <filename>" starts patches without headers
+	 * "---<sp>*" is a manual separator
+	 */
+	if (line->len < 4)
+		return 0;
+
+	if (starts_with(line->buf, "---")) {
+		/* space followed by a filename? */
+		if (line->buf[3] == ' ' && !isspace(line->buf[4]))
+			return 1;
+		/* Just whitespace? */
+		for (i = 3; i < line->len; i++) {
+			unsigned char c = line->buf[i];
+			if (c == '\n')
+				return 1;
+			if (!isspace(c))
+				break;
+		}
+		return 0;
+	}
+	return 0;
+}
+
+static int is_scissors_line(const struct strbuf *line)
+{
+	size_t i, len = line->len;
+	int scissors = 0, gap = 0;
+	int first_nonblank = -1;
+	int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
+	const char *buf = line->buf;
+
+	for (i = 0; i < len; i++) {
+		if (isspace(buf[i])) {
+			if (in_perforation) {
+				perforation++;
+				gap++;
+			}
+			continue;
+		}
+		last_nonblank = i;
+		if (first_nonblank < 0)
+			first_nonblank = i;
+		if (buf[i] == '-') {
+			in_perforation = 1;
+			perforation++;
+			continue;
+		}
+		if (i + 1 < len &&
+		    (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
+		     !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
+			in_perforation = 1;
+			perforation += 2;
+			scissors += 2;
+			i++;
+			continue;
+		}
+		in_perforation = 0;
+	}
+
+	/*
+	 * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+	 * Even though there can be arbitrary cruft on the same line
+	 * (e.g. "cut here"), in order to avoid misidentification, the
+	 * perforation must occupy more than a third of the visible
+	 * width of the line, and dashes and scissors must occupy more
+	 * than half of the perforation.
+	 */
+
+	visible = last_nonblank - first_nonblank + 1;
+	return (scissors && 8 <= visible &&
+		visible < perforation * 3 &&
+		gap * 2 < perforation);
+}
+
+static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
+{
+	assert(!mi->filter_stage);
+
+	if (mi->header_stage) {
+		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
+			return 0;
+	}
+
+	if (mi->use_inbody_headers && mi->header_stage) {
+		mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);
+		if (mi->header_stage)
+			return 0;
+	} else
+		/* Only trim the first (blank) line of the commit message
+		 * when ignoring in-body headers.
+		 */
+		mi->header_stage = 0;
+
+	/* normalize the log message to UTF-8. */
+	convert_to_utf8(mi, line, mi->charset.buf);
+
+	if (mi->use_scissors && is_scissors_line(line)) {
+		int i;
+
+		strbuf_setlen(&mi->log_message, 0);
+		mi->header_stage = 1;
+
+		/*
+		 * We may have already read "secondary headers"; purge
+		 * them to give ourselves a clean restart.
+		 */
+		for (i = 0; header[i]; i++) {
+			if (mi->s_hdr_data[i])
+				strbuf_release(mi->s_hdr_data[i]);
+			mi->s_hdr_data[i] = NULL;
+		}
+		return 0;
+	}
+
+	if (patchbreak(line)) {
+		if (mi->message_id)
+			strbuf_addf(&mi->log_message,
+				    "Message-Id: %s\n", mi->message_id);
+		return 1;
+	}
+
+	strbuf_addbuf(&mi->log_message, line);
+	return 0;
+}
+
+static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
+{
+	fwrite(line->buf, 1, line->len, mi->patchfile);
+	mi->patch_lines++;
+}
+
+static void handle_filter(struct mailinfo *mi, struct strbuf *line)
+{
+	switch (mi->filter_stage) {
+	case 0:
+		if (!handle_commit_msg(mi, line))
+			break;
+		mi->filter_stage++;
+	case 1:
+		handle_patch(mi, line);
+		break;
+	}
+}
+
+static int is_rfc2822_header(const struct strbuf *line)
+{
+	/*
+	 * The section that defines the loosest possible
+	 * field name is "3.6.8 Optional fields".
+	 *
+	 * optional-field = field-name ":" unstructured CRLF
+	 * field-name = 1*ftext
+	 * ftext = %d33-57 / %59-126
+	 */
+	int ch;
+	char *cp = line->buf;
+
+	/* Count mbox From headers as headers */
+	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
+		return 1;
+
+	while ((ch = *cp++)) {
+		if (ch == ':')
+			return 1;
+		if ((33 <= ch && ch <= 57) ||
+		    (59 <= ch && ch <= 126))
+			continue;
+		break;
+	}
+	return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+	/* Get the first part of the line. */
+	if (strbuf_getline(line, in, '\n'))
+		return 0;
+
+	/*
+	 * Is it an empty line or not a valid rfc2822 header?
+	 * If so, stop here, and return false ("not a header")
+	 */
+	strbuf_rtrim(line);
+	if (!line->len || !is_rfc2822_header(line)) {
+		/* Re-add the newline */
+		strbuf_addch(line, '\n');
+		return 0;
+	}
+
+	/*
+	 * Now we need to eat all the continuation lines..
+	 * Yuck, 2822 header "folding"
+	 */
+	for (;;) {
+		int peek;
+		struct strbuf continuation = STRBUF_INIT;
+
+		peek = fgetc(in); ungetc(peek, in);
+		if (peek != ' ' && peek != '\t')
+			break;
+		if (strbuf_getline(&continuation, in, '\n'))
+			break;
+		continuation.buf[0] = ' ';
+		strbuf_rtrim(&continuation);
+		strbuf_addbuf(line, &continuation);
+	}
+
+	return 1;
+}
+
+static int find_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+	while (!strbuf_getline(line, mi->input, '\n')) {
+		if (*(mi->content_top) && is_multipart_boundary(mi, line))
+			return 1;
+	}
+	return 0;
+}
+
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+	struct strbuf newline = STRBUF_INIT;
+
+	strbuf_addch(&newline, '\n');
+again:
+	if (line->len >= (*(mi->content_top))->len + 2 &&
+	    !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
+		/* we hit an end boundary */
+		/* pop the current boundary off the stack */
+		strbuf_release(*(mi->content_top));
+		free(*(mi->content_top));
+		*(mi->content_top) = NULL;
+
+		/* technically won't happen as is_multipart_boundary()
+		   will fail first.  But just in case..
+		 */
+		if (--mi->content_top < mi->content) {
+			fprintf(stderr, "Detected mismatched boundaries, "
+					"can't recover\n");
+			exit(1);
+		}
+		handle_filter(mi, &newline);
+		strbuf_release(&newline);
+
+		/* skip to the next boundary */
+		if (!find_boundary(mi, line))
+			return 0;
+		goto again;
+	}
+
+	/* set some defaults */
+	mi->transfer_encoding = TE_DONTCARE;
+	strbuf_reset(&mi->charset);
+
+	/* slurp in this section's info */
+	while (read_one_header_line(line, mi->input))
+		check_header(mi, line, mi->p_hdr_data, 0);
+
+	strbuf_release(&newline);
+	/* replenish line */
+	if (strbuf_getline(line, mi->input, '\n'))
+		return 0;
+	strbuf_addch(line, '\n');
+	return 1;
+}
+
+static void handle_body(struct mailinfo *mi, struct strbuf *line)
+{
+	struct strbuf prev = STRBUF_INIT;
+
+	/* Skip up to the first boundary */
+	if (*(mi->content_top)) {
+		if (!find_boundary(mi, line))
+			goto handle_body_out;
+	}
+
+	do {
+		/* process any boundary lines */
+		if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
+			/* flush any leftover */
+			if (prev.len) {
+				handle_filter(mi, &prev);
+				strbuf_reset(&prev);
+			}
+			if (!handle_boundary(mi, line))
+				goto handle_body_out;
+		}
+
+		/* Unwrap transfer encoding */
+		decode_transfer_encoding(mi, line);
+
+		switch (mi->transfer_encoding) {
+		case TE_BASE64:
+		case TE_QP:
+		{
+			struct strbuf **lines, **it, *sb;
+
+			/* Prepend any previous partial lines */
+			strbuf_insert(line, 0, prev.buf, prev.len);
+			strbuf_reset(&prev);
+
+			/*
+			 * This is a decoded line that may contain
+			 * multiple new lines.  Pass only one chunk
+			 * at a time to handle_filter()
+			 */
+			lines = strbuf_split(line, '\n');
+			for (it = lines; (sb = *it); it++) {
+				if (*(it + 1) == NULL) /* The last line */
+					if (sb->buf[sb->len - 1] != '\n') {
+						/* Partial line, save it for later. */
+						strbuf_addbuf(&prev, sb);
+						break;
+					}
+				handle_filter(mi, sb);
+			}
+			/*
+			 * The partial chunk is saved in "prev" and will be
+			 * appended by the next iteration of read_line_with_nul().
+			 */
+			strbuf_list_free(lines);
+			break;
+		}
+		default:
+			handle_filter(mi, line);
+		}
+
+	} while (!strbuf_getwholeline(line, mi->input, '\n'));
+
+handle_body_out:
+	strbuf_release(&prev);
+}
+
+static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
+{
+	const char *sp = data->buf;
+	while (1) {
+		char *ep = strchr(sp, '\n');
+		int len;
+		if (!ep)
+			len = strlen(sp);
+		else
+			len = ep - sp;
+		fprintf(fout, "%s: %.*s\n", hdr, len, sp);
+		if (!ep)
+			break;
+		sp = ep + 1;
+	}
+}
+
+static void handle_info(struct mailinfo *mi)
+{
+	struct strbuf *hdr;
+	int i;
+
+	for (i = 0; header[i]; i++) {
+		/* only print inbody headers if we output a patch file */
+		if (mi->patch_lines && mi->s_hdr_data[i])
+			hdr = mi->s_hdr_data[i];
+		else if (mi->p_hdr_data[i])
+			hdr = mi->p_hdr_data[i];
+		else
+			continue;
+
+		if (!strcmp(header[i], "Subject")) {
+			if (!mi->keep_subject) {
+				cleanup_subject(mi, hdr);
+				cleanup_space(hdr);
+			}
+			output_header_lines(mi->output, "Subject", hdr);
+		} else if (!strcmp(header[i], "From")) {
+			cleanup_space(hdr);
+			handle_from(mi, hdr);
+			fprintf(mi->output, "Author: %s\n", mi->name.buf);
+			fprintf(mi->output, "Email: %s\n", mi->email.buf);
+		} else {
+			cleanup_space(hdr);
+			fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
+		}
+	}
+	fprintf(mi->output, "\n");
+}
+
+int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
+{
+	FILE *cmitmsg;
+	int peek;
+	struct strbuf line = STRBUF_INIT;
+
+	cmitmsg = fopen(msg, "w");
+	if (!cmitmsg) {
+		perror(msg);
+		return -1;
+	}
+	mi->patchfile = fopen(patch, "w");
+	if (!mi->patchfile) {
+		perror(patch);
+		fclose(cmitmsg);
+		return -1;
+	}
+
+	mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+	mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
+
+	do {
+		peek = fgetc(mi->input);
+	} while (isspace(peek));
+	ungetc(peek, mi->input);
+
+	/* process the email header */
+	while (read_one_header_line(&line, mi->input))
+		check_header(mi, &line, mi->p_hdr_data, 1);
+
+	handle_body(mi, &line);
+	fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+	fclose(cmitmsg);
+	fclose(mi->patchfile);
+
+	handle_info(mi);
+
+	return 0;
+}
+
+static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+{
+	struct mailinfo *mi = mi_;
+
+	if (!starts_with(var, "mailinfo."))
+		return git_default_config(var, value, NULL);
+	if (!strcmp(var, "mailinfo.scissors")) {
+		mi->use_scissors = git_config_bool(var, value);
+		return 0;
+	}
+	/* perhaps others here */
+	return 0;
+}
+
+void setup_mailinfo(struct mailinfo *mi)
+{
+	memset(mi, 0, sizeof(*mi));
+	strbuf_init(&mi->name, 0);
+	strbuf_init(&mi->email, 0);
+	strbuf_init(&mi->charset, 0);
+	strbuf_init(&mi->log_message, 0);
+	mi->header_stage = 1;
+	mi->use_inbody_headers = 1;
+	mi->content_top = mi->content;
+	git_config(git_mailinfo_config, &mi);
+}
+
+void clear_mailinfo(struct mailinfo *mi)
+{
+	strbuf_release(&mi->name);
+	strbuf_release(&mi->email);
+	strbuf_release(&mi->charset);
+	strbuf_release(&mi->log_message);
+
+	while (mi->content < mi->content_top) {
+		free(*(mi->content_top));
+		mi->content_top--;
+	}
+}
diff --git a/mailinfo.h b/mailinfo.h
new file mode 100644
index 0000000..27841be
--- /dev/null
+++ b/mailinfo.h
@@ -0,0 +1,40 @@
+#ifndef MAILINFO_H
+#define MAILINFO_H
+
+#define MAX_BOUNDARIES 5
+
+struct mailinfo {
+	FILE *input;
+	FILE *output;
+	FILE *patchfile;
+
+	struct strbuf name;
+	struct strbuf email;
+	int keep_subject;
+	int keep_non_patch_brackets_in_subject;
+	int add_message_id;
+	int use_scissors;
+	int use_inbody_headers; /* defaults to 1 */
+	const char *metainfo_charset;
+
+	struct strbuf *content[MAX_BOUNDARIES];
+	struct strbuf **content_top;
+	struct strbuf charset;
+	char *message_id;
+	enum  {
+		TE_DONTCARE, TE_QP, TE_BASE64
+	} transfer_encoding;
+	int patch_lines;
+	int filter_stage; /* still reading log or are we copying patch? */
+	int header_stage; /* still checking in-body headers? */
+	struct strbuf **p_hdr_data;
+	struct strbuf **s_hdr_data;
+
+	struct strbuf log_message;
+};
+
+extern void setup_mailinfo(struct mailinfo *);
+extern int mailinfo(struct mailinfo *, const char *msg, const char *patch);
+extern void clear_mailinfo(struct mailinfo *);
+
+#endif /* MAILINFO_H */
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 29/31] mailinfo: handle charset conversion errors in the caller
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (27 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 28/31] mailinfo: libify Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 30/31] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
                     ` (2 subsequent siblings)
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

Instead of dying in convert_to_utf8(), just report an error and let
the callers handle it.  Between the two callers:

 - decode_header() silently punts when it cannot parse a broken
   RFC2047 encoded text (e.g. when it sees anything other than B or
   Q after it sees "=?<charset>") by jumping to release_return,
   returning the string it successfully parsed out so far, to the
   caller.  A piece of string that convert_to_utf8() cannot handle
   can be treated the same way.

 - handle_commit_msg() doesn't cope with a malformed line well, so
   die there for now.  We'll lift this even higher in later changes
   in this series.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 mailinfo.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/mailinfo.c b/mailinfo.c
index 59479f6..7969bf4 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -344,21 +344,22 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
 	return out;
 }
 
-static void convert_to_utf8(struct mailinfo *mi,
-			    struct strbuf *line, const char *charset)
+static int convert_to_utf8(struct mailinfo *mi,
+			   struct strbuf *line, const char *charset)
 {
 	char *out;
 
 	if (!mi->metainfo_charset || !charset || !*charset)
-		return;
+		return 0;
 
 	if (same_encoding(mi->metainfo_charset, charset))
-		return;
+		return 0;
 	out = reencode_string(line->buf, mi->metainfo_charset, charset);
 	if (!out)
-		die("cannot convert from %s to %s",
-		    charset, mi->metainfo_charset);
+		return error("cannot convert from %s to %s",
+			     charset, mi->metainfo_charset);
 	strbuf_attach(line, out, strlen(out), strlen(out));
+	return 0;
 }
 
 static void decode_header(struct mailinfo *mi, struct strbuf *it)
@@ -424,7 +425,8 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
 			dec = decode_q_segment(&piecebuf, 1);
 			break;
 		}
-		convert_to_utf8(mi, dec, charset_q.buf);
+		if (convert_to_utf8(mi, dec, charset_q.buf))
+			goto release_return;
 
 		strbuf_addbuf(&outbuf, dec);
 		strbuf_release(dec);
@@ -637,7 +639,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	convert_to_utf8(mi, line, mi->charset.buf);
+	if (convert_to_utf8(mi, line, mi->charset.buf))
+		exit(128);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 30/31] mailinfo: remove calls to exit() and die() deep in the callchain
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (28 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 29/31] mailinfo: handle charset conversion errors in the caller Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-14 20:45   ` [PATCH v2 31/31] am: make direct call to mailinfo Junio C Hamano
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

The top-level mailinfo() would instead punt when the code in the
deeper part of the callchain detects an unrecoverable error in the
input.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 mailinfo.c | 30 ++++++++++++++++++++++--------
 mailinfo.h |  1 +
 2 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/mailinfo.c b/mailinfo.c
index 7969bf4..cb39c6d 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -163,8 +163,10 @@ static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
 		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
-			fprintf(stderr, "Too many boundaries to handle\n");
-			exit(1);
+			error("Too many boundaries to handle");
+			mi->input_error = -1;
+			mi->content_top = &mi->content[MAX_BOUNDARIES] - 1;
+			return;
 		}
 		*(mi->content_top) = boundary;
 		boundary = NULL;
@@ -355,9 +357,11 @@ static int convert_to_utf8(struct mailinfo *mi,
 	if (same_encoding(mi->metainfo_charset, charset))
 		return 0;
 	out = reencode_string(line->buf, mi->metainfo_charset, charset);
-	if (!out)
+	if (!out) {
+		mi->input_error = -1;
 		return error("cannot convert from %s to %s",
 			     charset, mi->metainfo_charset);
+	}
 	strbuf_attach(line, out, strlen(out), strlen(out));
 	return 0;
 }
@@ -367,6 +371,7 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
 	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+	int found_error = 1; /* pessimism */
 
 	in = it->buf;
 	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
@@ -436,10 +441,14 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
 	strbuf_addstr(&outbuf, in);
 	strbuf_reset(it);
 	strbuf_addbuf(it, &outbuf);
+	found_error = 0;
 release_return:
 	strbuf_release(&outbuf);
 	strbuf_release(&charset_q);
 	strbuf_release(&piecebuf);
+
+	if (found_error)
+		mi->input_error = -1;
 }
 
 static int check_header(struct mailinfo *mi,
@@ -640,7 +649,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	/* normalize the log message to UTF-8. */
 	if (convert_to_utf8(mi, line, mi->charset.buf))
-		exit(128);
+		return 0; /* mi->input_error already set */
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -783,12 +792,15 @@ again:
 		   will fail first.  But just in case..
 		 */
 		if (--mi->content_top < mi->content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
+			error("Detected mismatched boundaries, can't recover");
+			mi->input_error = -1;
+			mi->content_top = mi->content;
+			return 0;
 		}
 		handle_filter(mi, &newline);
 		strbuf_release(&newline);
+		if (mi->input_error)
+			return 0;
 
 		/* skip to the next boundary */
 		if (!find_boundary(mi, line))
@@ -873,6 +885,8 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 			handle_filter(mi, line);
 		}
 
+		if (mi->input_error)
+			break;
 	} while (!strbuf_getwholeline(line, mi->input, '\n'));
 
 handle_body_out:
@@ -966,7 +980,7 @@ int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
 	handle_info(mi);
 
-	return 0;
+	return mi->input_error;
 }
 
 static int git_mailinfo_config(const char *var, const char *value, void *mi_)
diff --git a/mailinfo.h b/mailinfo.h
index 27841be..5bf257d 100644
--- a/mailinfo.h
+++ b/mailinfo.h
@@ -31,6 +31,7 @@ struct mailinfo {
 	struct strbuf **s_hdr_data;
 
 	struct strbuf log_message;
+	int input_error;
 };
 
 extern void setup_mailinfo(struct mailinfo *);
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v2 31/31] am: make direct call to mailinfo
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (29 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 30/31] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
@ 2015-10-14 20:45   ` Junio C Hamano
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
  31 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 20:45 UTC (permalink / raw)
  To: git

And finally the endgame.  Instead of spawning "git mailinfo" via the
run_command() API the same number of times as there are incoming
patches, make direct internal call to the libified mailinfo() from
"git am" to reduce the spawning overhead, which would matter on some
platforms.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/am.c | 42 +++++++++++++++++++++++-------------------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 4f77e07..1873307 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -27,6 +27,7 @@
 #include "notes-utils.h"
 #include "rerere.h"
 #include "prompt.h"
+#include "mailinfo.h"
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -1258,58 +1259,61 @@ static void am_append_signoff(struct am_state *state)
 static int parse_mail(struct am_state *state, const char *mail)
 {
 	FILE *fp;
-	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf msg = STRBUF_INIT;
 	struct strbuf author_name = STRBUF_INIT;
 	struct strbuf author_date = STRBUF_INIT;
 	struct strbuf author_email = STRBUF_INIT;
 	int ret = 0;
+	struct mailinfo mi;
 
-	cp.git_cmd = 1;
-	cp.in = xopen(mail, O_RDONLY, 0);
-	cp.out = xopen(am_path(state, "info"), O_WRONLY | O_CREAT, 0777);
+	setup_mailinfo(&mi);
 
-	argv_array_push(&cp.args, "mailinfo");
-	argv_array_push(&cp.args, state->utf8 ? "-u" : "-n");
+	if (state->utf8)
+		mi.metainfo_charset = get_commit_output_encoding();
+	else
+		mi.metainfo_charset = NULL;
 
 	switch (state->keep) {
 	case KEEP_FALSE:
 		break;
 	case KEEP_TRUE:
-		argv_array_push(&cp.args, "-k");
+		mi.keep_subject = 1;
 		break;
 	case KEEP_NON_PATCH:
-		argv_array_push(&cp.args, "-b");
+		mi.keep_non_patch_brackets_in_subject = 1;
 		break;
 	default:
 		die("BUG: invalid value for state->keep");
 	}
 
 	if (state->message_id)
-		argv_array_push(&cp.args, "-m");
+		mi.add_message_id = 1;
 
 	switch (state->scissors) {
 	case SCISSORS_UNSET:
 		break;
 	case SCISSORS_FALSE:
-		argv_array_push(&cp.args, "--no-scissors");
+		mi.use_scissors = 0;
 		break;
 	case SCISSORS_TRUE:
-		argv_array_push(&cp.args, "--scissors");
+		mi.use_scissors = 1;
 		break;
 	default:
 		die("BUG: invalid value for state->scissors");
 	}
 
-	argv_array_push(&cp.args, am_path(state, "msg"));
-	argv_array_push(&cp.args, am_path(state, "patch"));
-
-	if (run_command(&cp) < 0)
+	mi.input = fopen(mail, "r");
+	if (!mi.input)
+		die("could not open input");
+	mi.output = fopen(am_path(state, "info"), "w");
+	if (!mi.output)
+		die("could not open output 'info'");
+	if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
 		die("could not parse patch");
 
-	close(cp.in);
-	close(cp.out);
+	fclose(mi.input);
+	fclose(mi.output);
 
 	/* Extract message and author information */
 	fp = xfopen(am_path(state, "info"), "r");
@@ -1341,8 +1345,7 @@ static int parse_mail(struct am_state *state, const char *mail)
 	}
 
 	strbuf_addstr(&msg, "\n\n");
-	if (strbuf_read_file(&msg, am_path(state, "msg"), 0) < 0)
-		die_errno(_("could not read '%s'"), am_path(state, "msg"));
+	strbuf_addbuf(&msg, &mi.log_message);
 	stripspace(&msg, 0);
 
 	if (state->signoff)
@@ -1366,6 +1369,7 @@ finish:
 	strbuf_release(&author_email);
 	strbuf_release(&author_name);
 	strbuf_release(&sb);
+	clear_mailinfo(&mi);
 	return ret;
 }
 
-- 
2.6.1-320-g86a1181

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* Re: [PATCH v2 20/31] mailinfo: move cmitmsg and patchfile to struct mailinfo
  2015-10-14 20:45   ` [PATCH v2 20/31] mailinfo: move cmitmsg and patchfile to struct mailinfo Junio C Hamano
@ 2015-10-14 22:55     ` Junio C Hamano
  0 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 22:55 UTC (permalink / raw)
  To: git

This step does not compile correctly; will be fixed in v3.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v2 21/31] mailinfo: move [ps]_hdr_data to struct mailinfo
  2015-10-14 20:45   ` [PATCH v2 21/31] mailinfo: move [ps]_hdr_data " Junio C Hamano
@ 2015-10-14 22:57     ` Junio C Hamano
  0 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-14 22:57 UTC (permalink / raw)
  To: git

Junio C Hamano <gitster@pobox.com> writes:

> @@ -995,10 +995,10 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
>  
>  	/* process the email header */
>  	while (read_one_header_line(&line, mi->input))
> -		check_header(mi, &line, p_hdr_data, 1);
> +		check_header(mi, &line, mi->p_hdr_data, 1);
>  
>  	handle_body(mi, &line);
> -	fclose(patchfile);
> +	fclose(mi->patchfile);

... this is the bit that should have be in 20/31.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v2 16/31] mailinfo: move metainfo_charset to struct mailinfo
  2015-10-14 20:45   ` [PATCH v2 16/31] mailinfo: move metainfo_charset " Junio C Hamano
@ 2015-10-15 20:47     ` Eric Sunshine
  0 siblings, 0 replies; 124+ messages in thread
From: Eric Sunshine @ 2015-10-15 20:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git List

On Wed, Oct 14, 2015 at 4:45 PM, Junio C Hamano <gitster@pobox.com> wrote:
> This requires us to pass the struct down to decode_header() and
> convert_to_utf8() callchain.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> @@ -520,23 +519,24 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
> -static void convert_to_utf8(struct strbuf *line, const char *charset)
> +static void convert_to_utf8(struct mailinfo *mi,
> +                           struct strbuf *line, const char *charset)
>  {
>         char *out;
>
> -       if (!charset || !*charset)
> +       if (!mi->metainfo_charset || !charset || !*charset)
>                 return;

Mental note: convert_to_utf8() is updated to return early when
metainfo_charset==NULL.

> -       if (same_encoding(metainfo_charset, charset))
> +       if (same_encoding(mi->metainfo_charset, charset))
>                 return;
> -       out = reencode_string(line->buf, metainfo_charset, charset);
> +       out = reencode_string(line->buf, mi->metainfo_charset, charset);
>         if (!out)
>                 die("cannot convert from %s to %s",
> -                   charset, metainfo_charset);
> +                   charset, mi->metainfo_charset);
>         strbuf_attach(line, out, strlen(out), strlen(out));
>  }
>
> -static void decode_header(struct strbuf *it)
> +static void decode_header(struct mailinfo *mi, struct strbuf *it)
>  {
>         char *in, *ep, *cp;
>         struct strbuf outbuf = STRBUF_INIT, *dec;
> @@ -599,8 +599,7 @@ static void decode_header(struct strbuf *it)
>                         dec = decode_q_segment(&piecebuf, 1);
>                         break;
>                 }
> -               if (metainfo_charset)
> -                       convert_to_utf8(dec, charset_q.buf);
> +               convert_to_utf8(mi, dec, charset_q.buf);

It's safe to drop the conditional here since convert_to_utf8() now
checks metainfo_charset. Okay.

>                 strbuf_addbuf(&outbuf, dec);
>                 strbuf_release(dec);
> @@ -745,8 +744,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
>                 mi->header_stage = 0;
>
>         /* normalize the log message to UTF-8. */
> -       if (metainfo_charset)
> -               convert_to_utf8(line, charset.buf);
> +       convert_to_utf8(mi, line, charset.buf);

Ditto.

>         if (mi->use_scissors && is_scissors_line(line)) {
>                 int i;

^ permalink raw reply	[flat|nested] 124+ messages in thread

* [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
                     ` (30 preceding siblings ...)
  2015-10-14 20:45   ` [PATCH v2 31/31] am: make direct call to mailinfo Junio C Hamano
@ 2015-10-19  7:28   ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 01/34] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
                       ` (35 more replies)
  31 siblings, 36 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

During the discussion on the recent "git am" regression, I noticed
that the command reimplemented in C spawns one "mailsplit" and then
spawns "mailinfo" followed by "apply --index" to commit the changes
described in each message.  As there are platforms where spawning
subprocess via run_command() interface is heavy-weight, something
that is conceptually very simple like "mailinfo" is better called
directly inside the process---something that is lightweight and
frequently used is where the overhead of run_command() would be felt
most.

I think this round is ready for 'next'.  Relative to the previous
round, the changes are:

 * Editorial fixes on log messages.

 * The previous round leaked some fields in struct mailinfo upon
   completion (of course, inherited from the original that let the
   system clean them up upon process termination).  clear_mailinfo()
   has been enhanced to clear them.

 * The step to remove the global "line" variable has been split into
   multiple steps.

 * The step to move metainfo_charset to the struct has been split
   into two.

And here are the patches.

  mailinfo: remove a no-op call convert_to_utf8(it, "")
  mailinfo: fold decode_header_bq() into decode_header()
  mailinfo: fix an off-by-one error in the boundary stack
  mailinfo: explicitly close file handle to the patch output
  mailinfo: move handle_boundary() lower
  mailinfo: get rid of function-local static states

Mostly unchanged other than editorial fixes on their log messages.

  mailinfo: do not let handle_body() touch global "line" directly
  mailinfo: do not let handle_boundary() touch global "line" directly
  mailinfo: do not let find_boundary() touch global "line" directly
  mailinfo: move global "line" into mailinfo() function

After Stefan's review comments, I wanted to be really sure that this
conversion was correct.  Blindingly replacing the reference to the
global with a pointer passed from the callern is not sufficient to
ensure that the change is a no-op; the patches needed to show that
the pointer passed from the caller is always the global "line".  For
that, the patch [v2 7/31] needed to be split further into 3 patches
to convert three functions, each of which always is called with the
pointer that points at the global "line", in separate steps.

  mailinfo: introduce "struct mailinfo" to hold globals

The previous round was lazy and did not introduce clear_mailinfo()
until later, leaking the two strbuf moved into the struct.  The
original was leaking the global anyway, so it is not a big deal, but
this round adds corresponding clean-up as the patches move global
variables to the struct, which should make it harder to miss
forgotten clean-up.

  mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  mailinfo: move filter/header stage to struct mailinfo
  mailinfo: move patch_lines to struct mailinfo
  mailinfo: move add_message_id and message_id to struct mailinfo
  mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  mailinfo: move metainfo_charset to struct mailinfo

Mostly unchanged, except for the clean-up in clear_mailinfo().

  mailinfo: move check for metainfo_charset to convert_to_utf8()

Eric's review noticed that the previous round conflated this step
in the previous step.  Separated the change to its own commit.

  mailinfo: move transfer_encoding to struct mailinfo
  mailinfo: move charset to struct mailinfo
  mailinfo: move cmitmsg and patchfile to struct mailinfo
  mailinfo: move [ps]_hdr_data to struct mailinfo
  mailinfo: move content/content_top to struct mailinfo

Mostly unchanged, except for the clean-up in clear_mailinfo().

  mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  mailinfo: keep the parsed log message in a strbuf

These two were placed earlier in the series in the previous round,
but are not about moving globals to struct.  This round finishes
moving the globals to struct mailinfo before doing these two steps.

  mailinfo: move read_one_header_line() closer to its callers
  mailinfo: move check_header() after the helpers it uses
  mailinfo: move cleanup_space() before its users
  mailinfo: move definition of MAX_HDR_PARSED closer to its use

Mostly unchanged.  These are pure shuffling of functions and
variables to lose forward declarations and to allow easier reading
by grouping related things together.

  mailinfo: libify

Mostly pure code movement that is unchanged since v2.

  mailinfo: handle charset conversion errors in the caller
  mailinfo: remove calls to exit() and die() deep in the callchain
  am: make direct call to mailinfo

 Makefile                         |    1 +
 builtin/am.c                     |   42 +-
 builtin/mailinfo.c               | 1137 ++------------------------------------
 builtin/mailinfo.c => mailinfo.c |  799 +++++++++++++--------------
 mailinfo.h                       |   41 ++
 5 files changed, 505 insertions(+), 1515 deletions(-)
 rewrite builtin/mailinfo.c (95%)
 copy builtin/mailinfo.c => mailinfo.c (67%)
 create mode 100644 mailinfo.h

-- 
2.6.2-383-g144b2e6

^ permalink raw reply	[flat|nested] 124+ messages in thread

* [PATCH v3 01/34] mailinfo: remove a no-op call convert_to_utf8(it, "")
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 02/34] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
                       ` (34 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

The called function checks if the second parameter is either a NULL
or an empty string at the very beginning and returns without doing
anything.  Remove the useless call.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 999a525..5a4ed75 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -612,11 +612,6 @@ static void decode_header(struct strbuf *it)
 {
 	if (decode_header_bq(it))
 		return;
-	/* otherwise "it" is a straight copy of the input.
-	 * This can be binary guck but there is no charset specified.
-	 */
-	if (metainfo_charset)
-		convert_to_utf8(it, "");
 }
 
 static void decode_transfer_encoding(struct strbuf *line)
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 02/34] mailinfo: fold decode_header_bq() into decode_header()
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 01/34] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 03/34] mailinfo: fix an off-by-one error in the boundary stack Junio C Hamano
                       ` (33 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

In olden days we might have wanted to behave differently in
decode_header() if the header line was encoded with RFC2047, but we
apparently do not do so, hence this helper function can go, together
with its return value.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 5a4ed75..addc0e0 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -525,19 +525,17 @@ static void convert_to_utf8(struct strbuf *line, const char *charset)
 	strbuf_attach(line, out, strlen(out), strlen(out));
 }
 
-static int decode_header_bq(struct strbuf *it)
+static void decode_header(struct strbuf *it)
 {
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
 	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
-	int rfc2047 = 0;
 
 	in = it->buf;
 	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
 		int encoding;
 		strbuf_reset(&charset_q);
 		strbuf_reset(&piecebuf);
-		rfc2047 = 1;
 
 		if (in != ep) {
 			/*
@@ -567,22 +565,22 @@ static int decode_header_bq(struct strbuf *it)
 		ep += 2;
 
 		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
-			goto decode_header_bq_out;
+			goto release_return;
 
 		if (cp + 3 - it->buf > it->len)
-			goto decode_header_bq_out;
+			goto release_return;
 		strbuf_add(&charset_q, ep, cp - ep);
 
 		encoding = cp[1];
 		if (!encoding || cp[2] != '?')
-			goto decode_header_bq_out;
+			goto release_return;
 		ep = strstr(cp + 3, "?=");
 		if (!ep)
-			goto decode_header_bq_out;
+			goto release_return;
 		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
 		switch (tolower(encoding)) {
 		default:
-			goto decode_header_bq_out;
+			goto release_return;
 		case 'b':
 			dec = decode_b_segment(&piecebuf);
 			break;
@@ -601,17 +599,10 @@ static int decode_header_bq(struct strbuf *it)
 	strbuf_addstr(&outbuf, in);
 	strbuf_reset(it);
 	strbuf_addbuf(it, &outbuf);
-decode_header_bq_out:
+release_return:
 	strbuf_release(&outbuf);
 	strbuf_release(&charset_q);
 	strbuf_release(&piecebuf);
-	return rfc2047;
-}
-
-static void decode_header(struct strbuf *it)
-{
-	if (decode_header_bq(it))
-		return;
 }
 
 static void decode_transfer_encoding(struct strbuf *line)
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 03/34] mailinfo: fix an off-by-one error in the boundary stack
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 01/34] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 02/34] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 04/34] mailinfo: explicitly close file handle to the patch output Junio C Hamano
                       ` (32 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

We pre-increment the pointer that we will use to store something at,
so the pointer is already beyond the end of the array if it points
at content[MAX_BOUNDARIES].

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index addc0e0..1566c19 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -185,7 +185,7 @@ static void handle_content_type(struct strbuf *line)
 
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
-		if (++content_top > &content[MAX_BOUNDARIES]) {
+		if (++content_top >= &content[MAX_BOUNDARIES]) {
 			fprintf(stderr, "Too many boundaries to handle\n");
 			exit(1);
 		}
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 04/34] mailinfo: explicitly close file handle to the patch output
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (2 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 03/34] mailinfo: fix an off-by-one error in the boundary stack Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 05/34] mailinfo: move handle_boundary() lower Junio C Hamano
                       ` (31 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This does not make a difference within the context of "git mailinfo"
that runs once and exits, as flushing and closing would happen upon
process termination.  It however will matter when we eventually make
it callable as an API function.

Besides, cleaning after yourself once you are done is a good hygiene.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 1566c19..73be47c 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -999,6 +999,8 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 		check_header(&line, p_hdr_data, 1);
 
 	handle_body();
+	fclose(patchfile);
+
 	handle_info();
 
 	return 0;
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 05/34] mailinfo: move handle_boundary() lower
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (3 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 04/34] mailinfo: explicitly close file handle to the patch output Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 06/34] mailinfo: get rid of function-local static states Junio C Hamano
                       ` (30 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This function wants to call find_boundary() and is called only from
one place without any recursing, so it becomes easier to read if it
appears after the called function.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 114 ++++++++++++++++++++++++++---------------------------
 1 file changed, 56 insertions(+), 58 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 73be47c..2b7f97b 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -626,64 +626,6 @@ static void decode_transfer_encoding(struct strbuf *line)
 	free(ret);
 }
 
-static void handle_filter(struct strbuf *line);
-
-static int find_boundary(void)
-{
-	while (!strbuf_getline(&line, fin, '\n')) {
-		if (*content_top && is_multipart_boundary(&line))
-			return 1;
-	}
-	return 0;
-}
-
-static int handle_boundary(void)
-{
-	struct strbuf newline = STRBUF_INIT;
-
-	strbuf_addch(&newline, '\n');
-again:
-	if (line.len >= (*content_top)->len + 2 &&
-	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
-		/* we hit an end boundary */
-		/* pop the current boundary off the stack */
-		strbuf_release(*content_top);
-		free(*content_top);
-		*content_top = NULL;
-
-		/* technically won't happen as is_multipart_boundary()
-		   will fail first.  But just in case..
-		 */
-		if (--content_top < content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
-		}
-		handle_filter(&newline);
-		strbuf_release(&newline);
-
-		/* skip to the next boundary */
-		if (!find_boundary())
-			return 0;
-		goto again;
-	}
-
-	/* set some defaults */
-	transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&charset);
-
-	/* slurp in this section's info */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 0);
-
-	strbuf_release(&newline);
-	/* replenish line */
-	if (strbuf_getline(&line, fin, '\n'))
-		return 0;
-	strbuf_addch(&line, '\n');
-	return 1;
-}
-
 static inline int patchbreak(const struct strbuf *line)
 {
 	size_t i;
@@ -851,6 +793,62 @@ static void handle_filter(struct strbuf *line)
 	}
 }
 
+static int find_boundary(void)
+{
+	while (!strbuf_getline(&line, fin, '\n')) {
+		if (*content_top && is_multipart_boundary(&line))
+			return 1;
+	}
+	return 0;
+}
+
+static int handle_boundary(void)
+{
+	struct strbuf newline = STRBUF_INIT;
+
+	strbuf_addch(&newline, '\n');
+again:
+	if (line.len >= (*content_top)->len + 2 &&
+	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
+		/* we hit an end boundary */
+		/* pop the current boundary off the stack */
+		strbuf_release(*content_top);
+		free(*content_top);
+		*content_top = NULL;
+
+		/* technically won't happen as is_multipart_boundary()
+		   will fail first.  But just in case..
+		 */
+		if (--content_top < content) {
+			fprintf(stderr, "Detected mismatched boundaries, "
+					"can't recover\n");
+			exit(1);
+		}
+		handle_filter(&newline);
+		strbuf_release(&newline);
+
+		/* skip to the next boundary */
+		if (!find_boundary())
+			return 0;
+		goto again;
+	}
+
+	/* set some defaults */
+	transfer_encoding = TE_DONTCARE;
+	strbuf_reset(&charset);
+
+	/* slurp in this section's info */
+	while (read_one_header_line(&line, fin))
+		check_header(&line, p_hdr_data, 0);
+
+	strbuf_release(&newline);
+	/* replenish line */
+	if (strbuf_getline(&line, fin, '\n'))
+		return 0;
+	strbuf_addch(&line, '\n');
+	return 1;
+}
+
 static void handle_body(void)
 {
 	struct strbuf prev = STRBUF_INIT;
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 06/34] mailinfo: get rid of function-local static states
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (4 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 05/34] mailinfo: move handle_boundary() lower Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 07/34] mailinfo: do not let handle_body() touch global "line" directly Junio C Hamano
                       ` (29 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Two helper functions use "static int" in their scope to keep track
of the state while repeatedly getting called once for each input
line.  Move these state variables to their ultimate caller and pass
down pointers to them along the callchain, as a small step in
preparation for making this entire callchain more reentrant.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 41 +++++++++++++++++++----------------------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 2b7f97b..1518708 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -713,27 +713,25 @@ static int is_scissors_line(const struct strbuf *line)
 		gap * 2 < perforation);
 }
 
-static int handle_commit_msg(struct strbuf *line)
+static int handle_commit_msg(struct strbuf *line, int *still_looking)
 {
-	static int still_looking = 1;
-
 	if (!cmitmsg)
 		return 0;
 
-	if (still_looking) {
+	if (*still_looking) {
 		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
 			return 0;
 	}
 
-	if (use_inbody_headers && still_looking) {
-		still_looking = check_header(line, s_hdr_data, 0);
-		if (still_looking)
+	if (use_inbody_headers && *still_looking) {
+		*still_looking = check_header(line, s_hdr_data, 0);
+		if (*still_looking)
 			return 0;
 	} else
 		/* Only trim the first (blank) line of the commit message
 		 * when ignoring in-body headers.
 		 */
-		still_looking = 0;
+		*still_looking = 0;
 
 	/* normalize the log message to UTF-8. */
 	if (metainfo_charset)
@@ -745,7 +743,7 @@ static int handle_commit_msg(struct strbuf *line)
 			die_errno("Could not rewind output message file");
 		if (ftruncate(fileno(cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
-		still_looking = 1;
+		*still_looking = 1;
 
 		/*
 		 * We may have already read "secondary headers"; purge
@@ -777,16 +775,13 @@ static void handle_patch(const struct strbuf *line)
 	patch_lines++;
 }
 
-static void handle_filter(struct strbuf *line)
+static void handle_filter(struct strbuf *line, int *filter_stage, int *header_stage)
 {
-	static int filter = 0;
-
-	/* filter tells us which part we left off on */
-	switch (filter) {
+	switch (*filter_stage) {
 	case 0:
-		if (!handle_commit_msg(line))
+		if (!handle_commit_msg(line, header_stage))
 			break;
-		filter++;
+		(*filter_stage)++;
 	case 1:
 		handle_patch(line);
 		break;
@@ -802,7 +797,7 @@ static int find_boundary(void)
 	return 0;
 }
 
-static int handle_boundary(void)
+static int handle_boundary(int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -824,7 +819,7 @@ again:
 					"can't recover\n");
 			exit(1);
 		}
-		handle_filter(&newline);
+		handle_filter(&newline, filter_stage, header_stage);
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
@@ -852,6 +847,8 @@ again:
 static void handle_body(void)
 {
 	struct strbuf prev = STRBUF_INIT;
+	int filter_stage = 0;
+	int header_stage = 1;
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
@@ -864,10 +861,10 @@ static void handle_body(void)
 		if (*content_top && is_multipart_boundary(&line)) {
 			/* flush any leftover */
 			if (prev.len) {
-				handle_filter(&prev);
+				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary())
+			if (!handle_boundary(&filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
@@ -897,7 +894,7 @@ static void handle_body(void)
 						strbuf_addbuf(&prev, sb);
 						break;
 					}
-				handle_filter(sb);
+				handle_filter(sb, &filter_stage, &header_stage);
 			}
 			/*
 			 * The partial chunk is saved in "prev" and will be
@@ -907,7 +904,7 @@ static void handle_body(void)
 			break;
 		}
 		default:
-			handle_filter(&line);
+			handle_filter(&line, &filter_stage, &header_stage);
 		}
 
 	} while (!strbuf_getwholeline(&line, fin, '\n'));
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 07/34] mailinfo: do not let handle_body() touch global "line" directly
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (5 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 06/34] mailinfo: get rid of function-local static states Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 08/34] mailinfo: do not let handle_boundary() " Junio C Hamano
                       ` (28 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This function has a single caller, and called with the global "line"
holding the first line of the e-mail body after the caller finished
processing the e-mail headers.  The function then goes into a loop
to process each line of the input, starting from what was given by
its caller, and fills the same global "line" variable from the input
as it needs to process more lines.

Let the caller explicitly pass a pointer to this global "line"
variable as an argument, and have the function itself use that
strbuf throughout, instead of referring to the global "line" itself.

There are helper functions that this function calls that still touch
the global directly; they will be updated as the series progresses.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 1518708..52be7db 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -844,7 +844,7 @@ again:
 	return 1;
 }
 
-static void handle_body(void)
+static void handle_body(struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
 	int filter_stage = 0;
@@ -858,7 +858,7 @@ static void handle_body(void)
 
 	do {
 		/* process any boundary lines */
-		if (*content_top && is_multipart_boundary(&line)) {
+		if (*content_top && is_multipart_boundary(line)) {
 			/* flush any leftover */
 			if (prev.len) {
 				handle_filter(&prev, &filter_stage, &header_stage);
@@ -869,7 +869,7 @@ static void handle_body(void)
 		}
 
 		/* Unwrap transfer encoding */
-		decode_transfer_encoding(&line);
+		decode_transfer_encoding(line);
 
 		switch (transfer_encoding) {
 		case TE_BASE64:
@@ -878,7 +878,7 @@ static void handle_body(void)
 			struct strbuf **lines, **it, *sb;
 
 			/* Prepend any previous partial lines */
-			strbuf_insert(&line, 0, prev.buf, prev.len);
+			strbuf_insert(line, 0, prev.buf, prev.len);
 			strbuf_reset(&prev);
 
 			/*
@@ -886,7 +886,7 @@ static void handle_body(void)
 			 * multiple new lines.  Pass only one chunk
 			 * at a time to handle_filter()
 			 */
-			lines = strbuf_split(&line, '\n');
+			lines = strbuf_split(line, '\n');
 			for (it = lines; (sb = *it); it++) {
 				if (*(it + 1) == NULL) /* The last line */
 					if (sb->buf[sb->len - 1] != '\n') {
@@ -904,10 +904,10 @@ static void handle_body(void)
 			break;
 		}
 		default:
-			handle_filter(&line, &filter_stage, &header_stage);
+			handle_filter(line, &filter_stage, &header_stage);
 		}
 
-	} while (!strbuf_getwholeline(&line, fin, '\n'));
+	} while (!strbuf_getwholeline(line, fin, '\n'));
 
 handle_body_out:
 	strbuf_release(&prev);
@@ -993,7 +993,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 	while (read_one_header_line(&line, fin))
 		check_header(&line, p_hdr_data, 1);
 
-	handle_body();
+	handle_body(&line);
 	fclose(patchfile);
 
 	handle_info();
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 08/34] mailinfo: do not let handle_boundary() touch global "line" directly
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (6 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 07/34] mailinfo: do not let handle_body() touch global "line" directly Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 09/34] mailinfo: do not let find_boundary() " Junio C Hamano
                       ` (27 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This function has a single caller, and called with the global "line"
holding the multi-part boundary line the caller saw while processing
the e-mail body.  The function then goes into a loop to process each
line of the input, and fills the same global "line" variable from
the input as it needs to read more lines to process the multi-part
headers.

Let the caller explicitly pass a pointer to this global "line"
variable as an argument, and have the function itself use that
strbuf throughout, instead of referring to the global "line" itself.

There still is a helper function that this function calls that still
touches the global directly; it will be updated as the series progresses.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 52be7db..cab1235 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -797,14 +797,14 @@ static int find_boundary(void)
 	return 0;
 }
 
-static int handle_boundary(int *filter_stage, int *header_stage)
+static int handle_boundary(struct strbuf *line, int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
 	strbuf_addch(&newline, '\n');
 again:
-	if (line.len >= (*content_top)->len + 2 &&
-	    !memcmp(line.buf + (*content_top)->len, "--", 2)) {
+	if (line->len >= (*content_top)->len + 2 &&
+	    !memcmp(line->buf + (*content_top)->len, "--", 2)) {
 		/* we hit an end boundary */
 		/* pop the current boundary off the stack */
 		strbuf_release(*content_top);
@@ -833,14 +833,14 @@ again:
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
-	while (read_one_header_line(&line, fin))
-		check_header(&line, p_hdr_data, 0);
+	while (read_one_header_line(line, fin))
+		check_header(line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
-	if (strbuf_getline(&line, fin, '\n'))
+	if (strbuf_getline(line, fin, '\n'))
 		return 0;
-	strbuf_addch(&line, '\n');
+	strbuf_addch(line, '\n');
 	return 1;
 }
 
@@ -864,7 +864,7 @@ static void handle_body(struct strbuf *line)
 				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(&filter_stage, &header_stage))
+			if (!handle_boundary(line, &filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 09/34] mailinfo: do not let find_boundary() touch global "line" directly
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (7 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 08/34] mailinfo: do not let handle_boundary() " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 10/34] mailinfo: move global "line" into mailinfo() function Junio C Hamano
                       ` (26 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

With the previous two commits, we established that the local
variable "line" in handle_body() and handle_boundary() functions
always refer to the global "line" that is used as the common and
shared "current line from the input".  They are the only callers of
the last function that refers to the global line directly, i.e.
find_boundary().  Pass "line" as a parameter to this leaf function
to complete the clean-up.  Now the only function that directly refers
to the global "line" is the caller of handle_body() at the very
beginning of this whole callchain.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index cab1235..12d1eda 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -788,10 +788,10 @@ static void handle_filter(struct strbuf *line, int *filter_stage, int *header_st
 	}
 }
 
-static int find_boundary(void)
+static int find_boundary(struct strbuf *line)
 {
-	while (!strbuf_getline(&line, fin, '\n')) {
-		if (*content_top && is_multipart_boundary(&line))
+	while (!strbuf_getline(line, fin, '\n')) {
+		if (*content_top && is_multipart_boundary(line))
 			return 1;
 	}
 	return 0;
@@ -823,7 +823,7 @@ again:
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
-		if (!find_boundary())
+		if (!find_boundary(line))
 			return 0;
 		goto again;
 	}
@@ -852,7 +852,7 @@ static void handle_body(struct strbuf *line)
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
-		if (!find_boundary())
+		if (!find_boundary(line))
 			goto handle_body_out;
 	}
 
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 10/34] mailinfo: move global "line" into mailinfo() function
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (8 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 09/34] mailinfo: do not let find_boundary() " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19 22:57       ` Eric Sunshine
  2015-10-19  7:28     ` [PATCH v3 11/34] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
                       ` (25 subsequent siblings)
  35 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

With the previous steps, it becomes clear that the mailinfo()
function is the only one that wants the "line" to be directly
touchable.  Move it to the function scope of this function.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 12d1eda..c8dc73f 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,7 +12,6 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
-static struct strbuf line = STRBUF_INIT;
 static struct strbuf name = STRBUF_INIT;
 static struct strbuf email = STRBUF_INIT;
 static char *message_id;
@@ -966,6 +965,8 @@ static void handle_info(void)
 static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 {
 	int peek;
+	struct strbuf line = STRBUF_INIT;
+
 	fin = in;
 	fout = out;
 
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 11/34] mailinfo: introduce "struct mailinfo" to hold globals
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (9 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 10/34] mailinfo: move global "line" into mailinfo() function Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 12/34] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
                       ` (24 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

In this first step, move only 'email' and 'name' fields in there and
remove the corresponding globals.  In subsequent patches, more
globals will be moved to this and the structure will be passed
around as a new parameter to more functions.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 71 ++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 47 insertions(+), 24 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index c8dc73f..e3c0c31 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,8 +12,11 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
 static int keep_subject;
 static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
-static struct strbuf name = STRBUF_INIT;
-static struct strbuf email = STRBUF_INIT;
+
+struct mailinfo {
+	struct strbuf name;
+	struct strbuf email;
+};
 static char *message_id;
 
 static enum  {
@@ -45,7 +48,7 @@ static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf
 	strbuf_addbuf(out, src);
 }
 
-static void parse_bogus_from(const struct strbuf *line)
+static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
 {
 	/* John Doe <johndoe> */
 
@@ -53,7 +56,7 @@ static void parse_bogus_from(const struct strbuf *line)
 	/* This is fallback, so do not bother if we already have an
 	 * e-mail address.
 	 */
-	if (email.len)
+	if (mi->email.len)
 		return;
 
 	bra = strchr(line->buf, '<');
@@ -63,16 +66,16 @@ static void parse_bogus_from(const struct strbuf *line)
 	if (!ket)
 		return;
 
-	strbuf_reset(&email);
-	strbuf_add(&email, bra + 1, ket - bra - 1);
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, bra + 1, ket - bra - 1);
 
-	strbuf_reset(&name);
-	strbuf_add(&name, line->buf, bra - line->buf);
-	strbuf_trim(&name);
-	get_sane_name(&name, &name, &email);
+	strbuf_reset(&mi->name);
+	strbuf_add(&mi->name, line->buf, bra - line->buf);
+	strbuf_trim(&mi->name);
+	get_sane_name(&mi->name, &mi->name, &mi->email);
 }
 
-static void handle_from(const struct strbuf *from)
+static void handle_from(struct mailinfo *mi, const struct strbuf *from)
 {
 	char *at;
 	size_t el;
@@ -83,14 +86,14 @@ static void handle_from(const struct strbuf *from)
 
 	at = strchr(f.buf, '@');
 	if (!at) {
-		parse_bogus_from(from);
+		parse_bogus_from(mi, from);
 		return;
 	}
 
 	/*
 	 * If we already have one email, don't take any confusing lines
 	 */
-	if (email.len && strchr(at + 1, '@')) {
+	if (mi->email.len && strchr(at + 1, '@')) {
 		strbuf_release(&f);
 		return;
 	}
@@ -109,8 +112,8 @@ static void handle_from(const struct strbuf *from)
 		at--;
 	}
 	el = strcspn(at, " \n\t\r\v\f>");
-	strbuf_reset(&email);
-	strbuf_add(&email, at, el);
+	strbuf_reset(&mi->email);
+	strbuf_add(&mi->email, at, el);
 	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
 
 	/* The remainder is name.  It could be
@@ -132,7 +135,7 @@ static void handle_from(const struct strbuf *from)
 		strbuf_setlen(&f, f.len - 1);
 	}
 
-	get_sane_name(&name, &f, &email);
+	get_sane_name(&mi->name, &f, &mi->email);
 	strbuf_release(&f);
 }
 
@@ -929,7 +932,7 @@ static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf
 	}
 }
 
-static void handle_info(void)
+static void handle_info(struct mailinfo *mi)
 {
 	struct strbuf *hdr;
 	int i;
@@ -951,9 +954,9 @@ static void handle_info(void)
 			output_header_lines(fout, "Subject", hdr);
 		} else if (!strcmp(header[i], "From")) {
 			cleanup_space(hdr);
-			handle_from(hdr);
-			fprintf(fout, "Author: %s\n", name.buf);
-			fprintf(fout, "Email: %s\n", email.buf);
+			handle_from(mi, hdr);
+			fprintf(fout, "Author: %s\n", mi->name.buf);
+			fprintf(fout, "Email: %s\n", mi->email.buf);
 		} else {
 			cleanup_space(hdr);
 			fprintf(fout, "%s: %s\n", header[i], hdr->buf);
@@ -962,7 +965,8 @@ static void handle_info(void)
 	fprintf(fout, "\n");
 }
 
-static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
+static int mailinfo(struct mailinfo *mi,
+		    FILE *in, FILE *out, const char *msg, const char *patch)
 {
 	int peek;
 	struct strbuf line = STRBUF_INIT;
@@ -997,7 +1001,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 	handle_body(&line);
 	fclose(patchfile);
 
-	handle_info();
+	handle_info(mi);
 
 	return 0;
 }
@@ -1014,17 +1018,33 @@ static int git_mailinfo_config(const char *var, const char *value, void *unused)
 	return 0;
 }
 
+static void setup_mailinfo(struct mailinfo *mi)
+{
+	memset(mi, 0, sizeof(*mi));
+	strbuf_init(&mi->name, 0);
+	strbuf_init(&mi->email, 0);
+	git_config(git_mailinfo_config, &mi);
+}
+
+static void clear_mailinfo(struct mailinfo *mi)
+{
+	strbuf_release(&mi->name);
+	strbuf_release(&mi->email);
+}
+
 static const char mailinfo_usage[] =
 	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
 
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
 	const char *def_charset;
+	struct mailinfo mi;
+	int status;
 
 	/* NEEDSWORK: might want to do the optional .git/ directory
 	 * discovery
 	 */
-	git_config(git_mailinfo_config, NULL);
+	setup_mailinfo(&mi);
 
 	def_charset = get_commit_output_encoding();
 	metainfo_charset = def_charset;
@@ -1056,5 +1076,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	if (argc != 3)
 		usage(mailinfo_usage);
 
-	return !!mailinfo(stdin, stdout, argv[1], argv[2]);
+	status = !!mailinfo(&mi, stdin, stdout, argv[1], argv[2]);
+	clear_mailinfo(&mi);
+
+	return status;
 }
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 12/34] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (10 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 11/34] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 13/34] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
                       ` (23 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

These two are the only easy ones that do not require passing the
structure around to deep corners of the callchain.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index e3c0c31..08c67f5 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -9,13 +9,13 @@
 
 static FILE *cmitmsg, *patchfile, *fin, *fout;
 
-static int keep_subject;
-static int keep_non_patch_brackets_in_subject;
 static const char *metainfo_charset;
 
 struct mailinfo {
 	struct strbuf name;
 	struct strbuf email;
+	int keep_subject;
+	int keep_non_patch_brackets_in_subject;
 };
 static char *message_id;
 
@@ -224,7 +224,7 @@ static int is_multipart_boundary(const struct strbuf *line)
 		!memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
 }
 
-static void cleanup_subject(struct strbuf *subject)
+static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
 {
 	size_t at = 0;
 
@@ -252,7 +252,7 @@ static void cleanup_subject(struct strbuf *subject)
 			if (!pos)
 				break;
 			remove = pos - subject->buf + at + 1;
-			if (!keep_non_patch_brackets_in_subject ||
+			if (!mi->keep_non_patch_brackets_in_subject ||
 			    (7 <= remove &&
 			     memmem(subject->buf + at, remove, "PATCH", 5)))
 				strbuf_remove(subject, at, remove);
@@ -947,8 +947,8 @@ static void handle_info(struct mailinfo *mi)
 			continue;
 
 		if (!strcmp(header[i], "Subject")) {
-			if (!keep_subject) {
-				cleanup_subject(hdr);
+			if (!mi->keep_subject) {
+				cleanup_subject(mi, hdr);
 				cleanup_space(hdr);
 			}
 			output_header_lines(fout, "Subject", hdr);
@@ -1051,9 +1051,9 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 
 	while (1 < argc && argv[1][0] == '-') {
 		if (!strcmp(argv[1], "-k"))
-			keep_subject = 1;
+			mi.keep_subject = 1;
 		else if (!strcmp(argv[1], "-b"))
-			keep_non_patch_brackets_in_subject = 1;
+			mi.keep_non_patch_brackets_in_subject = 1;
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
 			add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 13/34] mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (11 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 12/34] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 14/34] mailinfo: move filter/header stage " Junio C Hamano
                       ` (22 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This requires us to pass "struct mailinfo" to more functions
throughout the codepath that read input lines.  Incidentally,
later steps are helped by this patch passing the struct to
more callchains.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 54 ++++++++++++++++++++++++++++--------------------------
 1 file changed, 28 insertions(+), 26 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 08c67f5..b150936 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,11 +7,14 @@
 #include "utf8.h"
 #include "strbuf.h"
 
-static FILE *cmitmsg, *patchfile, *fin, *fout;
+static FILE *cmitmsg, *patchfile;
 
 static const char *metainfo_charset;
 
 struct mailinfo {
+	FILE *input;
+	FILE *output;
+
 	struct strbuf name;
 	struct strbuf email;
 	int keep_subject;
@@ -790,16 +793,17 @@ static void handle_filter(struct strbuf *line, int *filter_stage, int *header_st
 	}
 }
 
-static int find_boundary(struct strbuf *line)
+static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 {
-	while (!strbuf_getline(line, fin, '\n')) {
+	while (!strbuf_getline(line, mi->input, '\n')) {
 		if (*content_top && is_multipart_boundary(line))
 			return 1;
 	}
 	return 0;
 }
 
-static int handle_boundary(struct strbuf *line, int *filter_stage, int *header_stage)
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line,
+			   int *filter_stage, int *header_stage)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -825,7 +829,7 @@ again:
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
-		if (!find_boundary(line))
+		if (!find_boundary(mi, line))
 			return 0;
 		goto again;
 	}
@@ -835,18 +839,18 @@ again:
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
-	while (read_one_header_line(line, fin))
+	while (read_one_header_line(line, mi->input))
 		check_header(line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
-	if (strbuf_getline(line, fin, '\n'))
+	if (strbuf_getline(line, mi->input, '\n'))
 		return 0;
 	strbuf_addch(line, '\n');
 	return 1;
 }
 
-static void handle_body(struct strbuf *line)
+static void handle_body(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
 	int filter_stage = 0;
@@ -854,7 +858,7 @@ static void handle_body(struct strbuf *line)
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
-		if (!find_boundary(line))
+		if (!find_boundary(mi, line))
 			goto handle_body_out;
 	}
 
@@ -866,7 +870,7 @@ static void handle_body(struct strbuf *line)
 				handle_filter(&prev, &filter_stage, &header_stage);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(line, &filter_stage, &header_stage))
+			if (!handle_boundary(mi, line, &filter_stage, &header_stage))
 				goto handle_body_out;
 		}
 
@@ -909,7 +913,7 @@ static void handle_body(struct strbuf *line)
 			handle_filter(line, &filter_stage, &header_stage);
 		}
 
-	} while (!strbuf_getwholeline(line, fin, '\n'));
+	} while (!strbuf_getwholeline(line, mi->input, '\n'));
 
 handle_body_out:
 	strbuf_release(&prev);
@@ -951,29 +955,25 @@ static void handle_info(struct mailinfo *mi)
 				cleanup_subject(mi, hdr);
 				cleanup_space(hdr);
 			}
-			output_header_lines(fout, "Subject", hdr);
+			output_header_lines(mi->output, "Subject", hdr);
 		} else if (!strcmp(header[i], "From")) {
 			cleanup_space(hdr);
 			handle_from(mi, hdr);
-			fprintf(fout, "Author: %s\n", mi->name.buf);
-			fprintf(fout, "Email: %s\n", mi->email.buf);
+			fprintf(mi->output, "Author: %s\n", mi->name.buf);
+			fprintf(mi->output, "Email: %s\n", mi->email.buf);
 		} else {
 			cleanup_space(hdr);
-			fprintf(fout, "%s: %s\n", header[i], hdr->buf);
+			fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
 		}
 	}
-	fprintf(fout, "\n");
+	fprintf(mi->output, "\n");
 }
 
-static int mailinfo(struct mailinfo *mi,
-		    FILE *in, FILE *out, const char *msg, const char *patch)
+static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 {
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	fin = in;
-	fout = out;
-
 	cmitmsg = fopen(msg, "w");
 	if (!cmitmsg) {
 		perror(msg);
@@ -990,15 +990,15 @@ static int mailinfo(struct mailinfo *mi,
 	s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
 
 	do {
-		peek = fgetc(in);
+		peek = fgetc(mi->input);
 	} while (isspace(peek));
-	ungetc(peek, in);
+	ungetc(peek, mi->input);
 
 	/* process the email header */
-	while (read_one_header_line(&line, fin))
+	while (read_one_header_line(&line, mi->input))
 		check_header(&line, p_hdr_data, 1);
 
-	handle_body(&line);
+	handle_body(mi, &line);
 	fclose(patchfile);
 
 	handle_info(mi);
@@ -1076,7 +1076,9 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	if (argc != 3)
 		usage(mailinfo_usage);
 
-	status = !!mailinfo(&mi, stdin, stdout, argv[1], argv[2]);
+	mi.input = stdin;
+	mi.output = stdout;
+	status = !!mailinfo(&mi, argv[1], argv[2]);
 	clear_mailinfo(&mi);
 
 	return status;
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 14/34] mailinfo: move filter/header stage to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (12 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 13/34] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-21 20:20       ` Stefan Beller
  2015-10-19  7:28     ` [PATCH v3 15/34] mailinfo: move patch_lines " Junio C Hamano
                       ` (21 subsequent siblings)
  35 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Earlier we got rid of two function-scope static variables that kept
track of the states of helper functions by making them extra arguments
that are passed throughout the callchain.  Now we have a convenient
place to store and pass them around in the form of "struct mailinfo",
change them into two fields in the struct.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index b150936..5823ae5 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -19,6 +19,9 @@ struct mailinfo {
 	struct strbuf email;
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
+
+	int filter_stage; /* still reading log or are we copying patch? */
+	int header_stage; /* still checking in-body headers? */
 };
 static char *message_id;
 
@@ -28,6 +31,7 @@ static enum  {
 
 static struct strbuf charset = STRBUF_INIT;
 static int patch_lines;
+
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
 static int add_message_id;
@@ -718,25 +722,25 @@ static int is_scissors_line(const struct strbuf *line)
 		gap * 2 < perforation);
 }
 
-static int handle_commit_msg(struct strbuf *line, int *still_looking)
+static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 {
 	if (!cmitmsg)
 		return 0;
 
-	if (*still_looking) {
+	if (mi->header_stage) {
 		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
 			return 0;
 	}
 
-	if (use_inbody_headers && *still_looking) {
-		*still_looking = check_header(line, s_hdr_data, 0);
-		if (*still_looking)
+	if (use_inbody_headers && mi->header_stage) {
+		mi->header_stage = check_header(line, s_hdr_data, 0);
+		if (mi->header_stage)
 			return 0;
 	} else
 		/* Only trim the first (blank) line of the commit message
 		 * when ignoring in-body headers.
 		 */
-		*still_looking = 0;
+		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
 	if (metainfo_charset)
@@ -748,7 +752,7 @@ static int handle_commit_msg(struct strbuf *line, int *still_looking)
 			die_errno("Could not rewind output message file");
 		if (ftruncate(fileno(cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
-		*still_looking = 1;
+		mi->header_stage = 1;
 
 		/*
 		 * We may have already read "secondary headers"; purge
@@ -780,13 +784,13 @@ static void handle_patch(const struct strbuf *line)
 	patch_lines++;
 }
 
-static void handle_filter(struct strbuf *line, int *filter_stage, int *header_stage)
+static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 {
-	switch (*filter_stage) {
+	switch (mi->filter_stage) {
 	case 0:
-		if (!handle_commit_msg(line, header_stage))
+		if (!handle_commit_msg(mi, line))
 			break;
-		(*filter_stage)++;
+		mi->filter_stage++;
 	case 1:
 		handle_patch(line);
 		break;
@@ -802,8 +806,7 @@ static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 	return 0;
 }
 
-static int handle_boundary(struct mailinfo *mi, struct strbuf *line,
-			   int *filter_stage, int *header_stage)
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf newline = STRBUF_INIT;
 
@@ -825,7 +828,7 @@ again:
 					"can't recover\n");
 			exit(1);
 		}
-		handle_filter(&newline, filter_stage, header_stage);
+		handle_filter(mi, &newline);
 		strbuf_release(&newline);
 
 		/* skip to the next boundary */
@@ -853,8 +856,6 @@ again:
 static void handle_body(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf prev = STRBUF_INIT;
-	int filter_stage = 0;
-	int header_stage = 1;
 
 	/* Skip up to the first boundary */
 	if (*content_top) {
@@ -867,10 +868,10 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 		if (*content_top && is_multipart_boundary(line)) {
 			/* flush any leftover */
 			if (prev.len) {
-				handle_filter(&prev, &filter_stage, &header_stage);
+				handle_filter(mi, &prev);
 				strbuf_reset(&prev);
 			}
-			if (!handle_boundary(mi, line, &filter_stage, &header_stage))
+			if (!handle_boundary(mi, line))
 				goto handle_body_out;
 		}
 
@@ -900,7 +901,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 						strbuf_addbuf(&prev, sb);
 						break;
 					}
-				handle_filter(sb, &filter_stage, &header_stage);
+				handle_filter(mi, sb);
 			}
 			/*
 			 * The partial chunk is saved in "prev" and will be
@@ -910,7 +911,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 			break;
 		}
 		default:
-			handle_filter(line, &filter_stage, &header_stage);
+			handle_filter(mi, line);
 		}
 
 	} while (!strbuf_getwholeline(line, mi->input, '\n'));
@@ -1023,6 +1024,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	memset(mi, 0, sizeof(*mi));
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
+	mi->header_stage = 1;
 	git_config(git_mailinfo_config, &mi);
 }
 
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 15/34] mailinfo: move patch_lines to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (13 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 14/34] mailinfo: move filter/header stage " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 16/34] mailinfo: move add_message_id and message_id " Junio C Hamano
                       ` (20 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This one is trivial thanks to previous steps that started passing
the structure throughout the input codepaths.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 5823ae5..8bd76c6 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -20,6 +20,7 @@ struct mailinfo {
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
 
+	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
@@ -30,7 +31,6 @@ static enum  {
 } transfer_encoding;
 
 static struct strbuf charset = STRBUF_INIT;
-static int patch_lines;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
@@ -778,10 +778,10 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	return 0;
 }
 
-static void handle_patch(const struct strbuf *line)
+static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
 {
 	fwrite(line->buf, 1, line->len, patchfile);
-	patch_lines++;
+	mi->patch_lines++;
 }
 
 static void handle_filter(struct mailinfo *mi, struct strbuf *line)
@@ -792,7 +792,7 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 			break;
 		mi->filter_stage++;
 	case 1:
-		handle_patch(line);
+		handle_patch(mi, line);
 		break;
 	}
 }
@@ -944,7 +944,7 @@ static void handle_info(struct mailinfo *mi)
 
 	for (i = 0; header[i]; i++) {
 		/* only print inbody headers if we output a patch file */
-		if (patch_lines && s_hdr_data[i])
+		if (mi->patch_lines && s_hdr_data[i])
 			hdr = s_hdr_data[i];
 		else if (p_hdr_data[i])
 			hdr = p_hdr_data[i];
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 16/34] mailinfo: move add_message_id and message_id to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (14 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 15/34] mailinfo: move patch_lines " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 17/34] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
                       ` (19 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This requires us to pass the structure into check_header() codepath.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 31 +++++++++++++++++--------------
 1 file changed, 17 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 8bd76c6..c0522f2 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -19,12 +19,13 @@ struct mailinfo {
 	struct strbuf email;
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
+	int add_message_id;
 
+	char *message_id;
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
-static char *message_id;
 
 static enum  {
 	TE_DONTCARE, TE_QP, TE_BASE64
@@ -34,7 +35,6 @@ static struct strbuf charset = STRBUF_INIT;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
-static int add_message_id;
 static int use_inbody_headers = 1;
 
 #define MAX_HDR_PARSED 10
@@ -209,10 +209,10 @@ static void handle_content_type(struct strbuf *line)
 	}
 }
 
-static void handle_message_id(const struct strbuf *line)
+static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
 {
-	if (add_message_id)
-		message_id = strdup(line->buf);
+	if (mi->add_message_id)
+		mi->message_id = strdup(line->buf);
 }
 
 static void handle_content_transfer_encoding(const struct strbuf *line)
@@ -321,11 +321,13 @@ static int is_format_patch_separator(const char *line, int len)
 	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
 }
 
-static int check_header(const struct strbuf *line,
-				struct strbuf *hdr_data[], int overwrite)
+static int check_header(struct mailinfo *mi,
+			const struct strbuf *line,
+			struct strbuf *hdr_data[], int overwrite)
 {
 	int i, ret = 0, len;
 	struct strbuf sb = STRBUF_INIT;
+
 	/* search for the interesting parts */
 	for (i = 0; header[i]; i++) {
 		int len = strlen(header[i]);
@@ -363,7 +365,7 @@ static int check_header(const struct strbuf *line,
 		len = strlen("Message-Id: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(&sb);
-		handle_message_id(&sb);
+		handle_message_id(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -733,7 +735,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (use_inbody_headers && mi->header_stage) {
-		mi->header_stage = check_header(line, s_hdr_data, 0);
+		mi->header_stage = check_header(mi, line, s_hdr_data, 0);
 		if (mi->header_stage)
 			return 0;
 	} else
@@ -767,8 +769,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (patchbreak(line)) {
-		if (message_id)
-			fprintf(cmitmsg, "Message-Id: %s\n", message_id);
+		if (mi->message_id)
+			fprintf(cmitmsg, "Message-Id: %s\n", mi->message_id);
 		fclose(cmitmsg);
 		cmitmsg = NULL;
 		return 1;
@@ -843,7 +845,7 @@ again:
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
-		check_header(line, p_hdr_data, 0);
+		check_header(mi, line, p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
@@ -997,7 +999,7 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
 	/* process the email header */
 	while (read_one_header_line(&line, mi->input))
-		check_header(&line, p_hdr_data, 1);
+		check_header(mi, &line, p_hdr_data, 1);
 
 	handle_body(mi, &line);
 	fclose(patchfile);
@@ -1032,6 +1034,7 @@ static void clear_mailinfo(struct mailinfo *mi)
 {
 	strbuf_release(&mi->name);
 	strbuf_release(&mi->email);
+	free(mi->message_id);
 }
 
 static const char mailinfo_usage[] =
@@ -1057,7 +1060,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-b"))
 			mi.keep_non_patch_brackets_in_subject = 1;
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
-			add_message_id = 1;
+			mi.add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
 			metainfo_charset = def_charset;
 		else if (!strcmp(argv[1], "-n"))
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 17/34] mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (15 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 16/34] mailinfo: move add_message_id and message_id " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-21 20:24       ` Stefan Beller
  2015-10-19  7:28     ` [PATCH v3 18/34] mailinfo: move metainfo_charset " Junio C Hamano
                       ` (18 subsequent siblings)
  35 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index c0522f2..2c8f249 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -20,6 +20,8 @@ struct mailinfo {
 	int keep_subject;
 	int keep_non_patch_brackets_in_subject;
 	int add_message_id;
+	int use_scissors;
+	int use_inbody_headers; /* defaults to 1 */
 
 	char *message_id;
 	int patch_lines;
@@ -34,8 +36,6 @@ static enum  {
 static struct strbuf charset = STRBUF_INIT;
 
 static struct strbuf **p_hdr_data, **s_hdr_data;
-static int use_scissors;
-static int use_inbody_headers = 1;
 
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
@@ -734,7 +734,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 			return 0;
 	}
 
-	if (use_inbody_headers && mi->header_stage) {
+	if (mi->use_inbody_headers && mi->header_stage) {
 		mi->header_stage = check_header(mi, line, s_hdr_data, 0);
 		if (mi->header_stage)
 			return 0;
@@ -748,7 +748,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	if (metainfo_charset)
 		convert_to_utf8(line, charset.buf);
 
-	if (use_scissors && is_scissors_line(line)) {
+	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
 		if (fseek(cmitmsg, 0L, SEEK_SET))
 			die_errno("Could not rewind output message file");
@@ -1009,12 +1009,14 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 	return 0;
 }
 
-static int git_mailinfo_config(const char *var, const char *value, void *unused)
+static int git_mailinfo_config(const char *var, const char *value, void *mi_)
 {
+	struct mailinfo *mi = mi_;
+
 	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, unused);
+		return git_default_config(var, value, NULL);
 	if (!strcmp(var, "mailinfo.scissors")) {
-		use_scissors = git_config_bool(var, value);
+		mi->use_scissors = git_config_bool(var, value);
 		return 0;
 	}
 	/* perhaps others here */
@@ -1027,6 +1029,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
 	mi->header_stage = 1;
+	mi->use_inbody_headers = 1;
 	git_config(git_mailinfo_config, &mi);
 }
 
@@ -1068,11 +1071,11 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (starts_with(argv[1], "--encoding="))
 			metainfo_charset = argv[1] + 11;
 		else if (!strcmp(argv[1], "--scissors"))
-			use_scissors = 1;
+			mi.use_scissors = 1;
 		else if (!strcmp(argv[1], "--no-scissors"))
-			use_scissors = 0;
+			mi.use_scissors = 0;
 		else if (!strcmp(argv[1], "--no-inbody-headers"))
-			use_inbody_headers = 0;
+			mi.use_inbody_headers = 0;
 		else
 			usage(mailinfo_usage);
 		argc--; argv++;
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 18/34] mailinfo: move metainfo_charset to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (16 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 17/34] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 19/34] mailinfo: move check for metainfo_charset to convert_to_utf8() Junio C Hamano
                       ` (17 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This requires us to pass the struct down to decode_header() and
convert_to_utf8() callchain.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 2c8f249..26fd9b1 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -9,8 +9,6 @@
 
 static FILE *cmitmsg, *patchfile;
 
-static const char *metainfo_charset;
-
 struct mailinfo {
 	FILE *input;
 	FILE *output;
@@ -22,6 +20,7 @@ struct mailinfo {
 	int add_message_id;
 	int use_scissors;
 	int use_inbody_headers; /* defaults to 1 */
+	const char *metainfo_charset;
 
 	char *message_id;
 	int patch_lines;
@@ -293,7 +292,7 @@ static void cleanup_space(struct strbuf *sb)
 	}
 }
 
-static void decode_header(struct strbuf *line);
+static void decode_header(struct mailinfo *mi, struct strbuf *line);
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
@@ -336,7 +335,7 @@ static int check_header(struct mailinfo *mi,
 			 * normalize the meta information to utf8.
 			 */
 			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(&sb);
+			decode_header(mi, &sb);
 			handle_header(&hdr_data[i], &sb);
 			ret = 1;
 			goto check_header_out;
@@ -347,7 +346,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Content-Type")) {
 		len = strlen("Content-Type: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		strbuf_insert(&sb, 0, "Content-Type: ", len);
 		handle_content_type(&sb);
 		ret = 1;
@@ -356,7 +355,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Content-Transfer-Encoding")) {
 		len = strlen("Content-Transfer-Encoding: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		handle_content_transfer_encoding(&sb);
 		ret = 1;
 		goto check_header_out;
@@ -364,7 +363,7 @@ static int check_header(struct mailinfo *mi,
 	if (cmp_header(line, "Message-Id")) {
 		len = strlen("Message-Id: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(&sb);
+		decode_header(mi, &sb);
 		handle_message_id(mi, &sb);
 		ret = 1;
 		goto check_header_out;
@@ -520,23 +519,24 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
 	return out;
 }
 
-static void convert_to_utf8(struct strbuf *line, const char *charset)
+static void convert_to_utf8(struct mailinfo *mi,
+			    struct strbuf *line, const char *charset)
 {
 	char *out;
 
 	if (!charset || !*charset)
 		return;
 
-	if (same_encoding(metainfo_charset, charset))
+	if (same_encoding(mi->metainfo_charset, charset))
 		return;
-	out = reencode_string(line->buf, metainfo_charset, charset);
+	out = reencode_string(line->buf, mi->metainfo_charset, charset);
 	if (!out)
 		die("cannot convert from %s to %s",
-		    charset, metainfo_charset);
+		    charset, mi->metainfo_charset);
 	strbuf_attach(line, out, strlen(out), strlen(out));
 }
 
-static void decode_header(struct strbuf *it)
+static void decode_header(struct mailinfo *mi, struct strbuf *it)
 {
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
@@ -599,8 +599,8 @@ static void decode_header(struct strbuf *it)
 			dec = decode_q_segment(&piecebuf, 1);
 			break;
 		}
-		if (metainfo_charset)
-			convert_to_utf8(dec, charset_q.buf);
+		if (mi->metainfo_charset)
+			convert_to_utf8(mi, dec, charset_q.buf);
 
 		strbuf_addbuf(&outbuf, dec);
 		strbuf_release(dec);
@@ -745,8 +745,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	if (metainfo_charset)
-		convert_to_utf8(line, charset.buf);
+	if (mi->metainfo_charset)
+		convert_to_utf8(mi, line, charset.buf);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -1055,7 +1055,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 	setup_mailinfo(&mi);
 
 	def_charset = get_commit_output_encoding();
-	metainfo_charset = def_charset;
+	mi.metainfo_charset = def_charset;
 
 	while (1 < argc && argv[1][0] == '-') {
 		if (!strcmp(argv[1], "-k"))
@@ -1065,11 +1065,11 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
 			mi.add_message_id = 1;
 		else if (!strcmp(argv[1], "-u"))
-			metainfo_charset = def_charset;
+			mi.metainfo_charset = def_charset;
 		else if (!strcmp(argv[1], "-n"))
-			metainfo_charset = NULL;
+			mi.metainfo_charset = NULL;
 		else if (starts_with(argv[1], "--encoding="))
-			metainfo_charset = argv[1] + 11;
+			mi.metainfo_charset = argv[1] + 11;
 		else if (!strcmp(argv[1], "--scissors"))
 			mi.use_scissors = 1;
 		else if (!strcmp(argv[1], "--no-scissors"))
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 19/34] mailinfo: move check for metainfo_charset to convert_to_utf8()
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (17 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 18/34] mailinfo: move metainfo_charset " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19 22:57       ` Eric Sunshine
  2015-10-19  7:28     ` [PATCH v3 20/34] mailinfo: move transfer_encoding to struct mailinfo Junio C Hamano
                       ` (16 subsequent siblings)
  35 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

All callers of this function refrains from calling it when
mi->metainfo_charset is NULL; move the check to the callee,
as it already has a few conditions at its beginning to turn
it into a no-op.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 26fd9b1..737d0fc 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -524,7 +524,7 @@ static void convert_to_utf8(struct mailinfo *mi,
 {
 	char *out;
 
-	if (!charset || !*charset)
+	if (!mi->metainfo_charset || !charset || !*charset)
 		return;
 
 	if (same_encoding(mi->metainfo_charset, charset))
@@ -599,8 +599,7 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
 			dec = decode_q_segment(&piecebuf, 1);
 			break;
 		}
-		if (mi->metainfo_charset)
-			convert_to_utf8(mi, dec, charset_q.buf);
+		convert_to_utf8(mi, dec, charset_q.buf);
 
 		strbuf_addbuf(&outbuf, dec);
 		strbuf_release(dec);
@@ -745,8 +744,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	if (mi->metainfo_charset)
-		convert_to_utf8(mi, line, charset.buf);
+	convert_to_utf8(mi, line, charset.buf);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 20/34] mailinfo: move transfer_encoding to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (18 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 19/34] mailinfo: move check for metainfo_charset to convert_to_utf8() Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 21/34] mailinfo: move charset " Junio C Hamano
                       ` (15 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 737d0fc..18781b7 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -23,14 +23,14 @@ struct mailinfo {
 	const char *metainfo_charset;
 
 	char *message_id;
+	enum  {
+		TE_DONTCARE, TE_QP, TE_BASE64
+	} transfer_encoding;
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
 };
 
-static enum  {
-	TE_DONTCARE, TE_QP, TE_BASE64
-} transfer_encoding;
 
 static struct strbuf charset = STRBUF_INIT;
 
@@ -214,14 +214,15 @@ static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
 		mi->message_id = strdup(line->buf);
 }
 
-static void handle_content_transfer_encoding(const struct strbuf *line)
+static void handle_content_transfer_encoding(struct mailinfo *mi,
+					     const struct strbuf *line)
 {
 	if (strcasestr(line->buf, "base64"))
-		transfer_encoding = TE_BASE64;
+		mi->transfer_encoding = TE_BASE64;
 	else if (strcasestr(line->buf, "quoted-printable"))
-		transfer_encoding = TE_QP;
+		mi->transfer_encoding = TE_QP;
 	else
-		transfer_encoding = TE_DONTCARE;
+		mi->transfer_encoding = TE_DONTCARE;
 }
 
 static int is_multipart_boundary(const struct strbuf *line)
@@ -356,7 +357,7 @@ static int check_header(struct mailinfo *mi,
 		len = strlen("Content-Transfer-Encoding: ");
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(mi, &sb);
-		handle_content_transfer_encoding(&sb);
+		handle_content_transfer_encoding(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -615,11 +616,11 @@ release_return:
 	strbuf_release(&piecebuf);
 }
 
-static void decode_transfer_encoding(struct strbuf *line)
+static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *ret;
 
-	switch (transfer_encoding) {
+	switch (mi->transfer_encoding) {
 	case TE_QP:
 		ret = decode_q_segment(line, 0);
 		break;
@@ -838,7 +839,7 @@ again:
 	}
 
 	/* set some defaults */
-	transfer_encoding = TE_DONTCARE;
+	mi->transfer_encoding = TE_DONTCARE;
 	strbuf_reset(&charset);
 
 	/* slurp in this section's info */
@@ -876,9 +877,9 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 		}
 
 		/* Unwrap transfer encoding */
-		decode_transfer_encoding(line);
+		decode_transfer_encoding(mi, line);
 
-		switch (transfer_encoding) {
+		switch (mi->transfer_encoding) {
 		case TE_BASE64:
 		case TE_QP:
 		{
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 21/34] mailinfo: move charset to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (19 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 20/34] mailinfo: move transfer_encoding to struct mailinfo Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 22/34] mailinfo: move cmitmsg and patchfile " Junio C Hamano
                       ` (14 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 18781b7..810d132 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -22,6 +22,7 @@ struct mailinfo {
 	int use_inbody_headers; /* defaults to 1 */
 	const char *metainfo_charset;
 
+	struct strbuf charset;
 	char *message_id;
 	enum  {
 		TE_DONTCARE, TE_QP, TE_BASE64
@@ -31,9 +32,6 @@ struct mailinfo {
 	int header_stage; /* still checking in-body headers? */
 };
 
-
-static struct strbuf charset = STRBUF_INIT;
-
 static struct strbuf **p_hdr_data, **s_hdr_data;
 
 #define MAX_HDR_PARSED 10
@@ -186,7 +184,7 @@ static struct strbuf *content[MAX_BOUNDARIES];
 
 static struct strbuf **content_top = content;
 
-static void handle_content_type(struct strbuf *line)
+static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
 	strbuf_init(boundary, line->len);
@@ -200,7 +198,7 @@ static void handle_content_type(struct strbuf *line)
 		*content_top = boundary;
 		boundary = NULL;
 	}
-	slurp_attr(line->buf, "charset=", &charset);
+	slurp_attr(line->buf, "charset=", &mi->charset);
 
 	if (boundary) {
 		strbuf_release(boundary);
@@ -349,7 +347,7 @@ static int check_header(struct mailinfo *mi,
 		strbuf_add(&sb, line->buf + len, line->len - len);
 		decode_header(mi, &sb);
 		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(&sb);
+		handle_content_type(mi, &sb);
 		ret = 1;
 		goto check_header_out;
 	}
@@ -745,7 +743,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	convert_to_utf8(mi, line, charset.buf);
+	convert_to_utf8(mi, line, mi->charset.buf);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -840,7 +838,7 @@ again:
 
 	/* set some defaults */
 	mi->transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&charset);
+	strbuf_reset(&mi->charset);
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
@@ -1027,6 +1025,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	memset(mi, 0, sizeof(*mi));
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
+	strbuf_init(&mi->charset, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
 	git_config(git_mailinfo_config, &mi);
@@ -1036,6 +1035,7 @@ static void clear_mailinfo(struct mailinfo *mi)
 {
 	strbuf_release(&mi->name);
 	strbuf_release(&mi->email);
+	strbuf_release(&mi->charset);
 	free(mi->message_id);
 }
 
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 22/34] mailinfo: move cmitmsg and patchfile to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (20 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 21/34] mailinfo: move charset " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 23/34] mailinfo: move [ps]_hdr_data " Junio C Hamano
                       ` (13 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 810d132..b8b94d6 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,11 +7,11 @@
 #include "utf8.h"
 #include "strbuf.h"
 
-static FILE *cmitmsg, *patchfile;
-
 struct mailinfo {
 	FILE *input;
 	FILE *output;
+	FILE *cmitmsg;
+	FILE *patchfile;
 
 	struct strbuf name;
 	struct strbuf email;
@@ -724,7 +724,7 @@ static int is_scissors_line(const struct strbuf *line)
 
 static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 {
-	if (!cmitmsg)
+	if (!mi->cmitmsg)
 		return 0;
 
 	if (mi->header_stage) {
@@ -747,9 +747,9 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-		if (fseek(cmitmsg, 0L, SEEK_SET))
+		if (fseek(mi->cmitmsg, 0L, SEEK_SET))
 			die_errno("Could not rewind output message file");
-		if (ftruncate(fileno(cmitmsg), 0))
+		if (ftruncate(fileno(mi->cmitmsg), 0))
 			die_errno("Could not truncate output message file at scissors");
 		mi->header_stage = 1;
 
@@ -767,19 +767,19 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (patchbreak(line)) {
 		if (mi->message_id)
-			fprintf(cmitmsg, "Message-Id: %s\n", mi->message_id);
-		fclose(cmitmsg);
-		cmitmsg = NULL;
+			fprintf(mi->cmitmsg, "Message-Id: %s\n", mi->message_id);
+		fclose(mi->cmitmsg);
+		mi->cmitmsg = NULL;
 		return 1;
 	}
 
-	fputs(line->buf, cmitmsg);
+	fputs(line->buf, mi->cmitmsg);
 	return 0;
 }
 
 static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
 {
-	fwrite(line->buf, 1, line->len, patchfile);
+	fwrite(line->buf, 1, line->len, mi->patchfile);
 	mi->patch_lines++;
 }
 
@@ -974,15 +974,15 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	cmitmsg = fopen(msg, "w");
-	if (!cmitmsg) {
+	mi->cmitmsg = fopen(msg, "w");
+	if (!mi->cmitmsg) {
 		perror(msg);
 		return -1;
 	}
-	patchfile = fopen(patch, "w");
-	if (!patchfile) {
+	mi->patchfile = fopen(patch, "w");
+	if (!mi->patchfile) {
 		perror(patch);
-		fclose(cmitmsg);
+		fclose(mi->cmitmsg);
 		return -1;
 	}
 
@@ -999,7 +999,7 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 		check_header(mi, &line, p_hdr_data, 1);
 
 	handle_body(mi, &line);
-	fclose(patchfile);
+	fclose(mi->patchfile);
 
 	handle_info(mi);
 
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 23/34] mailinfo: move [ps]_hdr_data to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (21 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 22/34] mailinfo: move cmitmsg and patchfile " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-21 20:30       ` Stefan Beller
  2015-10-19  7:28     ` [PATCH v3 24/34] mailinfo: move content/content_top " Junio C Hamano
                       ` (12 subsequent siblings)
  35 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 37 +++++++++++++++++++++++--------------
 1 file changed, 23 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index b8b94d6..2c194da 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -30,10 +30,10 @@ struct mailinfo {
 	int patch_lines;
 	int filter_stage; /* still reading log or are we copying patch? */
 	int header_stage; /* still checking in-body headers? */
+	struct strbuf **p_hdr_data;
+	struct strbuf **s_hdr_data;
 };
 
-static struct strbuf **p_hdr_data, **s_hdr_data;
-
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
 
@@ -733,7 +733,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 	}
 
 	if (mi->use_inbody_headers && mi->header_stage) {
-		mi->header_stage = check_header(mi, line, s_hdr_data, 0);
+		mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);
 		if (mi->header_stage)
 			return 0;
 	} else
@@ -758,9 +758,9 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		 * them to give ourselves a clean restart.
 		 */
 		for (i = 0; header[i]; i++) {
-			if (s_hdr_data[i])
-				strbuf_release(s_hdr_data[i]);
-			s_hdr_data[i] = NULL;
+			if (mi->s_hdr_data[i])
+				strbuf_release(mi->s_hdr_data[i]);
+			mi->s_hdr_data[i] = NULL;
 		}
 		return 0;
 	}
@@ -842,7 +842,7 @@ again:
 
 	/* slurp in this section's info */
 	while (read_one_header_line(line, mi->input))
-		check_header(mi, line, p_hdr_data, 0);
+		check_header(mi, line, mi->p_hdr_data, 0);
 
 	strbuf_release(&newline);
 	/* replenish line */
@@ -943,10 +943,10 @@ static void handle_info(struct mailinfo *mi)
 
 	for (i = 0; header[i]; i++) {
 		/* only print inbody headers if we output a patch file */
-		if (mi->patch_lines && s_hdr_data[i])
-			hdr = s_hdr_data[i];
-		else if (p_hdr_data[i])
-			hdr = p_hdr_data[i];
+		if (mi->patch_lines && mi->s_hdr_data[i])
+			hdr = mi->s_hdr_data[i];
+		else if (mi->p_hdr_data[i])
+			hdr = mi->p_hdr_data[i];
 		else
 			continue;
 
@@ -986,8 +986,8 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 		return -1;
 	}
 
-	p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*p_hdr_data));
-	s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
+	mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+	mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
 
 	do {
 		peek = fgetc(mi->input);
@@ -996,7 +996,7 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
 	/* process the email header */
 	while (read_one_header_line(&line, mi->input))
-		check_header(mi, &line, p_hdr_data, 1);
+		check_header(mi, &line, mi->p_hdr_data, 1);
 
 	handle_body(mi, &line);
 	fclose(mi->patchfile);
@@ -1033,10 +1033,19 @@ static void setup_mailinfo(struct mailinfo *mi)
 
 static void clear_mailinfo(struct mailinfo *mi)
 {
+	int i;
+
 	strbuf_release(&mi->name);
 	strbuf_release(&mi->email);
 	strbuf_release(&mi->charset);
 	free(mi->message_id);
+
+	for (i = 0; mi->p_hdr_data[i]; i++)
+		strbuf_release(mi->p_hdr_data[i]);
+	free(mi->p_hdr_data);
+	for (i = 0; mi->s_hdr_data[i]; i++)
+		strbuf_release(mi->s_hdr_data[i]);
+	free(mi->s_hdr_data);
 }
 
 static const char mailinfo_usage[] =
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 24/34] mailinfo: move content/content_top to struct mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (22 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 23/34] mailinfo: move [ps]_hdr_data " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-21 20:36       ` Stefan Beller
  2015-10-19  7:28     ` [PATCH v3 25/34] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
                       ` (11 subsequent siblings)
  35 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 45 ++++++++++++++++++++++++++-------------------
 1 file changed, 26 insertions(+), 19 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 2c194da..ec65805 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -7,6 +7,8 @@
 #include "utf8.h"
 #include "strbuf.h"
 
+#define MAX_BOUNDARIES 5
+
 struct mailinfo {
 	FILE *input;
 	FILE *output;
@@ -22,6 +24,8 @@ struct mailinfo {
 	int use_inbody_headers; /* defaults to 1 */
 	const char *metainfo_charset;
 
+	struct strbuf *content[MAX_BOUNDARIES];
+	struct strbuf **content_top;
 	struct strbuf charset;
 	char *message_id;
 	enum  {
@@ -35,7 +39,6 @@ struct mailinfo {
 };
 
 #define MAX_HDR_PARSED 10
-#define MAX_BOUNDARIES 5
 
 static void cleanup_space(struct strbuf *sb);
 
@@ -180,10 +183,6 @@ static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
 	return 1;
 }
 
-static struct strbuf *content[MAX_BOUNDARIES];
-
-static struct strbuf **content_top = content;
-
 static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
@@ -191,11 +190,11 @@ static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
-		if (++content_top >= &content[MAX_BOUNDARIES]) {
+		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
 			fprintf(stderr, "Too many boundaries to handle\n");
 			exit(1);
 		}
-		*content_top = boundary;
+		*(mi->content_top) = boundary;
 		boundary = NULL;
 	}
 	slurp_attr(line->buf, "charset=", &mi->charset);
@@ -223,10 +222,12 @@ static void handle_content_transfer_encoding(struct mailinfo *mi,
 		mi->transfer_encoding = TE_DONTCARE;
 }
 
-static int is_multipart_boundary(const struct strbuf *line)
+static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
 {
-	return (((*content_top)->len <= line->len) &&
-		!memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
+	struct strbuf *content_top = *(mi->content_top);
+
+	return ((content_top->len <= line->len) &&
+		!memcmp(line->buf, content_top->buf, content_top->len));
 }
 
 static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
@@ -799,7 +800,7 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 {
 	while (!strbuf_getline(line, mi->input, '\n')) {
-		if (*content_top && is_multipart_boundary(line))
+		if (*(mi->content_top) && is_multipart_boundary(mi, line))
 			return 1;
 	}
 	return 0;
@@ -811,18 +812,18 @@ static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
 
 	strbuf_addch(&newline, '\n');
 again:
-	if (line->len >= (*content_top)->len + 2 &&
-	    !memcmp(line->buf + (*content_top)->len, "--", 2)) {
+	if (line->len >= (*(mi->content_top))->len + 2 &&
+	    !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
 		/* we hit an end boundary */
 		/* pop the current boundary off the stack */
-		strbuf_release(*content_top);
-		free(*content_top);
-		*content_top = NULL;
+		strbuf_release(*(mi->content_top));
+		free(*(mi->content_top));
+		*(mi->content_top) = NULL;
 
 		/* technically won't happen as is_multipart_boundary()
 		   will fail first.  But just in case..
 		 */
-		if (--content_top < content) {
+		if (--mi->content_top < mi->content) {
 			fprintf(stderr, "Detected mismatched boundaries, "
 					"can't recover\n");
 			exit(1);
@@ -857,14 +858,14 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 	struct strbuf prev = STRBUF_INIT;
 
 	/* Skip up to the first boundary */
-	if (*content_top) {
+	if (*(mi->content_top)) {
 		if (!find_boundary(mi, line))
 			goto handle_body_out;
 	}
 
 	do {
 		/* process any boundary lines */
-		if (*content_top && is_multipart_boundary(line)) {
+		if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
 			/* flush any leftover */
 			if (prev.len) {
 				handle_filter(mi, &prev);
@@ -1028,6 +1029,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->charset, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
+	mi->content_top = mi->content;
 	git_config(git_mailinfo_config, &mi);
 }
 
@@ -1046,6 +1048,11 @@ static void clear_mailinfo(struct mailinfo *mi)
 	for (i = 0; mi->s_hdr_data[i]; i++)
 		strbuf_release(mi->s_hdr_data[i]);
 	free(mi->s_hdr_data);
+
+	while (mi->content < mi->content_top) {
+		free(*(mi->content_top));
+		mi->content_top--;
+	}
 }
 
 static const char mailinfo_usage[] =
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 25/34] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (23 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 24/34] mailinfo: move content/content_top " Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 26/34] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
                       ` (10 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

There is a strange "if (!mi->cmitmsg) return 0" at the very beginning
of handle_commit_msg(), but the condition should never trigger, because:

 * The only place cmitmsg is set to NULL is after this function sees
   a patch break, closes the FILE * to write the commit log message
   and returns 1.  This function returns non-zero only from that
   codepath.

 * The caller of this function, upon seeing a non-zero return,
   increments filter_stage, starts treating the input as patch text
   and will never call handle_commit_msg() again.

Replace it with an assert(!mi->filter_stage) to ensure the above
observation will stay to be true.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index ec65805..286eda0 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -725,8 +725,7 @@ static int is_scissors_line(const struct strbuf *line)
 
 static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 {
-	if (!mi->cmitmsg)
-		return 0;
+	assert(!mi->filter_stage);
 
 	if (mi->header_stage) {
 		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 26/34] mailinfo: keep the parsed log message in a strbuf
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (24 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 25/34] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 27/34] mailinfo: move read_one_header_line() closer to its callers Junio C Hamano
                       ` (9 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

When mailinfo() is eventually libified, the calling "git am" still
will have to write out the log message in the "msg" file for hooks
and other users of the information, but it does not have to reopen
and reread what it wrote earlier if the function kept it in a strbuf.

This also removes the need for seeking and truncating the output
file when we see a scissors mark in the input, which in turn allows
us to lose two callsites of die_errno().

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 286eda0..81bb3c7 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -12,7 +12,6 @@
 struct mailinfo {
 	FILE *input;
 	FILE *output;
-	FILE *cmitmsg;
 	FILE *patchfile;
 
 	struct strbuf name;
@@ -36,6 +35,8 @@ struct mailinfo {
 	int header_stage; /* still checking in-body headers? */
 	struct strbuf **p_hdr_data;
 	struct strbuf **s_hdr_data;
+
+	struct strbuf log_message;
 };
 
 #define MAX_HDR_PARSED 10
@@ -747,10 +748,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-		if (fseek(mi->cmitmsg, 0L, SEEK_SET))
-			die_errno("Could not rewind output message file");
-		if (ftruncate(fileno(mi->cmitmsg), 0))
-			die_errno("Could not truncate output message file at scissors");
+
+		strbuf_setlen(&mi->log_message, 0);
 		mi->header_stage = 1;
 
 		/*
@@ -767,13 +766,12 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	if (patchbreak(line)) {
 		if (mi->message_id)
-			fprintf(mi->cmitmsg, "Message-Id: %s\n", mi->message_id);
-		fclose(mi->cmitmsg);
-		mi->cmitmsg = NULL;
+			strbuf_addf(&mi->log_message,
+				    "Message-Id: %s\n", mi->message_id);
 		return 1;
 	}
 
-	fputs(line->buf, mi->cmitmsg);
+	strbuf_addbuf(&mi->log_message, line);
 	return 0;
 }
 
@@ -971,18 +969,19 @@ static void handle_info(struct mailinfo *mi)
 
 static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 {
+	FILE *cmitmsg;
 	int peek;
 	struct strbuf line = STRBUF_INIT;
 
-	mi->cmitmsg = fopen(msg, "w");
-	if (!mi->cmitmsg) {
+	cmitmsg = fopen(msg, "w");
+	if (!cmitmsg) {
 		perror(msg);
 		return -1;
 	}
 	mi->patchfile = fopen(patch, "w");
 	if (!mi->patchfile) {
 		perror(patch);
-		fclose(mi->cmitmsg);
+		fclose(cmitmsg);
 		return -1;
 	}
 
@@ -999,6 +998,8 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 		check_header(mi, &line, mi->p_hdr_data, 1);
 
 	handle_body(mi, &line);
+	fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+	fclose(cmitmsg);
 	fclose(mi->patchfile);
 
 	handle_info(mi);
@@ -1026,6 +1027,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	strbuf_init(&mi->name, 0);
 	strbuf_init(&mi->email, 0);
 	strbuf_init(&mi->charset, 0);
+	strbuf_init(&mi->log_message, 0);
 	mi->header_stage = 1;
 	mi->use_inbody_headers = 1;
 	mi->content_top = mi->content;
@@ -1052,6 +1054,8 @@ static void clear_mailinfo(struct mailinfo *mi)
 		free(*(mi->content_top));
 		mi->content_top--;
 	}
+
+	strbuf_release(&mi->log_message);
 }
 
 static const char mailinfo_usage[] =
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 27/34] mailinfo: move read_one_header_line() closer to its callers
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (25 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 26/34] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 28/34] mailinfo: move check_header() after the helpers it uses Junio C Hamano
                       ` (8 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 132 ++++++++++++++++++++++++++---------------------------
 1 file changed, 66 insertions(+), 66 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 81bb3c7..5a21d93 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -390,72 +390,6 @@ check_header_out:
 	return ret;
 }
 
-static int is_rfc2822_header(const struct strbuf *line)
-{
-	/*
-	 * The section that defines the loosest possible
-	 * field name is "3.6.8 Optional fields".
-	 *
-	 * optional-field = field-name ":" unstructured CRLF
-	 * field-name = 1*ftext
-	 * ftext = %d33-57 / %59-126
-	 */
-	int ch;
-	char *cp = line->buf;
-
-	/* Count mbox From headers as headers */
-	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
-		return 1;
-
-	while ((ch = *cp++)) {
-		if (ch == ':')
-			return 1;
-		if ((33 <= ch && ch <= 57) ||
-		    (59 <= ch && ch <= 126))
-			continue;
-		break;
-	}
-	return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
-	/* Get the first part of the line. */
-	if (strbuf_getline(line, in, '\n'))
-		return 0;
-
-	/*
-	 * Is it an empty line or not a valid rfc2822 header?
-	 * If so, stop here, and return false ("not a header")
-	 */
-	strbuf_rtrim(line);
-	if (!line->len || !is_rfc2822_header(line)) {
-		/* Re-add the newline */
-		strbuf_addch(line, '\n');
-		return 0;
-	}
-
-	/*
-	 * Now we need to eat all the continuation lines..
-	 * Yuck, 2822 header "folding"
-	 */
-	for (;;) {
-		int peek;
-		struct strbuf continuation = STRBUF_INIT;
-
-		peek = fgetc(in); ungetc(peek, in);
-		if (peek != ' ' && peek != '\t')
-			break;
-		if (strbuf_getline(&continuation, in, '\n'))
-			break;
-		continuation.buf[0] = ' ';
-		strbuf_rtrim(&continuation);
-		strbuf_addbuf(line, &continuation);
-	}
-
-	return 1;
-}
-
 static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
 {
 	const char *in = q_seg->buf;
@@ -794,6 +728,72 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
 	}
 }
 
+static int is_rfc2822_header(const struct strbuf *line)
+{
+	/*
+	 * The section that defines the loosest possible
+	 * field name is "3.6.8 Optional fields".
+	 *
+	 * optional-field = field-name ":" unstructured CRLF
+	 * field-name = 1*ftext
+	 * ftext = %d33-57 / %59-126
+	 */
+	int ch;
+	char *cp = line->buf;
+
+	/* Count mbox From headers as headers */
+	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
+		return 1;
+
+	while ((ch = *cp++)) {
+		if (ch == ':')
+			return 1;
+		if ((33 <= ch && ch <= 57) ||
+		    (59 <= ch && ch <= 126))
+			continue;
+		break;
+	}
+	return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+	/* Get the first part of the line. */
+	if (strbuf_getline(line, in, '\n'))
+		return 0;
+
+	/*
+	 * Is it an empty line or not a valid rfc2822 header?
+	 * If so, stop here, and return false ("not a header")
+	 */
+	strbuf_rtrim(line);
+	if (!line->len || !is_rfc2822_header(line)) {
+		/* Re-add the newline */
+		strbuf_addch(line, '\n');
+		return 0;
+	}
+
+	/*
+	 * Now we need to eat all the continuation lines..
+	 * Yuck, 2822 header "folding"
+	 */
+	for (;;) {
+		int peek;
+		struct strbuf continuation = STRBUF_INIT;
+
+		peek = fgetc(in); ungetc(peek, in);
+		if (peek != ' ' && peek != '\t')
+			break;
+		if (strbuf_getline(&continuation, in, '\n'))
+			break;
+		continuation.buf[0] = ' ';
+		strbuf_rtrim(&continuation);
+		strbuf_addbuf(line, &continuation);
+	}
+
+	return 1;
+}
+
 static int find_boundary(struct mailinfo *mi, struct strbuf *line)
 {
 	while (!strbuf_getline(line, mi->input, '\n')) {
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 28/34] mailinfo: move check_header() after the helpers it uses
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (26 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 27/34] mailinfo: move read_one_header_line() closer to its callers Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 29/34] mailinfo: move cleanup_space() before its users Junio C Hamano
                       ` (7 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

This way, we can lose a forward decl for decode_header().

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 139 ++++++++++++++++++++++++++---------------------------
 1 file changed, 69 insertions(+), 70 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 5a21d93..6fc6aa8 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -293,7 +293,6 @@ static void cleanup_space(struct strbuf *sb)
 	}
 }
 
-static void decode_header(struct mailinfo *mi, struct strbuf *line);
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
@@ -321,75 +320,6 @@ static int is_format_patch_separator(const char *line, int len)
 	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
 }
 
-static int check_header(struct mailinfo *mi,
-			const struct strbuf *line,
-			struct strbuf *hdr_data[], int overwrite)
-{
-	int i, ret = 0, len;
-	struct strbuf sb = STRBUF_INIT;
-
-	/* search for the interesting parts */
-	for (i = 0; header[i]; i++) {
-		int len = strlen(header[i]);
-		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
-			/* Unwrap inline B and Q encoding, and optionally
-			 * normalize the meta information to utf8.
-			 */
-			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(mi, &sb);
-			handle_header(&hdr_data[i], &sb);
-			ret = 1;
-			goto check_header_out;
-		}
-	}
-
-	/* Content stuff */
-	if (cmp_header(line, "Content-Type")) {
-		len = strlen("Content-Type: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Content-Transfer-Encoding")) {
-		len = strlen("Content-Transfer-Encoding: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_content_transfer_encoding(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Message-Id")) {
-		len = strlen("Message-Id: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_message_id(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-
-	/* for inbody stuff */
-	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
-		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
-		goto check_header_out;
-	}
-	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-		for (i = 0; header[i]; i++) {
-			if (!strcmp("Subject", header[i])) {
-				handle_header(&hdr_data[i], line);
-				ret = 1;
-				goto check_header_out;
-			}
-		}
-	}
-
-check_header_out:
-	strbuf_release(&sb);
-	return ret;
-}
-
 static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
 {
 	const char *in = q_seg->buf;
@@ -550,6 +480,75 @@ release_return:
 	strbuf_release(&piecebuf);
 }
 
+static int check_header(struct mailinfo *mi,
+			const struct strbuf *line,
+			struct strbuf *hdr_data[], int overwrite)
+{
+	int i, ret = 0, len;
+	struct strbuf sb = STRBUF_INIT;
+
+	/* search for the interesting parts */
+	for (i = 0; header[i]; i++) {
+		int len = strlen(header[i]);
+		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+			/* Unwrap inline B and Q encoding, and optionally
+			 * normalize the meta information to utf8.
+			 */
+			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+			decode_header(mi, &sb);
+			handle_header(&hdr_data[i], &sb);
+			ret = 1;
+			goto check_header_out;
+		}
+	}
+
+	/* Content stuff */
+	if (cmp_header(line, "Content-Type")) {
+		len = strlen("Content-Type: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		strbuf_insert(&sb, 0, "Content-Type: ", len);
+		handle_content_type(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Content-Transfer-Encoding")) {
+		len = strlen("Content-Transfer-Encoding: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		handle_content_transfer_encoding(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+	if (cmp_header(line, "Message-Id")) {
+		len = strlen("Message-Id: ");
+		strbuf_add(&sb, line->buf + len, line->len - len);
+		decode_header(mi, &sb);
+		handle_message_id(mi, &sb);
+		ret = 1;
+		goto check_header_out;
+	}
+
+	/* for inbody stuff */
+	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
+		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
+		goto check_header_out;
+	}
+	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+		for (i = 0; header[i]; i++) {
+			if (!strcmp("Subject", header[i])) {
+				handle_header(&hdr_data[i], line);
+				ret = 1;
+				goto check_header_out;
+			}
+		}
+	}
+
+check_header_out:
+	strbuf_release(&sb);
+	return ret;
+}
+
 static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
 {
 	struct strbuf *ret;
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 29/34] mailinfo: move cleanup_space() before its users
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (27 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 28/34] mailinfo: move check_header() after the helpers it uses Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 30/34] mailinfo: move definition of MAX_HDR_PARSED closer to its use Junio C Hamano
                       ` (6 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 25 +++++++++++--------------
 1 file changed, 11 insertions(+), 14 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 6fc6aa8..104d3d9 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -41,8 +41,17 @@ struct mailinfo {
 
 #define MAX_HDR_PARSED 10
 
-static void cleanup_space(struct strbuf *sb);
-
+static void cleanup_space(struct strbuf *sb)
+{
+	size_t pos, cnt;
+	for (pos = 0; pos < sb->len; pos++) {
+		if (isspace(sb->buf[pos])) {
+			sb->buf[pos] = ' ';
+			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+			strbuf_remove(sb, pos + 1, cnt);
+		}
+	}
+}
 
 static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
 {
@@ -281,18 +290,6 @@ static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
 	strbuf_trim(subject);
 }
 
-static void cleanup_space(struct strbuf *sb)
-{
-	size_t pos, cnt;
-	for (pos = 0; pos < sb->len; pos++) {
-		if (isspace(sb->buf[pos])) {
-			sb->buf[pos] = ' ';
-			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
-			strbuf_remove(sb, pos + 1, cnt);
-		}
-	}
-}
-
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 30/34] mailinfo: move definition of MAX_HDR_PARSED closer to its use
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (28 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 29/34] mailinfo: move cleanup_space() before its users Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 31/34] mailinfo: libify Junio C Hamano
                       ` (5 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/mailinfo.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 104d3d9..4eabc11 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -39,8 +39,6 @@ struct mailinfo {
 	struct strbuf log_message;
 };
 
-#define MAX_HDR_PARSED 10
-
 static void cleanup_space(struct strbuf *sb)
 {
 	size_t pos, cnt;
@@ -290,6 +288,7 @@ static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
 	strbuf_trim(subject);
 }
 
+#define MAX_HDR_PARSED 10
 static const char *header[MAX_HDR_PARSED] = {
 	"From","Subject","Date",
 };
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 31/34] mailinfo: libify
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (29 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 30/34] mailinfo: move definition of MAX_HDR_PARSED closer to its use Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 32/34] mailinfo: handle charset conversion errors in the caller Junio C Hamano
                       ` (4 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Move the bulk of the code from builtin/mailinfo.c to mailinfo.c
so that new callers can start calling mailinfo() directly.

Note that a few calls to exit() and die() need to be cleaned up
for the API to be truly useful, which will come in later steps.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Makefile                         |    1 +
 builtin/mailinfo.c               | 1167 ++------------------------------------
 builtin/mailinfo.c => mailinfo.c |   96 +---
 mailinfo.h                       |   40 ++
 4 files changed, 106 insertions(+), 1198 deletions(-)
 rewrite builtin/mailinfo.c (93%)
 copy builtin/mailinfo.c => mailinfo.c (90%)
 create mode 100644 mailinfo.h

diff --git a/Makefile b/Makefile
index 8d5df7e..7dd3bff 100644
--- a/Makefile
+++ b/Makefile
@@ -726,6 +726,7 @@ LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge.o
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
dissimilarity index 93%
index 4eabc11..f6df274 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -1,1106 +1,61 @@
-/*
- * Another stupid program, this one parsing the headers of an
- * email to figure out authorship and subject
- */
-#include "cache.h"
-#include "builtin.h"
-#include "utf8.h"
-#include "strbuf.h"
-
-#define MAX_BOUNDARIES 5
-
-struct mailinfo {
-	FILE *input;
-	FILE *output;
-	FILE *patchfile;
-
-	struct strbuf name;
-	struct strbuf email;
-	int keep_subject;
-	int keep_non_patch_brackets_in_subject;
-	int add_message_id;
-	int use_scissors;
-	int use_inbody_headers; /* defaults to 1 */
-	const char *metainfo_charset;
-
-	struct strbuf *content[MAX_BOUNDARIES];
-	struct strbuf **content_top;
-	struct strbuf charset;
-	char *message_id;
-	enum  {
-		TE_DONTCARE, TE_QP, TE_BASE64
-	} transfer_encoding;
-	int patch_lines;
-	int filter_stage; /* still reading log or are we copying patch? */
-	int header_stage; /* still checking in-body headers? */
-	struct strbuf **p_hdr_data;
-	struct strbuf **s_hdr_data;
-
-	struct strbuf log_message;
-};
-
-static void cleanup_space(struct strbuf *sb)
-{
-	size_t pos, cnt;
-	for (pos = 0; pos < sb->len; pos++) {
-		if (isspace(sb->buf[pos])) {
-			sb->buf[pos] = ' ';
-			for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
-			strbuf_remove(sb, pos + 1, cnt);
-		}
-	}
-}
-
-static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
-{
-	struct strbuf *src = name;
-	if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
-		strchr(name->buf, '<') || strchr(name->buf, '>'))
-		src = email;
-	else if (name == out)
-		return;
-	strbuf_reset(out);
-	strbuf_addbuf(out, src);
-}
-
-static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
-{
-	/* John Doe <johndoe> */
-
-	char *bra, *ket;
-	/* This is fallback, so do not bother if we already have an
-	 * e-mail address.
-	 */
-	if (mi->email.len)
-		return;
-
-	bra = strchr(line->buf, '<');
-	if (!bra)
-		return;
-	ket = strchr(bra, '>');
-	if (!ket)
-		return;
-
-	strbuf_reset(&mi->email);
-	strbuf_add(&mi->email, bra + 1, ket - bra - 1);
-
-	strbuf_reset(&mi->name);
-	strbuf_add(&mi->name, line->buf, bra - line->buf);
-	strbuf_trim(&mi->name);
-	get_sane_name(&mi->name, &mi->name, &mi->email);
-}
-
-static void handle_from(struct mailinfo *mi, const struct strbuf *from)
-{
-	char *at;
-	size_t el;
-	struct strbuf f;
-
-	strbuf_init(&f, from->len);
-	strbuf_addbuf(&f, from);
-
-	at = strchr(f.buf, '@');
-	if (!at) {
-		parse_bogus_from(mi, from);
-		return;
-	}
-
-	/*
-	 * If we already have one email, don't take any confusing lines
-	 */
-	if (mi->email.len && strchr(at + 1, '@')) {
-		strbuf_release(&f);
-		return;
-	}
-
-	/* Pick up the string around '@', possibly delimited with <>
-	 * pair; that is the email part.
-	 */
-	while (at > f.buf) {
-		char c = at[-1];
-		if (isspace(c))
-			break;
-		if (c == '<') {
-			at[-1] = ' ';
-			break;
-		}
-		at--;
-	}
-	el = strcspn(at, " \n\t\r\v\f>");
-	strbuf_reset(&mi->email);
-	strbuf_add(&mi->email, at, el);
-	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
-
-	/* The remainder is name.  It could be
-	 *
-	 * - "John Doe <john.doe@xz>"			(a), or
-	 * - "john.doe@xz (John Doe)"			(b), or
-	 * - "John (zzz) Doe <john.doe@xz> (Comment)"	(c)
-	 *
-	 * but we have removed the email part, so
-	 *
-	 * - remove extra spaces which could stay after email (case 'c'), and
-	 * - trim from both ends, possibly removing the () pair at the end
-	 *   (cases 'a' and 'b').
-	 */
-	cleanup_space(&f);
-	strbuf_trim(&f);
-	if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
-		strbuf_remove(&f, 0, 1);
-		strbuf_setlen(&f, f.len - 1);
-	}
-
-	get_sane_name(&mi->name, &f, &mi->email);
-	strbuf_release(&f);
-}
-
-static void handle_header(struct strbuf **out, const struct strbuf *line)
-{
-	if (!*out) {
-		*out = xmalloc(sizeof(struct strbuf));
-		strbuf_init(*out, line->len);
-	} else
-		strbuf_reset(*out);
-
-	strbuf_addbuf(*out, line);
-}
-
-/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
- * to have enough heuristics to grok MIME encoded patches often found
- * on our mailing lists.  For example, we do not even treat header lines
- * case insensitively.
- */
-
-static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
-{
-	const char *ends, *ap = strcasestr(line, name);
-	size_t sz;
-
-	strbuf_setlen(attr, 0);
-	if (!ap)
-		return 0;
-	ap += strlen(name);
-	if (*ap == '"') {
-		ap++;
-		ends = "\"";
-	}
-	else
-		ends = "; \t";
-	sz = strcspn(ap, ends);
-	strbuf_add(attr, ap, sz);
-	return 1;
-}
-
-static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
-	strbuf_init(boundary, line->len);
-
-	if (slurp_attr(line->buf, "boundary=", boundary)) {
-		strbuf_insert(boundary, 0, "--", 2);
-		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
-			fprintf(stderr, "Too many boundaries to handle\n");
-			exit(1);
-		}
-		*(mi->content_top) = boundary;
-		boundary = NULL;
-	}
-	slurp_attr(line->buf, "charset=", &mi->charset);
-
-	if (boundary) {
-		strbuf_release(boundary);
-		free(boundary);
-	}
-}
-
-static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
-{
-	if (mi->add_message_id)
-		mi->message_id = strdup(line->buf);
-}
-
-static void handle_content_transfer_encoding(struct mailinfo *mi,
-					     const struct strbuf *line)
-{
-	if (strcasestr(line->buf, "base64"))
-		mi->transfer_encoding = TE_BASE64;
-	else if (strcasestr(line->buf, "quoted-printable"))
-		mi->transfer_encoding = TE_QP;
-	else
-		mi->transfer_encoding = TE_DONTCARE;
-}
-
-static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
-{
-	struct strbuf *content_top = *(mi->content_top);
-
-	return ((content_top->len <= line->len) &&
-		!memcmp(line->buf, content_top->buf, content_top->len));
-}
-
-static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
-{
-	size_t at = 0;
-
-	while (at < subject->len) {
-		char *pos;
-		size_t remove;
-
-		switch (subject->buf[at]) {
-		case 'r': case 'R':
-			if (subject->len <= at + 3)
-				break;
-			if ((subject->buf[at + 1] == 'e' ||
-			     subject->buf[at + 1] == 'E') &&
-			    subject->buf[at + 2] == ':') {
-				strbuf_remove(subject, at, 3);
-				continue;
-			}
-			at++;
-			break;
-		case ' ': case '\t': case ':':
-			strbuf_remove(subject, at, 1);
-			continue;
-		case '[':
-			pos = strchr(subject->buf + at, ']');
-			if (!pos)
-				break;
-			remove = pos - subject->buf + at + 1;
-			if (!mi->keep_non_patch_brackets_in_subject ||
-			    (7 <= remove &&
-			     memmem(subject->buf + at, remove, "PATCH", 5)))
-				strbuf_remove(subject, at, remove);
-			else {
-				at += remove;
-				/*
-				 * If the input had a space after the ], keep
-				 * it.  We don't bother with finding the end of
-				 * the space, since we later normalize it
-				 * anyway.
-				 */
-				if (isspace(subject->buf[at]))
-					at += 1;
-			}
-			continue;
-		}
-		break;
-	}
-	strbuf_trim(subject);
-}
-
-#define MAX_HDR_PARSED 10
-static const char *header[MAX_HDR_PARSED] = {
-	"From","Subject","Date",
-};
-
-static inline int cmp_header(const struct strbuf *line, const char *hdr)
-{
-	int len = strlen(hdr);
-	return !strncasecmp(line->buf, hdr, len) && line->len > len &&
-			line->buf[len] == ':' && isspace(line->buf[len + 1]);
-}
-
-static int is_format_patch_separator(const char *line, int len)
-{
-	static const char SAMPLE[] =
-		"From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
-	const char *cp;
-
-	if (len != strlen(SAMPLE))
-		return 0;
-	if (!skip_prefix(line, "From ", &cp))
-		return 0;
-	if (strspn(cp, "0123456789abcdef") != 40)
-		return 0;
-	cp += 40;
-	return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
-}
-
-static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
-{
-	const char *in = q_seg->buf;
-	int c;
-	struct strbuf *out = xmalloc(sizeof(struct strbuf));
-	strbuf_init(out, q_seg->len);
-
-	while ((c = *in++) != 0) {
-		if (c == '=') {
-			int d = *in++;
-			if (d == '\n' || !d)
-				break; /* drop trailing newline */
-			strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
-			continue;
-		}
-		if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
-			c = 0x20;
-		strbuf_addch(out, c);
-	}
-	return out;
-}
-
-static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
-{
-	/* Decode in..ep, possibly in-place to ot */
-	int c, pos = 0, acc = 0;
-	const char *in = b_seg->buf;
-	struct strbuf *out = xmalloc(sizeof(struct strbuf));
-	strbuf_init(out, b_seg->len);
-
-	while ((c = *in++) != 0) {
-		if (c == '+')
-			c = 62;
-		else if (c == '/')
-			c = 63;
-		else if ('A' <= c && c <= 'Z')
-			c -= 'A';
-		else if ('a' <= c && c <= 'z')
-			c -= 'a' - 26;
-		else if ('0' <= c && c <= '9')
-			c -= '0' - 52;
-		else
-			continue; /* garbage */
-		switch (pos++) {
-		case 0:
-			acc = (c << 2);
-			break;
-		case 1:
-			strbuf_addch(out, (acc | (c >> 4)));
-			acc = (c & 15) << 4;
-			break;
-		case 2:
-			strbuf_addch(out, (acc | (c >> 2)));
-			acc = (c & 3) << 6;
-			break;
-		case 3:
-			strbuf_addch(out, (acc | c));
-			acc = pos = 0;
-			break;
-		}
-	}
-	return out;
-}
-
-static void convert_to_utf8(struct mailinfo *mi,
-			    struct strbuf *line, const char *charset)
-{
-	char *out;
-
-	if (!mi->metainfo_charset || !charset || !*charset)
-		return;
-
-	if (same_encoding(mi->metainfo_charset, charset))
-		return;
-	out = reencode_string(line->buf, mi->metainfo_charset, charset);
-	if (!out)
-		die("cannot convert from %s to %s",
-		    charset, mi->metainfo_charset);
-	strbuf_attach(line, out, strlen(out), strlen(out));
-}
-
-static void decode_header(struct mailinfo *mi, struct strbuf *it)
-{
-	char *in, *ep, *cp;
-	struct strbuf outbuf = STRBUF_INIT, *dec;
-	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
-
-	in = it->buf;
-	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
-		int encoding;
-		strbuf_reset(&charset_q);
-		strbuf_reset(&piecebuf);
-
-		if (in != ep) {
-			/*
-			 * We are about to process an encoded-word
-			 * that begins at ep, but there is something
-			 * before the encoded word.
-			 */
-			char *scan;
-			for (scan = in; scan < ep; scan++)
-				if (!isspace(*scan))
-					break;
-
-			if (scan != ep || in == it->buf) {
-				/*
-				 * We should not lose that "something",
-				 * unless we have just processed an
-				 * encoded-word, and there is only LWS
-				 * before the one we are about to process.
-				 */
-				strbuf_add(&outbuf, in, ep - in);
-			}
-		}
-		/* E.g.
-		 * ep : "=?iso-2022-jp?B?GyR...?= foo"
-		 * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
-		 */
-		ep += 2;
-
-		if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
-			goto release_return;
-
-		if (cp + 3 - it->buf > it->len)
-			goto release_return;
-		strbuf_add(&charset_q, ep, cp - ep);
-
-		encoding = cp[1];
-		if (!encoding || cp[2] != '?')
-			goto release_return;
-		ep = strstr(cp + 3, "?=");
-		if (!ep)
-			goto release_return;
-		strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
-		switch (tolower(encoding)) {
-		default:
-			goto release_return;
-		case 'b':
-			dec = decode_b_segment(&piecebuf);
-			break;
-		case 'q':
-			dec = decode_q_segment(&piecebuf, 1);
-			break;
-		}
-		convert_to_utf8(mi, dec, charset_q.buf);
-
-		strbuf_addbuf(&outbuf, dec);
-		strbuf_release(dec);
-		free(dec);
-		in = ep + 2;
-	}
-	strbuf_addstr(&outbuf, in);
-	strbuf_reset(it);
-	strbuf_addbuf(it, &outbuf);
-release_return:
-	strbuf_release(&outbuf);
-	strbuf_release(&charset_q);
-	strbuf_release(&piecebuf);
-}
-
-static int check_header(struct mailinfo *mi,
-			const struct strbuf *line,
-			struct strbuf *hdr_data[], int overwrite)
-{
-	int i, ret = 0, len;
-	struct strbuf sb = STRBUF_INIT;
-
-	/* search for the interesting parts */
-	for (i = 0; header[i]; i++) {
-		int len = strlen(header[i]);
-		if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
-			/* Unwrap inline B and Q encoding, and optionally
-			 * normalize the meta information to utf8.
-			 */
-			strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-			decode_header(mi, &sb);
-			handle_header(&hdr_data[i], &sb);
-			ret = 1;
-			goto check_header_out;
-		}
-	}
-
-	/* Content stuff */
-	if (cmp_header(line, "Content-Type")) {
-		len = strlen("Content-Type: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		strbuf_insert(&sb, 0, "Content-Type: ", len);
-		handle_content_type(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Content-Transfer-Encoding")) {
-		len = strlen("Content-Transfer-Encoding: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_content_transfer_encoding(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-	if (cmp_header(line, "Message-Id")) {
-		len = strlen("Message-Id: ");
-		strbuf_add(&sb, line->buf + len, line->len - len);
-		decode_header(mi, &sb);
-		handle_message_id(mi, &sb);
-		ret = 1;
-		goto check_header_out;
-	}
-
-	/* for inbody stuff */
-	if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
-		ret = is_format_patch_separator(line->buf + 1, line->len - 1);
-		goto check_header_out;
-	}
-	if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-		for (i = 0; header[i]; i++) {
-			if (!strcmp("Subject", header[i])) {
-				handle_header(&hdr_data[i], line);
-				ret = 1;
-				goto check_header_out;
-			}
-		}
-	}
-
-check_header_out:
-	strbuf_release(&sb);
-	return ret;
-}
-
-static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf *ret;
-
-	switch (mi->transfer_encoding) {
-	case TE_QP:
-		ret = decode_q_segment(line, 0);
-		break;
-	case TE_BASE64:
-		ret = decode_b_segment(line);
-		break;
-	case TE_DONTCARE:
-	default:
-		return;
-	}
-	strbuf_reset(line);
-	strbuf_addbuf(line, ret);
-	strbuf_release(ret);
-	free(ret);
-}
-
-static inline int patchbreak(const struct strbuf *line)
-{
-	size_t i;
-
-	/* Beginning of a "diff -" header? */
-	if (starts_with(line->buf, "diff -"))
-		return 1;
-
-	/* CVS "Index: " line? */
-	if (starts_with(line->buf, "Index: "))
-		return 1;
-
-	/*
-	 * "--- <filename>" starts patches without headers
-	 * "---<sp>*" is a manual separator
-	 */
-	if (line->len < 4)
-		return 0;
-
-	if (starts_with(line->buf, "---")) {
-		/* space followed by a filename? */
-		if (line->buf[3] == ' ' && !isspace(line->buf[4]))
-			return 1;
-		/* Just whitespace? */
-		for (i = 3; i < line->len; i++) {
-			unsigned char c = line->buf[i];
-			if (c == '\n')
-				return 1;
-			if (!isspace(c))
-				break;
-		}
-		return 0;
-	}
-	return 0;
-}
-
-static int is_scissors_line(const struct strbuf *line)
-{
-	size_t i, len = line->len;
-	int scissors = 0, gap = 0;
-	int first_nonblank = -1;
-	int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
-	const char *buf = line->buf;
-
-	for (i = 0; i < len; i++) {
-		if (isspace(buf[i])) {
-			if (in_perforation) {
-				perforation++;
-				gap++;
-			}
-			continue;
-		}
-		last_nonblank = i;
-		if (first_nonblank < 0)
-			first_nonblank = i;
-		if (buf[i] == '-') {
-			in_perforation = 1;
-			perforation++;
-			continue;
-		}
-		if (i + 1 < len &&
-		    (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
-		     !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
-			in_perforation = 1;
-			perforation += 2;
-			scissors += 2;
-			i++;
-			continue;
-		}
-		in_perforation = 0;
-	}
-
-	/*
-	 * The mark must be at least 8 bytes long (e.g. "-- >8 --").
-	 * Even though there can be arbitrary cruft on the same line
-	 * (e.g. "cut here"), in order to avoid misidentification, the
-	 * perforation must occupy more than a third of the visible
-	 * width of the line, and dashes and scissors must occupy more
-	 * than half of the perforation.
-	 */
-
-	visible = last_nonblank - first_nonblank + 1;
-	return (scissors && 8 <= visible &&
-		visible < perforation * 3 &&
-		gap * 2 < perforation);
-}
-
-static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
-{
-	assert(!mi->filter_stage);
-
-	if (mi->header_stage) {
-		if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
-			return 0;
-	}
-
-	if (mi->use_inbody_headers && mi->header_stage) {
-		mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);
-		if (mi->header_stage)
-			return 0;
-	} else
-		/* Only trim the first (blank) line of the commit message
-		 * when ignoring in-body headers.
-		 */
-		mi->header_stage = 0;
-
-	/* normalize the log message to UTF-8. */
-	convert_to_utf8(mi, line, mi->charset.buf);
-
-	if (mi->use_scissors && is_scissors_line(line)) {
-		int i;
-
-		strbuf_setlen(&mi->log_message, 0);
-		mi->header_stage = 1;
-
-		/*
-		 * We may have already read "secondary headers"; purge
-		 * them to give ourselves a clean restart.
-		 */
-		for (i = 0; header[i]; i++) {
-			if (mi->s_hdr_data[i])
-				strbuf_release(mi->s_hdr_data[i]);
-			mi->s_hdr_data[i] = NULL;
-		}
-		return 0;
-	}
-
-	if (patchbreak(line)) {
-		if (mi->message_id)
-			strbuf_addf(&mi->log_message,
-				    "Message-Id: %s\n", mi->message_id);
-		return 1;
-	}
-
-	strbuf_addbuf(&mi->log_message, line);
-	return 0;
-}
-
-static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
-{
-	fwrite(line->buf, 1, line->len, mi->patchfile);
-	mi->patch_lines++;
-}
-
-static void handle_filter(struct mailinfo *mi, struct strbuf *line)
-{
-	switch (mi->filter_stage) {
-	case 0:
-		if (!handle_commit_msg(mi, line))
-			break;
-		mi->filter_stage++;
-	case 1:
-		handle_patch(mi, line);
-		break;
-	}
-}
-
-static int is_rfc2822_header(const struct strbuf *line)
-{
-	/*
-	 * The section that defines the loosest possible
-	 * field name is "3.6.8 Optional fields".
-	 *
-	 * optional-field = field-name ":" unstructured CRLF
-	 * field-name = 1*ftext
-	 * ftext = %d33-57 / %59-126
-	 */
-	int ch;
-	char *cp = line->buf;
-
-	/* Count mbox From headers as headers */
-	if (starts_with(cp, "From ") || starts_with(cp, ">From "))
-		return 1;
-
-	while ((ch = *cp++)) {
-		if (ch == ':')
-			return 1;
-		if ((33 <= ch && ch <= 57) ||
-		    (59 <= ch && ch <= 126))
-			continue;
-		break;
-	}
-	return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
-	/* Get the first part of the line. */
-	if (strbuf_getline(line, in, '\n'))
-		return 0;
-
-	/*
-	 * Is it an empty line or not a valid rfc2822 header?
-	 * If so, stop here, and return false ("not a header")
-	 */
-	strbuf_rtrim(line);
-	if (!line->len || !is_rfc2822_header(line)) {
-		/* Re-add the newline */
-		strbuf_addch(line, '\n');
-		return 0;
-	}
-
-	/*
-	 * Now we need to eat all the continuation lines..
-	 * Yuck, 2822 header "folding"
-	 */
-	for (;;) {
-		int peek;
-		struct strbuf continuation = STRBUF_INIT;
-
-		peek = fgetc(in); ungetc(peek, in);
-		if (peek != ' ' && peek != '\t')
-			break;
-		if (strbuf_getline(&continuation, in, '\n'))
-			break;
-		continuation.buf[0] = ' ';
-		strbuf_rtrim(&continuation);
-		strbuf_addbuf(line, &continuation);
-	}
-
-	return 1;
-}
-
-static int find_boundary(struct mailinfo *mi, struct strbuf *line)
-{
-	while (!strbuf_getline(line, mi->input, '\n')) {
-		if (*(mi->content_top) && is_multipart_boundary(mi, line))
-			return 1;
-	}
-	return 0;
-}
-
-static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf newline = STRBUF_INIT;
-
-	strbuf_addch(&newline, '\n');
-again:
-	if (line->len >= (*(mi->content_top))->len + 2 &&
-	    !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
-		/* we hit an end boundary */
-		/* pop the current boundary off the stack */
-		strbuf_release(*(mi->content_top));
-		free(*(mi->content_top));
-		*(mi->content_top) = NULL;
-
-		/* technically won't happen as is_multipart_boundary()
-		   will fail first.  But just in case..
-		 */
-		if (--mi->content_top < mi->content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
-		}
-		handle_filter(mi, &newline);
-		strbuf_release(&newline);
-
-		/* skip to the next boundary */
-		if (!find_boundary(mi, line))
-			return 0;
-		goto again;
-	}
-
-	/* set some defaults */
-	mi->transfer_encoding = TE_DONTCARE;
-	strbuf_reset(&mi->charset);
-
-	/* slurp in this section's info */
-	while (read_one_header_line(line, mi->input))
-		check_header(mi, line, mi->p_hdr_data, 0);
-
-	strbuf_release(&newline);
-	/* replenish line */
-	if (strbuf_getline(line, mi->input, '\n'))
-		return 0;
-	strbuf_addch(line, '\n');
-	return 1;
-}
-
-static void handle_body(struct mailinfo *mi, struct strbuf *line)
-{
-	struct strbuf prev = STRBUF_INIT;
-
-	/* Skip up to the first boundary */
-	if (*(mi->content_top)) {
-		if (!find_boundary(mi, line))
-			goto handle_body_out;
-	}
-
-	do {
-		/* process any boundary lines */
-		if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
-			/* flush any leftover */
-			if (prev.len) {
-				handle_filter(mi, &prev);
-				strbuf_reset(&prev);
-			}
-			if (!handle_boundary(mi, line))
-				goto handle_body_out;
-		}
-
-		/* Unwrap transfer encoding */
-		decode_transfer_encoding(mi, line);
-
-		switch (mi->transfer_encoding) {
-		case TE_BASE64:
-		case TE_QP:
-		{
-			struct strbuf **lines, **it, *sb;
-
-			/* Prepend any previous partial lines */
-			strbuf_insert(line, 0, prev.buf, prev.len);
-			strbuf_reset(&prev);
-
-			/*
-			 * This is a decoded line that may contain
-			 * multiple new lines.  Pass only one chunk
-			 * at a time to handle_filter()
-			 */
-			lines = strbuf_split(line, '\n');
-			for (it = lines; (sb = *it); it++) {
-				if (*(it + 1) == NULL) /* The last line */
-					if (sb->buf[sb->len - 1] != '\n') {
-						/* Partial line, save it for later. */
-						strbuf_addbuf(&prev, sb);
-						break;
-					}
-				handle_filter(mi, sb);
-			}
-			/*
-			 * The partial chunk is saved in "prev" and will be
-			 * appended by the next iteration of read_line_with_nul().
-			 */
-			strbuf_list_free(lines);
-			break;
-		}
-		default:
-			handle_filter(mi, line);
-		}
-
-	} while (!strbuf_getwholeline(line, mi->input, '\n'));
-
-handle_body_out:
-	strbuf_release(&prev);
-}
-
-static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
-{
-	const char *sp = data->buf;
-	while (1) {
-		char *ep = strchr(sp, '\n');
-		int len;
-		if (!ep)
-			len = strlen(sp);
-		else
-			len = ep - sp;
-		fprintf(fout, "%s: %.*s\n", hdr, len, sp);
-		if (!ep)
-			break;
-		sp = ep + 1;
-	}
-}
-
-static void handle_info(struct mailinfo *mi)
-{
-	struct strbuf *hdr;
-	int i;
-
-	for (i = 0; header[i]; i++) {
-		/* only print inbody headers if we output a patch file */
-		if (mi->patch_lines && mi->s_hdr_data[i])
-			hdr = mi->s_hdr_data[i];
-		else if (mi->p_hdr_data[i])
-			hdr = mi->p_hdr_data[i];
-		else
-			continue;
-
-		if (!strcmp(header[i], "Subject")) {
-			if (!mi->keep_subject) {
-				cleanup_subject(mi, hdr);
-				cleanup_space(hdr);
-			}
-			output_header_lines(mi->output, "Subject", hdr);
-		} else if (!strcmp(header[i], "From")) {
-			cleanup_space(hdr);
-			handle_from(mi, hdr);
-			fprintf(mi->output, "Author: %s\n", mi->name.buf);
-			fprintf(mi->output, "Email: %s\n", mi->email.buf);
-		} else {
-			cleanup_space(hdr);
-			fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
-		}
-	}
-	fprintf(mi->output, "\n");
-}
-
-static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
-{
-	FILE *cmitmsg;
-	int peek;
-	struct strbuf line = STRBUF_INIT;
-
-	cmitmsg = fopen(msg, "w");
-	if (!cmitmsg) {
-		perror(msg);
-		return -1;
-	}
-	mi->patchfile = fopen(patch, "w");
-	if (!mi->patchfile) {
-		perror(patch);
-		fclose(cmitmsg);
-		return -1;
-	}
-
-	mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
-	mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
-
-	do {
-		peek = fgetc(mi->input);
-	} while (isspace(peek));
-	ungetc(peek, mi->input);
-
-	/* process the email header */
-	while (read_one_header_line(&line, mi->input))
-		check_header(mi, &line, mi->p_hdr_data, 1);
-
-	handle_body(mi, &line);
-	fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
-	fclose(cmitmsg);
-	fclose(mi->patchfile);
-
-	handle_info(mi);
-
-	return 0;
-}
-
-static int git_mailinfo_config(const char *var, const char *value, void *mi_)
-{
-	struct mailinfo *mi = mi_;
-
-	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, NULL);
-	if (!strcmp(var, "mailinfo.scissors")) {
-		mi->use_scissors = git_config_bool(var, value);
-		return 0;
-	}
-	/* perhaps others here */
-	return 0;
-}
-
-static void setup_mailinfo(struct mailinfo *mi)
-{
-	memset(mi, 0, sizeof(*mi));
-	strbuf_init(&mi->name, 0);
-	strbuf_init(&mi->email, 0);
-	strbuf_init(&mi->charset, 0);
-	strbuf_init(&mi->log_message, 0);
-	mi->header_stage = 1;
-	mi->use_inbody_headers = 1;
-	mi->content_top = mi->content;
-	git_config(git_mailinfo_config, &mi);
-}
-
-static void clear_mailinfo(struct mailinfo *mi)
-{
-	int i;
-
-	strbuf_release(&mi->name);
-	strbuf_release(&mi->email);
-	strbuf_release(&mi->charset);
-	free(mi->message_id);
-
-	for (i = 0; mi->p_hdr_data[i]; i++)
-		strbuf_release(mi->p_hdr_data[i]);
-	free(mi->p_hdr_data);
-	for (i = 0; mi->s_hdr_data[i]; i++)
-		strbuf_release(mi->s_hdr_data[i]);
-	free(mi->s_hdr_data);
-
-	while (mi->content < mi->content_top) {
-		free(*(mi->content_top));
-		mi->content_top--;
-	}
-
-	strbuf_release(&mi->log_message);
-}
-
-static const char mailinfo_usage[] =
-	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
-
-int cmd_mailinfo(int argc, const char **argv, const char *prefix)
-{
-	const char *def_charset;
-	struct mailinfo mi;
-	int status;
-
-	/* NEEDSWORK: might want to do the optional .git/ directory
-	 * discovery
-	 */
-	setup_mailinfo(&mi);
-
-	def_charset = get_commit_output_encoding();
-	mi.metainfo_charset = def_charset;
-
-	while (1 < argc && argv[1][0] == '-') {
-		if (!strcmp(argv[1], "-k"))
-			mi.keep_subject = 1;
-		else if (!strcmp(argv[1], "-b"))
-			mi.keep_non_patch_brackets_in_subject = 1;
-		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
-			mi.add_message_id = 1;
-		else if (!strcmp(argv[1], "-u"))
-			mi.metainfo_charset = def_charset;
-		else if (!strcmp(argv[1], "-n"))
-			mi.metainfo_charset = NULL;
-		else if (starts_with(argv[1], "--encoding="))
-			mi.metainfo_charset = argv[1] + 11;
-		else if (!strcmp(argv[1], "--scissors"))
-			mi.use_scissors = 1;
-		else if (!strcmp(argv[1], "--no-scissors"))
-			mi.use_scissors = 0;
-		else if (!strcmp(argv[1], "--no-inbody-headers"))
-			mi.use_inbody_headers = 0;
-		else
-			usage(mailinfo_usage);
-		argc--; argv++;
-	}
-
-	if (argc != 3)
-		usage(mailinfo_usage);
-
-	mi.input = stdin;
-	mi.output = stdout;
-	status = !!mailinfo(&mi, argv[1], argv[2]);
-	clear_mailinfo(&mi);
-
-	return status;
-}
+/*
+ * Another stupid program, this one parsing the headers of an
+ * email to figure out authorship and subject
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "mailinfo.h"
+
+static const char mailinfo_usage[] =
+	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
+
+int cmd_mailinfo(int argc, const char **argv, const char *prefix)
+{
+	const char *def_charset;
+	struct mailinfo mi;
+	int status;
+
+	/* NEEDSWORK: might want to do the optional .git/ directory
+	 * discovery
+	 */
+	setup_mailinfo(&mi);
+
+	def_charset = get_commit_output_encoding();
+	mi.metainfo_charset = def_charset;
+
+	while (1 < argc && argv[1][0] == '-') {
+		if (!strcmp(argv[1], "-k"))
+			mi.keep_subject = 1;
+		else if (!strcmp(argv[1], "-b"))
+			mi.keep_non_patch_brackets_in_subject = 1;
+		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
+			mi.add_message_id = 1;
+		else if (!strcmp(argv[1], "-u"))
+			mi.metainfo_charset = def_charset;
+		else if (!strcmp(argv[1], "-n"))
+			mi.metainfo_charset = NULL;
+		else if (starts_with(argv[1], "--encoding="))
+			mi.metainfo_charset = argv[1] + 11;
+		else if (!strcmp(argv[1], "--scissors"))
+			mi.use_scissors = 1;
+		else if (!strcmp(argv[1], "--no-scissors"))
+			mi.use_scissors = 0;
+		else if (!strcmp(argv[1], "--no-inbody-headers"))
+			mi.use_inbody_headers = 0;
+		else
+			usage(mailinfo_usage);
+		argc--; argv++;
+	}
+
+	if (argc != 3)
+		usage(mailinfo_usage);
+
+	mi.input = stdin;
+	mi.output = stdout;
+	status = !!mailinfo(&mi, argv[1], argv[2]);
+	clear_mailinfo(&mi);
+
+	return status;
+}
diff --git a/builtin/mailinfo.c b/mailinfo.c
similarity index 90%
copy from builtin/mailinfo.c
copy to mailinfo.c
index 4eabc11..df6cfef 100644
--- a/builtin/mailinfo.c
+++ b/mailinfo.c
@@ -1,43 +1,7 @@
-/*
- * Another stupid program, this one parsing the headers of an
- * email to figure out authorship and subject
- */
 #include "cache.h"
-#include "builtin.h"
 #include "utf8.h"
 #include "strbuf.h"
-
-#define MAX_BOUNDARIES 5
-
-struct mailinfo {
-	FILE *input;
-	FILE *output;
-	FILE *patchfile;
-
-	struct strbuf name;
-	struct strbuf email;
-	int keep_subject;
-	int keep_non_patch_brackets_in_subject;
-	int add_message_id;
-	int use_scissors;
-	int use_inbody_headers; /* defaults to 1 */
-	const char *metainfo_charset;
-
-	struct strbuf *content[MAX_BOUNDARIES];
-	struct strbuf **content_top;
-	struct strbuf charset;
-	char *message_id;
-	enum  {
-		TE_DONTCARE, TE_QP, TE_BASE64
-	} transfer_encoding;
-	int patch_lines;
-	int filter_stage; /* still reading log or are we copying patch? */
-	int header_stage; /* still checking in-body headers? */
-	struct strbuf **p_hdr_data;
-	struct strbuf **s_hdr_data;
-
-	struct strbuf log_message;
-};
+#include "mailinfo.h"
 
 static void cleanup_space(struct strbuf *sb)
 {
@@ -962,7 +926,7 @@ static void handle_info(struct mailinfo *mi)
 	fprintf(mi->output, "\n");
 }
 
-static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
+int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 {
 	FILE *cmitmsg;
 	int peek;
@@ -1016,7 +980,7 @@ static int git_mailinfo_config(const char *var, const char *value, void *mi_)
 	return 0;
 }
 
-static void setup_mailinfo(struct mailinfo *mi)
+void setup_mailinfo(struct mailinfo *mi)
 {
 	memset(mi, 0, sizeof(*mi));
 	strbuf_init(&mi->name, 0);
@@ -1029,7 +993,7 @@ static void setup_mailinfo(struct mailinfo *mi)
 	git_config(git_mailinfo_config, &mi);
 }
 
-static void clear_mailinfo(struct mailinfo *mi)
+void clear_mailinfo(struct mailinfo *mi)
 {
 	int i;
 
@@ -1052,55 +1016,3 @@ static void clear_mailinfo(struct mailinfo *mi)
 
 	strbuf_release(&mi->log_message);
 }
-
-static const char mailinfo_usage[] =
-	"git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
-
-int cmd_mailinfo(int argc, const char **argv, const char *prefix)
-{
-	const char *def_charset;
-	struct mailinfo mi;
-	int status;
-
-	/* NEEDSWORK: might want to do the optional .git/ directory
-	 * discovery
-	 */
-	setup_mailinfo(&mi);
-
-	def_charset = get_commit_output_encoding();
-	mi.metainfo_charset = def_charset;
-
-	while (1 < argc && argv[1][0] == '-') {
-		if (!strcmp(argv[1], "-k"))
-			mi.keep_subject = 1;
-		else if (!strcmp(argv[1], "-b"))
-			mi.keep_non_patch_brackets_in_subject = 1;
-		else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
-			mi.add_message_id = 1;
-		else if (!strcmp(argv[1], "-u"))
-			mi.metainfo_charset = def_charset;
-		else if (!strcmp(argv[1], "-n"))
-			mi.metainfo_charset = NULL;
-		else if (starts_with(argv[1], "--encoding="))
-			mi.metainfo_charset = argv[1] + 11;
-		else if (!strcmp(argv[1], "--scissors"))
-			mi.use_scissors = 1;
-		else if (!strcmp(argv[1], "--no-scissors"))
-			mi.use_scissors = 0;
-		else if (!strcmp(argv[1], "--no-inbody-headers"))
-			mi.use_inbody_headers = 0;
-		else
-			usage(mailinfo_usage);
-		argc--; argv++;
-	}
-
-	if (argc != 3)
-		usage(mailinfo_usage);
-
-	mi.input = stdin;
-	mi.output = stdout;
-	status = !!mailinfo(&mi, argv[1], argv[2]);
-	clear_mailinfo(&mi);
-
-	return status;
-}
diff --git a/mailinfo.h b/mailinfo.h
new file mode 100644
index 0000000..27841be
--- /dev/null
+++ b/mailinfo.h
@@ -0,0 +1,40 @@
+#ifndef MAILINFO_H
+#define MAILINFO_H
+
+#define MAX_BOUNDARIES 5
+
+struct mailinfo {
+	FILE *input;
+	FILE *output;
+	FILE *patchfile;
+
+	struct strbuf name;
+	struct strbuf email;
+	int keep_subject;
+	int keep_non_patch_brackets_in_subject;
+	int add_message_id;
+	int use_scissors;
+	int use_inbody_headers; /* defaults to 1 */
+	const char *metainfo_charset;
+
+	struct strbuf *content[MAX_BOUNDARIES];
+	struct strbuf **content_top;
+	struct strbuf charset;
+	char *message_id;
+	enum  {
+		TE_DONTCARE, TE_QP, TE_BASE64
+	} transfer_encoding;
+	int patch_lines;
+	int filter_stage; /* still reading log or are we copying patch? */
+	int header_stage; /* still checking in-body headers? */
+	struct strbuf **p_hdr_data;
+	struct strbuf **s_hdr_data;
+
+	struct strbuf log_message;
+};
+
+extern void setup_mailinfo(struct mailinfo *);
+extern int mailinfo(struct mailinfo *, const char *msg, const char *patch);
+extern void clear_mailinfo(struct mailinfo *);
+
+#endif /* MAILINFO_H */
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 32/34] mailinfo: handle charset conversion errors in the caller
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (30 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 31/34] mailinfo: libify Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 33/34] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
                       ` (3 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

Instead of dying in convert_to_utf8(), just report an error and let
the callers handle it.  Between the two callers:

 - decode_header() silently punts when it cannot parse a broken
   RFC2047 encoded text (e.g. when it sees anything other than B or
   Q after it sees "=?<charset>") by jumping to release_return,
   returning the string it successfully parsed out so far, to the
   caller.  A piece of string that convert_to_utf8() cannot handle
   can be treated the same way.

 - handle_commit_msg() doesn't cope with a malformed line well, so
   die there for now.  We'll lift this even higher in later changes
   in this series.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 mailinfo.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/mailinfo.c b/mailinfo.c
index df6cfef..4fbf38f 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -344,21 +344,22 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
 	return out;
 }
 
-static void convert_to_utf8(struct mailinfo *mi,
-			    struct strbuf *line, const char *charset)
+static int convert_to_utf8(struct mailinfo *mi,
+			   struct strbuf *line, const char *charset)
 {
 	char *out;
 
 	if (!mi->metainfo_charset || !charset || !*charset)
-		return;
+		return 0;
 
 	if (same_encoding(mi->metainfo_charset, charset))
-		return;
+		return 0;
 	out = reencode_string(line->buf, mi->metainfo_charset, charset);
 	if (!out)
-		die("cannot convert from %s to %s",
-		    charset, mi->metainfo_charset);
+		return error("cannot convert from %s to %s",
+			     charset, mi->metainfo_charset);
 	strbuf_attach(line, out, strlen(out), strlen(out));
+	return 0;
 }
 
 static void decode_header(struct mailinfo *mi, struct strbuf *it)
@@ -424,7 +425,8 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
 			dec = decode_q_segment(&piecebuf, 1);
 			break;
 		}
-		convert_to_utf8(mi, dec, charset_q.buf);
+		if (convert_to_utf8(mi, dec, charset_q.buf))
+			goto release_return;
 
 		strbuf_addbuf(&outbuf, dec);
 		strbuf_release(dec);
@@ -637,7 +639,8 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 		mi->header_stage = 0;
 
 	/* normalize the log message to UTF-8. */
-	convert_to_utf8(mi, line, mi->charset.buf);
+	if (convert_to_utf8(mi, line, mi->charset.buf))
+		exit(128);
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 33/34] mailinfo: remove calls to exit() and die() deep in the callchain
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (31 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 32/34] mailinfo: handle charset conversion errors in the caller Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-19  7:28     ` [PATCH v3 34/34] am: make direct call to mailinfo Junio C Hamano
                       ` (2 subsequent siblings)
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

The top-level mailinfo() would instead punt when the code in the
deeper part of the callchain detects an unrecoverable error in the
input.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 mailinfo.c | 30 ++++++++++++++++++++++--------
 mailinfo.h |  1 +
 2 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/mailinfo.c b/mailinfo.c
index 4fbf38f..9d009fa 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -163,8 +163,10 @@ static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
 	if (slurp_attr(line->buf, "boundary=", boundary)) {
 		strbuf_insert(boundary, 0, "--", 2);
 		if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
-			fprintf(stderr, "Too many boundaries to handle\n");
-			exit(1);
+			error("Too many boundaries to handle");
+			mi->input_error = -1;
+			mi->content_top = &mi->content[MAX_BOUNDARIES] - 1;
+			return;
 		}
 		*(mi->content_top) = boundary;
 		boundary = NULL;
@@ -355,9 +357,11 @@ static int convert_to_utf8(struct mailinfo *mi,
 	if (same_encoding(mi->metainfo_charset, charset))
 		return 0;
 	out = reencode_string(line->buf, mi->metainfo_charset, charset);
-	if (!out)
+	if (!out) {
+		mi->input_error = -1;
 		return error("cannot convert from %s to %s",
 			     charset, mi->metainfo_charset);
+	}
 	strbuf_attach(line, out, strlen(out), strlen(out));
 	return 0;
 }
@@ -367,6 +371,7 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
 	char *in, *ep, *cp;
 	struct strbuf outbuf = STRBUF_INIT, *dec;
 	struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+	int found_error = 1; /* pessimism */
 
 	in = it->buf;
 	while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
@@ -436,10 +441,14 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
 	strbuf_addstr(&outbuf, in);
 	strbuf_reset(it);
 	strbuf_addbuf(it, &outbuf);
+	found_error = 0;
 release_return:
 	strbuf_release(&outbuf);
 	strbuf_release(&charset_q);
 	strbuf_release(&piecebuf);
+
+	if (found_error)
+		mi->input_error = -1;
 }
 
 static int check_header(struct mailinfo *mi,
@@ -640,7 +649,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 
 	/* normalize the log message to UTF-8. */
 	if (convert_to_utf8(mi, line, mi->charset.buf))
-		exit(128);
+		return 0; /* mi->input_error already set */
 
 	if (mi->use_scissors && is_scissors_line(line)) {
 		int i;
@@ -783,12 +792,15 @@ again:
 		   will fail first.  But just in case..
 		 */
 		if (--mi->content_top < mi->content) {
-			fprintf(stderr, "Detected mismatched boundaries, "
-					"can't recover\n");
-			exit(1);
+			error("Detected mismatched boundaries, can't recover");
+			mi->input_error = -1;
+			mi->content_top = mi->content;
+			return 0;
 		}
 		handle_filter(mi, &newline);
 		strbuf_release(&newline);
+		if (mi->input_error)
+			return 0;
 
 		/* skip to the next boundary */
 		if (!find_boundary(mi, line))
@@ -873,6 +885,8 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
 			handle_filter(mi, line);
 		}
 
+		if (mi->input_error)
+			break;
 	} while (!strbuf_getwholeline(line, mi->input, '\n'));
 
 handle_body_out:
@@ -966,7 +980,7 @@ int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
 	handle_info(mi);
 
-	return 0;
+	return mi->input_error;
 }
 
 static int git_mailinfo_config(const char *var, const char *value, void *mi_)
diff --git a/mailinfo.h b/mailinfo.h
index 27841be..5bf257d 100644
--- a/mailinfo.h
+++ b/mailinfo.h
@@ -31,6 +31,7 @@ struct mailinfo {
 	struct strbuf **s_hdr_data;
 
 	struct strbuf log_message;
+	int input_error;
 };
 
 extern void setup_mailinfo(struct mailinfo *);
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH v3 34/34] am: make direct call to mailinfo
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (32 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 33/34] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
@ 2015-10-19  7:28     ` Junio C Hamano
  2015-10-20 21:24     ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
  2015-10-21 23:15     ` [PATCH v4 00/35] " Junio C Hamano
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-19  7:28 UTC (permalink / raw)
  To: git

And finally the endgame.  Instead of spawning "git mailinfo" via the
run_command() API the same number of times as there are incoming
patches, make direct internal call to the libified mailinfo() from
"git am" to reduce the spawning overhead, which would matter on some
platforms.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/am.c | 42 +++++++++++++++++++++++-------------------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 4f77e07..1873307 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -27,6 +27,7 @@
 #include "notes-utils.h"
 #include "rerere.h"
 #include "prompt.h"
+#include "mailinfo.h"
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -1258,58 +1259,61 @@ static void am_append_signoff(struct am_state *state)
 static int parse_mail(struct am_state *state, const char *mail)
 {
 	FILE *fp;
-	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf msg = STRBUF_INIT;
 	struct strbuf author_name = STRBUF_INIT;
 	struct strbuf author_date = STRBUF_INIT;
 	struct strbuf author_email = STRBUF_INIT;
 	int ret = 0;
+	struct mailinfo mi;
 
-	cp.git_cmd = 1;
-	cp.in = xopen(mail, O_RDONLY, 0);
-	cp.out = xopen(am_path(state, "info"), O_WRONLY | O_CREAT, 0777);
+	setup_mailinfo(&mi);
 
-	argv_array_push(&cp.args, "mailinfo");
-	argv_array_push(&cp.args, state->utf8 ? "-u" : "-n");
+	if (state->utf8)
+		mi.metainfo_charset = get_commit_output_encoding();
+	else
+		mi.metainfo_charset = NULL;
 
 	switch (state->keep) {
 	case KEEP_FALSE:
 		break;
 	case KEEP_TRUE:
-		argv_array_push(&cp.args, "-k");
+		mi.keep_subject = 1;
 		break;
 	case KEEP_NON_PATCH:
-		argv_array_push(&cp.args, "-b");
+		mi.keep_non_patch_brackets_in_subject = 1;
 		break;
 	default:
 		die("BUG: invalid value for state->keep");
 	}
 
 	if (state->message_id)
-		argv_array_push(&cp.args, "-m");
+		mi.add_message_id = 1;
 
 	switch (state->scissors) {
 	case SCISSORS_UNSET:
 		break;
 	case SCISSORS_FALSE:
-		argv_array_push(&cp.args, "--no-scissors");
+		mi.use_scissors = 0;
 		break;
 	case SCISSORS_TRUE:
-		argv_array_push(&cp.args, "--scissors");
+		mi.use_scissors = 1;
 		break;
 	default:
 		die("BUG: invalid value for state->scissors");
 	}
 
-	argv_array_push(&cp.args, am_path(state, "msg"));
-	argv_array_push(&cp.args, am_path(state, "patch"));
-
-	if (run_command(&cp) < 0)
+	mi.input = fopen(mail, "r");
+	if (!mi.input)
+		die("could not open input");
+	mi.output = fopen(am_path(state, "info"), "w");
+	if (!mi.output)
+		die("could not open output 'info'");
+	if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
 		die("could not parse patch");
 
-	close(cp.in);
-	close(cp.out);
+	fclose(mi.input);
+	fclose(mi.output);
 
 	/* Extract message and author information */
 	fp = xfopen(am_path(state, "info"), "r");
@@ -1341,8 +1345,7 @@ static int parse_mail(struct am_state *state, const char *mail)
 	}
 
 	strbuf_addstr(&msg, "\n\n");
-	if (strbuf_read_file(&msg, am_path(state, "msg"), 0) < 0)
-		die_errno(_("could not read '%s'"), am_path(state, "msg"));
+	strbuf_addbuf(&msg, &mi.log_message);
 	stripspace(&msg, 0);
 
 	if (state->signoff)
@@ -1366,6 +1369,7 @@ finish:
 	strbuf_release(&author_email);
 	strbuf_release(&author_name);
 	strbuf_release(&sb);
+	clear_mailinfo(&mi);
 	return ret;
 }
 
-- 
2.6.2-383-g144b2e6

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 10/34] mailinfo: move global "line" into mailinfo() function
  2015-10-19  7:28     ` [PATCH v3 10/34] mailinfo: move global "line" into mailinfo() function Junio C Hamano
@ 2015-10-19 22:57       ` Eric Sunshine
  2015-10-20  5:19         ` Junio C Hamano
  0 siblings, 1 reply; 124+ messages in thread
From: Eric Sunshine @ 2015-10-19 22:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git List

On Mon, Oct 19, 2015 at 3:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
> With the previous steps, it becomes clear that the mailinfo()
> function is the only one that wants the "line" to be directly
> touchable.  Move it to the function scope of this function.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
> index 12d1eda..c8dc73f 100644
> --- a/builtin/mailinfo.c
> +++ b/builtin/mailinfo.c
> @@ -12,7 +12,6 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
>  static int keep_subject;
>  static int keep_non_patch_brackets_in_subject;
>  static const char *metainfo_charset;
> -static struct strbuf line = STRBUF_INIT;
>  static struct strbuf name = STRBUF_INIT;
>  static struct strbuf email = STRBUF_INIT;
>  static char *message_id;
> @@ -966,6 +965,8 @@ static void handle_info(void)
>  static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
>  {
>         int peek;
> +       struct strbuf line = STRBUF_INIT;

Does there need to be a corresponding strbuf_release(&line) at the end
of the function?

>         fin = in;
>         fout = out;
>
> --
> 2.6.2-383-g144b2e6

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 19/34] mailinfo: move check for metainfo_charset to convert_to_utf8()
  2015-10-19  7:28     ` [PATCH v3 19/34] mailinfo: move check for metainfo_charset to convert_to_utf8() Junio C Hamano
@ 2015-10-19 22:57       ` Eric Sunshine
  0 siblings, 0 replies; 124+ messages in thread
From: Eric Sunshine @ 2015-10-19 22:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git List

On Mon, Oct 19, 2015 at 3:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
> All callers of this function refrains from calling it when

s/refrains/refrain/

> mi->metainfo_charset is NULL; move the check to the callee,
> as it already has a few conditions at its beginning to turn
> it into a no-op.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
> index 26fd9b1..737d0fc 100644
> --- a/builtin/mailinfo.c
> +++ b/builtin/mailinfo.c
> @@ -524,7 +524,7 @@ static void convert_to_utf8(struct mailinfo *mi,
>  {
>         char *out;
>
> -       if (!charset || !*charset)
> +       if (!mi->metainfo_charset || !charset || !*charset)
>                 return;
>
>         if (same_encoding(mi->metainfo_charset, charset))
> @@ -599,8 +599,7 @@ static void decode_header(struct mailinfo *mi, struct strbuf *it)
>                         dec = decode_q_segment(&piecebuf, 1);
>                         break;
>                 }
> -               if (mi->metainfo_charset)
> -                       convert_to_utf8(mi, dec, charset_q.buf);
> +               convert_to_utf8(mi, dec, charset_q.buf);
>
>                 strbuf_addbuf(&outbuf, dec);
>                 strbuf_release(dec);
> @@ -745,8 +744,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
>                 mi->header_stage = 0;
>
>         /* normalize the log message to UTF-8. */
> -       if (mi->metainfo_charset)
> -               convert_to_utf8(mi, line, charset.buf);
> +       convert_to_utf8(mi, line, charset.buf);
>
>         if (mi->use_scissors && is_scissors_line(line)) {
>                 int i;
> --
> 2.6.2-383-g144b2e6

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 10/34] mailinfo: move global "line" into mailinfo() function
  2015-10-19 22:57       ` Eric Sunshine
@ 2015-10-20  5:19         ` Junio C Hamano
  0 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-20  5:19 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List

Eric Sunshine <sunshine@sunshineco.com> writes:

> On Mon, Oct 19, 2015 at 3:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> With the previous steps, it becomes clear that the mailinfo()
>> function is the only one that wants the "line" to be directly
>> touchable.  Move it to the function scope of this function.
>>
>> Signed-off-by: Junio C Hamano <gitster@pobox.com>
>> ---
>> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
>> index 12d1eda..c8dc73f 100644
>> --- a/builtin/mailinfo.c
>> +++ b/builtin/mailinfo.c
>> @@ -12,7 +12,6 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;
>>  static int keep_subject;
>>  static int keep_non_patch_brackets_in_subject;
>>  static const char *metainfo_charset;
>> -static struct strbuf line = STRBUF_INIT;
>>  static struct strbuf name = STRBUF_INIT;
>>  static struct strbuf email = STRBUF_INIT;
>>  static char *message_id;
>> @@ -966,6 +965,8 @@ static void handle_info(void)
>>  static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
>>  {
>>         int peek;
>> +       struct strbuf line = STRBUF_INIT;
>
> Does there need to be a corresponding strbuf_release(&line) at the end
> of the function?

Indeed.
Thanks.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (33 preceding siblings ...)
  2015-10-19  7:28     ` [PATCH v3 34/34] am: make direct call to mailinfo Junio C Hamano
@ 2015-10-20 21:24     ` Junio C Hamano
  2015-10-20 22:01       ` Stefan Beller
  2015-10-21 15:51       ` Ramsay Jones
  2015-10-21 23:15     ` [PATCH v4 00/35] " Junio C Hamano
  35 siblings, 2 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-20 21:24 UTC (permalink / raw)
  To: git

Junio C Hamano <gitster@pobox.com> writes:

> During the discussion on the recent "git am" regression, I noticed
> that the command reimplemented in C spawns one "mailsplit" and then
> spawns "mailinfo" followed by "apply --index" to commit the changes
> described in each message.  As there are platforms where spawning
> subprocess via run_command() interface is heavy-weight, something
> that is conceptually very simple like "mailinfo" is better called
> directly inside the process---something that is lightweight and
> frequently used is where the overhead of run_command() would be felt
> most.

Although I still haven't seen any offer to help from those who work
on the platforms that may benefit from this series the most, I have
some numbers on my desktop (Dell T3500 2.66GHz Xeon X5650 with 12GB,
running Ubuntu), where the cost of spawning is not as costly as
elsewhere, making this series less pressing.

Between 'master' and the version with this series (on 'jch'),
applying this 34-patch series itself on top of 'master' using "git
am", best of 5 numbers for running:

    time git am mbox >/dev/null

are

      (master)                 (with the series)
    real    0m0.648s            real    0m0.537s
    user    0m0.358s            user    0m0.338s
    sys     0m0.172s            sys     0m0.154s

I haven't re-read the series to catch leaks yet, so in that sense
the series is still not ready to be merged to 'next' (of course,
help with fresh set of eyes is very much appreciated here).

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-20 21:24     ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
@ 2015-10-20 22:01       ` Stefan Beller
  2015-10-20 22:06         ` Junio C Hamano
  2015-10-21 15:51       ` Ramsay Jones
  1 sibling, 1 reply; 124+ messages in thread
From: Stefan Beller @ 2015-10-20 22:01 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Oct 20, 2015 at 2:24 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> During the discussion on the recent "git am" regression, I noticed
>> that the command reimplemented in C spawns one "mailsplit" and then
>> spawns "mailinfo" followed by "apply --index" to commit the changes
>> described in each message.  As there are platforms where spawning
>> subprocess via run_command() interface is heavy-weight, something
>> that is conceptually very simple like "mailinfo" is better called
>> directly inside the process---something that is lightweight and
>> frequently used is where the overhead of run_command() would be felt
>> most.
>
> Although I still haven't seen any offer to help from those who work
> on the platforms that may benefit from this series the most, I have
> some numbers on my desktop (Dell T3500 2.66GHz Xeon X5650 with 12GB,
> running Ubuntu), where the cost of spawning is not as costly as
> elsewhere, making this series less pressing.

As far as I understand, this only helps for mailing list workflows, which
in my limited view of the world is only found in established infrastructure
projects, who tend to be maintained by people who run some kind of
ab-nomination of unix.
("Are there people in Windows land who heavily use the email workflow?")

>
> Between 'master' and the version with this series (on 'jch'),
> applying this 34-patch series itself on top of 'master' using "git
> am", best of 5 numbers for running:
>
>     time git am mbox >/dev/null
>
> are
>
>       (master)                 (with the series)
>     real    0m0.648s            real    0m0.537s
>     user    0m0.358s            user    0m0.338s
>     sys     0m0.172s            sys     0m0.154s
>
> I haven't re-read the series to catch leaks yet, so in that sense
> the series is still not ready to be merged to 'next' (of course,
> help with fresh set of eyes is very much appreciated here).

I'll take another look after sending out my series.

> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-20 22:01       ` Stefan Beller
@ 2015-10-20 22:06         ` Junio C Hamano
  2015-10-20 22:08           ` Stefan Beller
  0 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-20 22:06 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git

Stefan Beller <sbeller@google.com> writes:

> As far as I understand, this only helps for mailing list workflows, which
> in my limited view of the world is only found in established infrastructure
> projects, who tend to be maintained by people who run some kind of
> ab-nomination of unix.
> ("Are there people in Windows land who heavily use the email workflow?")

Forgot that "am" is a workhorse behind "rebase"?

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-20 22:06         ` Junio C Hamano
@ 2015-10-20 22:08           ` Stefan Beller
  0 siblings, 0 replies; 124+ messages in thread
From: Stefan Beller @ 2015-10-20 22:08 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Oct 20, 2015 at 3:06 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Stefan Beller <sbeller@google.com> writes:
>
>> As far as I understand, this only helps for mailing list workflows, which
>> in my limited view of the world is only found in established infrastructure
>> projects, who tend to be maintained by people who run some kind of
>> ab-nomination of unix.
>> ("Are there people in Windows land who heavily use the email workflow?")
>
> Forgot that "am" is a workhorse behind "rebase"?
>

Yes, I was deceived by the name of the series ("it must be mail related and only
mail related" was my thinking).

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-20 21:24     ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
  2015-10-20 22:01       ` Stefan Beller
@ 2015-10-21 15:51       ` Ramsay Jones
  2015-10-21 20:04         ` Johannes Sixt
  1 sibling, 1 reply; 124+ messages in thread
From: Ramsay Jones @ 2015-10-21 15:51 UTC (permalink / raw)
  To: Junio C Hamano, git



On 20/10/15 22:24, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
>> During the discussion on the recent "git am" regression, I noticed
>> that the command reimplemented in C spawns one "mailsplit" and then
>> spawns "mailinfo" followed by "apply --index" to commit the changes
>> described in each message.  As there are platforms where spawning
>> subprocess via run_command() interface is heavy-weight, something
>> that is conceptually very simple like "mailinfo" is better called
>> directly inside the process---something that is lightweight and
>> frequently used is where the overhead of run_command() would be felt
>> most.
> 
> Although I still haven't seen any offer to help from those who work
> on the platforms that may benefit from this series the most, I have
> some numbers on my desktop (Dell T3500 2.66GHz Xeon X5650 with 12GB,
> running Ubuntu), where the cost of spawning is not as costly as
> elsewhere, making this series less pressing.

I suspect that I haven't tested exactly the same version as you, but I had
a quick look at testing this on Cygwin today. I have included a complete
transcript (below), so you can see what I did wrong! :-P

> 
> Between 'master' and the version with this series (on 'jch'),
> applying this 34-patch series itself on top of 'master' using "git
> am", best of 5 numbers for running:
> 
>     time git am mbox >/dev/null
> 
> are
> 
>       (master)                 (with the series)
>     real    0m0.648s            real    0m0.537s
>     user    0m0.358s            user    0m0.338s
>     sys     0m0.172s            sys     0m0.154s
> 

The corresponding times for me were:

    (master)           (with the series)
  real	0m9.760s      real	0m5.744s
  user	0m0.531s      user	0m0.656s
  sys	0m5.726s      sys	0m3.520s

So, yes, a noticeable improvement! :)

HTH

ATB,
Ramsay Jones

  $ uname -a
  CYGWIN_NT-6.3 satellite 2.2.1(0.289/5/3) 2015-08-20 11:42 x86_64 Cygwin
  $ pwd
  /home/ramsay/git
  $ git log --decorate --oneline -1
  74301d6 (HEAD -> master, origin/master, origin/HEAD) Sync with maint
  $ ./git version
  git version 2.6.2.280.g74301d6
  $ git format-patch --stdout 2a5ce7c^..896df93 >mailinfo.mbox
  $ git format-patch --stdout a4106a8^..559e247 >>mailinfo.mbox
  $ git checkout -b master-mailinfo master
  Switched to a new branch 'master-mailinfo'
  $ time ./git am mailinfo.mbox
  Applying: mailinfo: remove a no-op call convert_to_utf8(it, "")
  Applying: mailinfo: fold decode_header_bq() into decode_header()
  Applying: mailinfo: fix an off-by-one error in the boundary stack
  Applying: mailinfo: explicitly close file handle to the patch output
  Applying: mailinfo: move handle_boundary() lower
  Applying: mailinfo: get rid of function-local static states
  Applying: mailinfo: do not let handle_body() touch global "line" directly
  Applying: mailinfo: do not let handle_boundary() touch global "line" directly
  Applying: mailinfo: do not let find_boundary() touch global "line" directly
  Applying: mailinfo: move global "line" into mailinfo() function
  Applying: mailinfo: introduce "struct mailinfo" to hold globals
  Applying: mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  Applying: mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  Applying: mailinfo: move filter/header stage to struct mailinfo
  Applying: mailinfo: move patch_lines to struct mailinfo
  Applying: mailinfo: move add_message_id and message_id to struct mailinfo
  Applying: mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  Applying: mailinfo: move metainfo_charset to struct mailinfo
  Applying: mailinfo: move check for metainfo_charset to convert_to_utf8()
  Applying: mailinfo: move transfer_encoding to struct mailinfo
  Applying: mailinfo: move charset to struct mailinfo
  Applying: mailinfo: move cmitmsg and patchfile to struct mailinfo
  Applying: mailinfo: move [ps]_hdr_data to struct mailinfo
  Applying: mailinfo: move content/content_top to struct mailinfo
  Applying: mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  Applying: mailinfo: keep the parsed log message in a strbuf
  Applying: mailinfo: move read_one_header_line() closer to its callers
  Applying: mailinfo: move check_header() after the helpers it uses
  Applying: mailinfo: move cleanup_space() before its users
  Applying: mailinfo: move definition of MAX_HDR_PARSED closer to its use
  Applying: mailinfo: libify
  Applying: mailinfo: handle charset conversion errors in the caller
  Applying: mailinfo: remove calls to exit() and die() deep in the callchain
  Applying: am: make direct call to mailinfo
  Applying: mailinfo: plug strbuf leak during continuation line handling
  
  real	0m9.760s
  user	0m0.531s
  sys	0m5.726s
  $ 
  
  $ make clean >/dev/null 2>&1
  $ make >out.mi 2>&1
  $ ./git version
  git version 2.6.2.315.g1e9f6ff
  $ git describe
  v2.6.2-315-g1e9f6ff
  $ git checkout -b new-mailinfo master
  Switched to a new branch 'new-mailinfo'
  $ time ./git am mailinfo.mbox
  Applying: mailinfo: remove a no-op call convert_to_utf8(it, "")
  Applying: mailinfo: fold decode_header_bq() into decode_header()
  Applying: mailinfo: fix an off-by-one error in the boundary stack
  Applying: mailinfo: explicitly close file handle to the patch output
  Applying: mailinfo: move handle_boundary() lower
  Applying: mailinfo: get rid of function-local static states
  Applying: mailinfo: do not let handle_body() touch global "line" directly
  Applying: mailinfo: do not let handle_boundary() touch global "line" directly
  Applying: mailinfo: do not let find_boundary() touch global "line" directly
  Applying: mailinfo: move global "line" into mailinfo() function
  Applying: mailinfo: introduce "struct mailinfo" to hold globals
  Applying: mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  Applying: mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  Applying: mailinfo: move filter/header stage to struct mailinfo
  Applying: mailinfo: move patch_lines to struct mailinfo
  Applying: mailinfo: move add_message_id and message_id to struct mailinfo
  Applying: mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  Applying: mailinfo: move metainfo_charset to struct mailinfo
  Applying: mailinfo: move check for metainfo_charset to convert_to_utf8()
  Applying: mailinfo: move transfer_encoding to struct mailinfo
  Applying: mailinfo: move charset to struct mailinfo
  Applying: mailinfo: move cmitmsg and patchfile to struct mailinfo
  Applying: mailinfo: move [ps]_hdr_data to struct mailinfo
  Applying: mailinfo: move content/content_top to struct mailinfo
  Applying: mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  Applying: mailinfo: keep the parsed log message in a strbuf
  Applying: mailinfo: move read_one_header_line() closer to its callers
  Applying: mailinfo: move check_header() after the helpers it uses
  Applying: mailinfo: move cleanup_space() before its users
  Applying: mailinfo: move definition of MAX_HDR_PARSED closer to its use
  Applying: mailinfo: libify
  Applying: mailinfo: handle charset conversion errors in the caller
  Applying: mailinfo: remove calls to exit() and die() deep in the callchain
  Applying: am: make direct call to mailinfo
  Applying: mailinfo: plug strbuf leak during continuation line handling
  
  real	0m5.744s
  user	0m0.656s
  sys	0m3.520s
  $ 

  $ git rev-parse master-mailinfo^{tree} new-mailinfo^{tree}
  cebb1a110f3af9f4393bb66b942ae28e2743f233
  cebb1a110f3af9f4393bb66b942ae28e2743f233
  $ 

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-21 15:51       ` Ramsay Jones
@ 2015-10-21 20:04         ` Johannes Sixt
  2015-10-21 20:07           ` Junio C Hamano
  2015-10-26 14:25           ` Johannes Schindelin
  0 siblings, 2 replies; 124+ messages in thread
From: Johannes Sixt @ 2015-10-21 20:04 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Ramsay Jones, git

Am 21.10.2015 um 17:51 schrieb Ramsay Jones:
> On 20/10/15 22:24, Junio C Hamano wrote:
>> Junio C Hamano <gitster@pobox.com> writes:
>> some numbers on my desktop (Dell T3500 2.66GHz Xeon X5650 with 12GB,
>> running Ubuntu),
>
> I suspect that I haven't tested exactly the same version as you, but I had
> a quick look at testing this on Cygwin today. I have included a complete
> transcript (below), so you can see what I did wrong! :-P
>
>>
>> Between 'master' and the version with this series (on 'jch'),
>> applying this 34-patch series itself on top of 'master' using "git
>> am", best of 5 numbers for running:
>>
>>      time git am mbox >/dev/null
>>
>> are
>>
>>        (master)                 (with the series)
>>      real    0m0.648s            real    0m0.537s
>>      user    0m0.358s            user    0m0.338s
>>      sys     0m0.172s            sys     0m0.154s
>>
>
> The corresponding times for me were:
>
>      (master)           (with the series)
>    real	0m9.760s      real	0m5.744s
>    user	0m0.531s      user	0m0.656s
>    sys	0m5.726s      sys	0m3.520s
>
> So, yes, a noticeable improvement! :)

Same here, on Windows built with the old msysgit environment:

(master)             (with the series)
real    0m3.147s      real    0m1.947s
user    0m0.016s      user    0m0.000s
sys     0m0.015s      sys     0m0.031s

Although I tested 'git am patches/*' where the patches/* are the result 
of 'git-format-patch v2.6.1..github/jc/mailinfo-lib'.

-- Hannes

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-21 20:04         ` Johannes Sixt
@ 2015-10-21 20:07           ` Junio C Hamano
  2015-10-26 14:25           ` Johannes Schindelin
  1 sibling, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-21 20:07 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Ramsay Jones, git

Johannes Sixt <j6t@kdbg.org> writes:

> Am 21.10.2015 um 17:51 schrieb Ramsay Jones:
>> On 20/10/15 22:24, Junio C Hamano wrote:
>>>
>>>      time git am mbox >/dev/null
>>>
>>> are
>>>
>>>        (master)                 (with the series)
>>>      real    0m0.648s            real    0m0.537s
>>>      user    0m0.358s            user    0m0.338s
>>>      sys     0m0.172s            sys     0m0.154s
>>>
>>
>> The corresponding times for me were:
>>
>>      (master)           (with the series)
>>    real	0m9.760s      real	0m5.744s
>>    user	0m0.531s      user	0m0.656s
>>    sys	0m5.726s      sys	0m3.520s
>>
>> So, yes, a noticeable improvement! :)
>
> Same here, on Windows built with the old msysgit environment:
>
> (master)             (with the series)
> real    0m3.147s      real    0m1.947s
> user    0m0.016s      user    0m0.000s
> sys     0m0.015s      sys     0m0.031s
>
> Although I tested 'git am patches/*' where the patches/* are the
> result of 'git-format-patch v2.6.1..github/jc/mailinfo-lib'.

Thanks, both.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 14/34] mailinfo: move filter/header stage to struct mailinfo
  2015-10-19  7:28     ` [PATCH v3 14/34] mailinfo: move filter/header stage " Junio C Hamano
@ 2015-10-21 20:20       ` Stefan Beller
  0 siblings, 0 replies; 124+ messages in thread
From: Stefan Beller @ 2015-10-21 20:20 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Mon, Oct 19, 2015 at 12:28 AM, Junio C Hamano <gitster@pobox.com> wrote:

> @@ -28,6 +31,7 @@ static enum  {
>
>  static struct strbuf charset = STRBUF_INIT;
>  static int patch_lines;
> +

nitpicking here. Why is this patch the best in series to introduce a
new line at this position? ;)

>  static struct strbuf **p_hdr_data, **s_hdr_data;
>  static int use_scissors;
>  static int add_message_id;
> @@ -718,25 +722,25 @@ static int is_scissors_line(const struct strbuf *line)
>                 gap * 2 < perforation);
>  }

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 17/34] mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  2015-10-19  7:28     ` [PATCH v3 17/34] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
@ 2015-10-21 20:24       ` Stefan Beller
  2015-10-21 21:02         ` Junio C Hamano
  0 siblings, 1 reply; 124+ messages in thread
From: Stefan Beller @ 2015-10-21 20:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Mon, Oct 19, 2015 at 12:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  builtin/mailinfo.c | 23 +++++++++++++----------
>  1 file changed, 13 insertions(+), 10 deletions(-)
>
> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
> index c0522f2..2c8f249 100644
> --- a/builtin/mailinfo.c
> +++ b/builtin/mailinfo.c
> @@ -20,6 +20,8 @@ struct mailinfo {
>         int keep_subject;
>         int keep_non_patch_brackets_in_subject;
>         int add_message_id;
> +       int use_scissors;
> +       int use_inbody_headers; /* defaults to 1 */

IMHO there is no need for the comment here, stating its default.
That can be looked up in the init function, which is as convenient as
reading globals in a file?

>
>         char *message_id;
>         int patch_lines;
> @@ -34,8 +36,6 @@ static enum  {
>  static struct strbuf charset = STRBUF_INIT;
>
>  static struct strbuf **p_hdr_data, **s_hdr_data;
> -static int use_scissors;
> -static int use_inbody_headers = 1;
>
>  #define MAX_HDR_PARSED 10
>  #define MAX_BOUNDARIES 5
> @@ -734,7 +734,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
>                         return 0;
>         }
>
> -       if (use_inbody_headers && mi->header_stage) {
> +       if (mi->use_inbody_headers && mi->header_stage) {
>                 mi->header_stage = check_header(mi, line, s_hdr_data, 0);
>                 if (mi->header_stage)
>                         return 0;
> @@ -748,7 +748,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
>         if (metainfo_charset)
>                 convert_to_utf8(line, charset.buf);
>
> -       if (use_scissors && is_scissors_line(line)) {
> +       if (mi->use_scissors && is_scissors_line(line)) {
>                 int i;
>                 if (fseek(cmitmsg, 0L, SEEK_SET))
>                         die_errno("Could not rewind output message file");
> @@ -1009,12 +1009,14 @@ static int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
>         return 0;
>  }
>
> -static int git_mailinfo_config(const char *var, const char *value, void *unused)
> +static int git_mailinfo_config(const char *var, const char *value, void *mi_)
>  {
> +       struct mailinfo *mi = mi_;
> +
>         if (!starts_with(var, "mailinfo."))
> -               return git_default_config(var, value, unused);
> +               return git_default_config(var, value, NULL);
>         if (!strcmp(var, "mailinfo.scissors")) {
> -               use_scissors = git_config_bool(var, value);
> +               mi->use_scissors = git_config_bool(var, value);
>                 return 0;
>         }
>         /* perhaps others here */
> @@ -1027,6 +1029,7 @@ static void setup_mailinfo(struct mailinfo *mi)
>         strbuf_init(&mi->name, 0);
>         strbuf_init(&mi->email, 0);
>         mi->header_stage = 1;
> +       mi->use_inbody_headers = 1;
>         git_config(git_mailinfo_config, &mi);
>  }
>
> @@ -1068,11 +1071,11 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
>                 else if (starts_with(argv[1], "--encoding="))
>                         metainfo_charset = argv[1] + 11;
>                 else if (!strcmp(argv[1], "--scissors"))
> -                       use_scissors = 1;
> +                       mi.use_scissors = 1;
>                 else if (!strcmp(argv[1], "--no-scissors"))
> -                       use_scissors = 0;
> +                       mi.use_scissors = 0;
>                 else if (!strcmp(argv[1], "--no-inbody-headers"))
> -                       use_inbody_headers = 0;
> +                       mi.use_inbody_headers = 0;
>                 else
>                         usage(mailinfo_usage);
>                 argc--; argv++;
> --
> 2.6.2-383-g144b2e6
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 23/34] mailinfo: move [ps]_hdr_data to struct mailinfo
  2015-10-19  7:28     ` [PATCH v3 23/34] mailinfo: move [ps]_hdr_data " Junio C Hamano
@ 2015-10-21 20:30       ` Stefan Beller
  0 siblings, 0 replies; 124+ messages in thread
From: Stefan Beller @ 2015-10-21 20:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Mon, Oct 19, 2015 at 12:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
> @@ -733,7 +733,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
>         }
>
>         if (mi->use_inbody_headers && mi->header_stage) {
> -               mi->header_stage = check_header(mi, line, s_hdr_data, 0);
> +               mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);

Would it make sense to get rid of the third argument for check_header instead?
We already pass in mi, so check_header can access s_hdr_data?

>                 if (mi->header_stage)

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 24/34] mailinfo: move content/content_top to struct mailinfo
  2015-10-19  7:28     ` [PATCH v3 24/34] mailinfo: move content/content_top " Junio C Hamano
@ 2015-10-21 20:36       ` Stefan Beller
  2015-10-21 21:04         ` Junio C Hamano
  0 siblings, 1 reply; 124+ messages in thread
From: Stefan Beller @ 2015-10-21 20:36 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Mon, Oct 19, 2015 at 12:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  builtin/mailinfo.c | 45 ++++++++++++++++++++++++++-------------------
>  1 file changed, 26 insertions(+), 19 deletions(-)
>
> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
> index 2c194da..ec65805 100644
> --- a/builtin/mailinfo.c
> +++ b/builtin/mailinfo.c
> @@ -7,6 +7,8 @@
>  #include "utf8.h"
>  #include "strbuf.h"
>
> +#define MAX_BOUNDARIES 5
> +
>  struct mailinfo {
>         FILE *input;
>         FILE *output;
> @@ -22,6 +24,8 @@ struct mailinfo {
>         int use_inbody_headers; /* defaults to 1 */
>         const char *metainfo_charset;
>
> +       struct strbuf *content[MAX_BOUNDARIES];
> +       struct strbuf **content_top;
>         struct strbuf charset;
>         char *message_id;
>         enum  {
> @@ -35,7 +39,6 @@ struct mailinfo {
>  };
>
>  #define MAX_HDR_PARSED 10
> -#define MAX_BOUNDARIES 5

Would it make sense to also move MAX_HDR_PARSED, such that we have all
defines in one place? The previous patch moved HDR related stuff around, so
either there or here?


>
>  static void cleanup_space(struct strbuf *sb);
>
> @@ -180,10 +183,6 @@ static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
>         return 1;
>  }
>
> -static struct strbuf *content[MAX_BOUNDARIES];
> -
> -static struct strbuf **content_top = content;
> -
>  static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
>  {
>         struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
> @@ -191,11 +190,11 @@ static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
>
>         if (slurp_attr(line->buf, "boundary=", boundary)) {
>                 strbuf_insert(boundary, 0, "--", 2);
> -               if (++content_top >= &content[MAX_BOUNDARIES]) {
> +               if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
>                         fprintf(stderr, "Too many boundaries to handle\n");
>                         exit(1);
>                 }
> -               *content_top = boundary;
> +               *(mi->content_top) = boundary;
>                 boundary = NULL;
>         }
>         slurp_attr(line->buf, "charset=", &mi->charset);
> @@ -223,10 +222,12 @@ static void handle_content_transfer_encoding(struct mailinfo *mi,
>                 mi->transfer_encoding = TE_DONTCARE;
>  }
>
> -static int is_multipart_boundary(const struct strbuf *line)
> +static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
>  {
> -       return (((*content_top)->len <= line->len) &&
> -               !memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
> +       struct strbuf *content_top = *(mi->content_top);
> +
> +       return ((content_top->len <= line->len) &&
> +               !memcmp(line->buf, content_top->buf, content_top->len));
>  }
>
>  static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
> @@ -799,7 +800,7 @@ static void handle_filter(struct mailinfo *mi, struct strbuf *line)
>  static int find_boundary(struct mailinfo *mi, struct strbuf *line)
>  {
>         while (!strbuf_getline(line, mi->input, '\n')) {
> -               if (*content_top && is_multipart_boundary(line))
> +               if (*(mi->content_top) && is_multipart_boundary(mi, line))
>                         return 1;
>         }
>         return 0;
> @@ -811,18 +812,18 @@ static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
>
>         strbuf_addch(&newline, '\n');
>  again:
> -       if (line->len >= (*content_top)->len + 2 &&
> -           !memcmp(line->buf + (*content_top)->len, "--", 2)) {
> +       if (line->len >= (*(mi->content_top))->len + 2 &&
> +           !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
>                 /* we hit an end boundary */
>                 /* pop the current boundary off the stack */
> -               strbuf_release(*content_top);
> -               free(*content_top);
> -               *content_top = NULL;
> +               strbuf_release(*(mi->content_top));
> +               free(*(mi->content_top));
> +               *(mi->content_top) = NULL;
>
>                 /* technically won't happen as is_multipart_boundary()
>                    will fail first.  But just in case..
>                  */
> -               if (--content_top < content) {
> +               if (--mi->content_top < mi->content) {
>                         fprintf(stderr, "Detected mismatched boundaries, "
>                                         "can't recover\n");
>                         exit(1);
> @@ -857,14 +858,14 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
>         struct strbuf prev = STRBUF_INIT;
>
>         /* Skip up to the first boundary */
> -       if (*content_top) {
> +       if (*(mi->content_top)) {
>                 if (!find_boundary(mi, line))
>                         goto handle_body_out;
>         }
>
>         do {
>                 /* process any boundary lines */
> -               if (*content_top && is_multipart_boundary(line)) {
> +               if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
>                         /* flush any leftover */
>                         if (prev.len) {
>                                 handle_filter(mi, &prev);
> @@ -1028,6 +1029,7 @@ static void setup_mailinfo(struct mailinfo *mi)
>         strbuf_init(&mi->charset, 0);
>         mi->header_stage = 1;
>         mi->use_inbody_headers = 1;
> +       mi->content_top = mi->content;
>         git_config(git_mailinfo_config, &mi);
>  }
>
> @@ -1046,6 +1048,11 @@ static void clear_mailinfo(struct mailinfo *mi)
>         for (i = 0; mi->s_hdr_data[i]; i++)
>                 strbuf_release(mi->s_hdr_data[i]);
>         free(mi->s_hdr_data);
> +
> +       while (mi->content < mi->content_top) {
> +               free(*(mi->content_top));
> +               mi->content_top--;
> +       }
>  }
>
>  static const char mailinfo_usage[] =
> --
> 2.6.2-383-g144b2e6
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 17/34] mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  2015-10-21 20:24       ` Stefan Beller
@ 2015-10-21 21:02         ` Junio C Hamano
  0 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-21 21:02 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git

Stefan Beller <sbeller@google.com> writes:

> On Mon, Oct 19, 2015 at 12:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Signed-off-by: Junio C Hamano <gitster@pobox.com>
>> ---
>>  builtin/mailinfo.c | 23 +++++++++++++----------
>>  1 file changed, 13 insertions(+), 10 deletions(-)
>>
>> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
>> index c0522f2..2c8f249 100644
>> --- a/builtin/mailinfo.c
>> +++ b/builtin/mailinfo.c
>> @@ -20,6 +20,8 @@ struct mailinfo {
>>         int keep_subject;
>>         int keep_non_patch_brackets_in_subject;
>>         int add_message_id;
>> +       int use_scissors;
>> +       int use_inbody_headers; /* defaults to 1 */
>
> IMHO there is no need for the comment here, stating its default.
> That can be looked up in the init function, which is as convenient as
> reading globals in a file?

It was crucial to ensure correctness during conversion, that is, a
conversion done in

 (1) Remove "static int use_inbody_headers = 1"
 (2) Add "int use_inbody_headers;"
 (3) Update setup_mailinfo()

sequence would have lost a clue as to what the right value to assign
in step (3).  Tweaking (2) to leave "/* this starts at 1 */" helped
avoid such a mistake.

When reading the end result, the above three appear to have been
done simultaneously, so I agree that the comment does not help.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 24/34] mailinfo: move content/content_top to struct mailinfo
  2015-10-21 20:36       ` Stefan Beller
@ 2015-10-21 21:04         ` Junio C Hamano
  2015-10-21 21:08           ` Stefan Beller
  0 siblings, 1 reply; 124+ messages in thread
From: Junio C Hamano @ 2015-10-21 21:04 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git

Stefan Beller <sbeller@google.com> writes:

> On Mon, Oct 19, 2015 at 12:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Signed-off-by: Junio C Hamano <gitster@pobox.com>
>> ---
>>  builtin/mailinfo.c | 45 ++++++++++++++++++++++++++-------------------
>>  1 file changed, 26 insertions(+), 19 deletions(-)
>>
>> diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
>> index 2c194da..ec65805 100644
>> --- a/builtin/mailinfo.c
>> +++ b/builtin/mailinfo.c
>> @@ -7,6 +7,8 @@
>>  #include "utf8.h"
>>  #include "strbuf.h"
>>
>> +#define MAX_BOUNDARIES 5
>> +
>>  struct mailinfo {
>>         FILE *input;
>>         FILE *output;
>> @@ -22,6 +24,8 @@ struct mailinfo {
>>         int use_inbody_headers; /* defaults to 1 */
>>         const char *metainfo_charset;
>>
>> +       struct strbuf *content[MAX_BOUNDARIES];
>> +       struct strbuf **content_top;
>>         struct strbuf charset;
>>         char *message_id;
>>         enum  {
>> @@ -35,7 +39,6 @@ struct mailinfo {
>>  };
>>
>>  #define MAX_HDR_PARSED 10
>> -#define MAX_BOUNDARIES 5
>
> Would it make sense to also move MAX_HDR_PARSED, such that we have all
> defines in one place?

Looking at their final resting place, I do not think so.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 24/34] mailinfo: move content/content_top to struct mailinfo
  2015-10-21 21:04         ` Junio C Hamano
@ 2015-10-21 21:08           ` Stefan Beller
  0 siblings, 0 replies; 124+ messages in thread
From: Stefan Beller @ 2015-10-21 21:08 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Wed, Oct 21, 2015 at 2:04 PM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Looking at their final resting place, I do not think so.

Right, I comment along the way without looking ahead, so this was a bad comment.

^ permalink raw reply	[flat|nested] 124+ messages in thread

* [PATCH v4 00/35] libify mailinfo and call it directly from am
  2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
                       ` (34 preceding siblings ...)
  2015-10-20 21:24     ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
@ 2015-10-21 23:15     ` Junio C Hamano
  35 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-21 23:15 UTC (permalink / raw)
  To: git

The change in the endgame since v3 ($gmane/279832) is almost none
(interdiff attached at the end), so I won't be sending the patches
themselves.  The bulk out of the miniscule interdiff comes from the
fifth "plug strbuf leak" patch.

The patches have been reordered to make the structure of the series
clearer:

Preliminary fixes:

  mailinfo: remove a no-op call convert_to_utf8(it, "")
  mailinfo: fold decode_header_bq() into decode_header()
  mailinfo: fix an off-by-one error in the boundary stack
  mailinfo: explicitly close file handle to the patch output
  mailinfo: plug strbuf leak during continuation line handling

Group related things together and remove forward declarations:

  mailinfo: move handle_boundary() lower
  mailinfo: move read_one_header_line() closer to its callers
  mailinfo: move check_header() after the helpers it uses
  mailinfo: move cleanup_space() before its users
  mailinfo: move definition of MAX_HDR_PARSED closer to its use

Preliminary logic clean-up:

  mailinfo: get rid of function-local static states
  mailinfo: do not let handle_body() touch global "line" directly
  mailinfo: do not let handle_boundary() touch global "line" directly
  mailinfo: do not let find_boundary() touch global "line" directly
  mailinfo: move global "line" into mailinfo() function

Getting rid of the globals:

  mailinfo: introduce "struct mailinfo" to hold globals
  mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo
  mailinfo: move global "FILE *fin, *fout" to struct mailinfo
  mailinfo: move filter/header stage to struct mailinfo
  mailinfo: move patch_lines to struct mailinfo
  mailinfo: move add_message_id and message_id to struct mailinfo
  mailinfo: move use_scissors and use_inbody_headers to struct mailinfo
  mailinfo: move metainfo_charset to struct mailinfo
  mailinfo: move check for metainfo_charset to convert_to_utf8()
  mailinfo: move transfer_encoding to struct mailinfo
  mailinfo: move charset to struct mailinfo
  mailinfo: move cmitmsg and patchfile to struct mailinfo
  mailinfo: move [ps]_hdr_data to struct mailinfo
  mailinfo: move content/content_top to struct mailinfo

Libify:

  mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak
  mailinfo: keep the parsed log message in a strbuf
  mailinfo: libify

Lifting the error handling to the caller:

  mailinfo: handle charset conversion errors in the caller
  mailinfo: remove calls to exit() and die() deep in the callchain

Endgame:

  am: make direct call to mailinfo

 Makefile           |    1 +
 builtin/am.c       |   42 ++-
 builtin/mailinfo.c | 1055 +---------------------------------------------------
 mailinfo.c         | 1037 +++++++++++++++++++++++++++++++++++++++++++++++++++
 mailinfo.h         |   41 ++
 5 files changed, 1122 insertions(+), 1054 deletions(-)
 create mode 100644 mailinfo.c
 create mode 100644 mailinfo.h

-- 
2.6.2-377-g450896c


(Interdiff)

diff --git a/mailinfo.c b/mailinfo.c
index 49eaa99..e157ca6 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -729,6 +729,8 @@ static int is_rfc2822_header(const struct strbuf *line)
 
 static int read_one_header_line(struct strbuf *line, FILE *in)
 {
+	struct strbuf continuation = STRBUF_INIT;
+
 	/* Get the first part of the line. */
 	if (strbuf_getline(line, in, '\n'))
 		return 0;
@@ -750,7 +752,6 @@ static int read_one_header_line(struct strbuf *line, FILE *in)
 	 */
 	for (;;) {
 		int peek;
-		struct strbuf continuation = STRBUF_INIT;
 
 		peek = fgetc(in); ungetc(peek, in);
 		if (peek != ' ' && peek != '\t')
@@ -761,6 +762,7 @@ static int read_one_header_line(struct strbuf *line, FILE *in)
 		strbuf_rtrim(&continuation);
 		strbuf_addbuf(line, &continuation);
 	}
+	strbuf_release(&continuation);
 
 	return 1;
 }
diff --git a/mailinfo.h b/mailinfo.h
index 5bf257d..93776a7 100644
--- a/mailinfo.h
+++ b/mailinfo.h
@@ -14,7 +14,7 @@ struct mailinfo {
 	int keep_non_patch_brackets_in_subject;
 	int add_message_id;
 	int use_scissors;
-	int use_inbody_headers; /* defaults to 1 */
+	int use_inbody_headers;
 	const char *metainfo_charset;
 
 	struct strbuf *content[MAX_BOUNDARIES];

^ permalink raw reply related	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-21 20:04         ` Johannes Sixt
  2015-10-21 20:07           ` Junio C Hamano
@ 2015-10-26 14:25           ` Johannes Schindelin
  2015-10-26 18:42             ` Junio C Hamano
  1 sibling, 1 reply; 124+ messages in thread
From: Johannes Schindelin @ 2015-10-26 14:25 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Junio C Hamano, Ramsay Jones, git

Hi,

On Wed, 21 Oct 2015, Johannes Sixt wrote:

> Am 21.10.2015 um 17:51 schrieb Ramsay Jones:
> > On 20/10/15 22:24, Junio C Hamano wrote:
> > > Junio C Hamano <gitster@pobox.com> writes:
> > > some numbers on my desktop (Dell T3500 2.66GHz Xeon X5650 with 12GB,
> > > running Ubuntu),
> >
> > I suspect that I haven't tested exactly the same version as you, but I had
> > a quick look at testing this on Cygwin today. I have included a complete
> > transcript (below), so you can see what I did wrong! :-P
> >
> > >
> > > Between 'master' and the version with this series (on 'jch'),
> > > applying this 34-patch series itself on top of 'master' using "git
> > > am", best of 5 numbers for running:
> > >
> > >      time git am mbox >/dev/null
> > >
> > > are
> > >
> > >        (master)                 (with the series)
> > >      real    0m0.648s            real    0m0.537s
> > >      user    0m0.358s            user    0m0.338s
> > >      sys     0m0.172s            sys     0m0.154s
> > >
> >
> > The corresponding times for me were:
> >
> >      (master)           (with the series)
> >    real	0m9.760s      real	0m5.744s
> >    user	0m0.531s      user	0m0.656s
> >    sys	0m5.726s      sys	0m3.520s
> >
> > So, yes, a noticeable improvement! :)
> 
> Same here, on Windows built with the old msysgit environment:
> 
> (master)             (with the series)
> real    0m3.147s      real    0m1.947s
> user    0m0.016s      user    0m0.000s
> sys     0m0.015s      sys     0m0.031s
> 
> Although I tested 'git am patches/*' where the patches/* are the result of
> 'git-format-patch v2.6.1..github/jc/mailinfo-lib'.

2.548s vs 2.068s here.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH v3 00/34] libify mailinfo and call it directly from am
  2015-10-26 14:25           ` Johannes Schindelin
@ 2015-10-26 18:42             ` Junio C Hamano
  0 siblings, 0 replies; 124+ messages in thread
From: Junio C Hamano @ 2015-10-26 18:42 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Johannes Sixt, Ramsay Jones, git

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> > The corresponding times for me were:
>> >
>> >      (master)           (with the series)
>> >    real	0m9.760s      real	0m5.744s
>> >    user	0m0.531s      user	0m0.656s
>> >    sys	0m5.726s      sys	0m3.520s
>> >
>> > So, yes, a noticeable improvement! :)
>> 
>> Same here, on Windows built with the old msysgit environment:
>> 
>> (master)             (with the series)
>> real    0m3.147s      real    0m1.947s
>> user    0m0.016s      user    0m0.000s
>> sys     0m0.015s      sys     0m0.031s
>> 
>> Although I tested 'git am patches/*' where the patches/* are the result of
>> 'git-format-patch v2.6.1..github/jc/mailinfo-lib'.
>
> 2.548s vs 2.068s here.
>
> Ciao,
> Johannes

Thanks.  Let's start thinking about moving it forward.

^ permalink raw reply	[flat|nested] 124+ messages in thread

end of thread, other threads:[~2015-10-26 18:42 UTC | newest]

Thread overview: 124+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-10-13 23:16 [PATCH 00/26] mailinfo libification Junio C Hamano
2015-10-13 23:16 ` [PATCH 01/26] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
2015-10-13 23:16 ` [PATCH 02/26] mailinfo: fix for off-by-one error in boundary stack Junio C Hamano
2015-10-14 20:12   ` Stefan Beller
2015-10-14 20:28     ` Junio C Hamano
2015-10-13 23:16 ` [PATCH 03/26] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
2015-10-13 23:16 ` [PATCH 04/26] mailinfo: move handle_boundary() lower Junio C Hamano
2015-10-13 23:16 ` [PATCH 05/26] mailinfo: get rid of function-local static states Junio C Hamano
2015-10-13 23:16 ` [PATCH 06/26] mailinfo: always pass "line" as an argument Junio C Hamano
2015-10-14 20:22   ` Stefan Beller
2015-10-14 20:27     ` Junio C Hamano
2015-10-13 23:16 ` [PATCH 07/26] mailinfo: move global "line" into mailinfo() function Junio C Hamano
2015-10-14 20:27   ` Stefan Beller
2015-10-13 23:16 ` [PATCH 08/26] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
2015-10-13 23:16 ` [PATCH 09/26] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
2015-10-13 23:16 ` [PATCH 10/26] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
2015-10-13 23:16 ` [PATCH 11/26] mailinfo: move filter/header stage " Junio C Hamano
2015-10-13 23:16 ` [PATCH 12/26] mailinfo: move patch_lines " Junio C Hamano
2015-10-13 23:16 ` [PATCH 13/26] mailinfo: move add_message_id and message_id " Junio C Hamano
2015-10-13 23:16 ` [PATCH 14/26] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
2015-10-13 23:16 ` [PATCH 15/26] mailinfo: move metainfo_charset " Junio C Hamano
2015-10-13 23:16 ` [PATCH 16/26] mailinfo: move transfer_encoding " Junio C Hamano
2015-10-13 23:16 ` [PATCH 17/26] mailinfo: move charset " Junio C Hamano
2015-10-13 23:16 ` [PATCH 18/26] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
2015-10-13 23:16 ` [PATCH 19/26] mailinfo: move cmitmsg and patchfile to struct mailinfo Junio C Hamano
2015-10-14  1:37   ` [PATCH 27/26] mailinfo: close patchfile Junio C Hamano
2015-10-14  1:46     ` [PATCH 28/26] am: make direct call to mailinfo Junio C Hamano
2015-10-13 23:16 ` [PATCH 20/26] mailinfo: move [ps]_hdr_data to struct mailinfo Junio C Hamano
2015-10-13 23:16 ` [PATCH 21/26] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
2015-10-13 23:16 ` [PATCH 22/26] mailinfo: move content/content_top to struct mailinfo Junio C Hamano
2015-10-13 23:16 ` [PATCH 23/26] mailinfo: handle errors found in decode_header() better Junio C Hamano
2015-10-13 23:16 ` [PATCH 24/26] mailinfo: handle charset conversion errors in the caller Junio C Hamano
2015-10-13 23:16 ` [PATCH 25/26] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
2015-10-13 23:16 ` [PATCH 26/26] mailinfo: libify the whole thing Junio C Hamano
2015-10-14 20:45 ` [PATCH v2 00/31] libify mailinfo and call it directly from am Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 01/31] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 02/31] mailinfo: fix for off-by-one error in boundary stack Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 03/31] mailinfo: explicitly close file handle to the patch output Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 04/31] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 05/31] mailinfo: move handle_boundary() lower Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 06/31] mailinfo: get rid of function-local static states Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 07/31] mailinfo: always pass "line" as an argument Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 08/31] mailinfo: move global "line" into mailinfo() function Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 09/31] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 10/31] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 11/31] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 12/31] mailinfo: move filter/header stage " Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 13/31] mailinfo: move patch_lines " Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 14/31] mailinfo: move add_message_id and message_id " Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 15/31] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 16/31] mailinfo: move metainfo_charset " Junio C Hamano
2015-10-15 20:47     ` Eric Sunshine
2015-10-14 20:45   ` [PATCH v2 17/31] mailinfo: move transfer_encoding " Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 18/31] mailinfo: move charset " Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 19/31] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 20/31] mailinfo: move cmitmsg and patchfile to struct mailinfo Junio C Hamano
2015-10-14 22:55     ` Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 21/31] mailinfo: move [ps]_hdr_data " Junio C Hamano
2015-10-14 22:57     ` Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 22/31] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 23/31] mailinfo: move content/content_top to struct mailinfo Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 24/31] mailinfo: move read_one_header_line() closer to its callers Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 25/31] mailinfo: move check_header() after the helpers it uses Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 26/31] mailinfo: move cleanup_space() before its users Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 27/31] mailinfo: move definition of MAX_HDR_PARSED to closer to its use Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 28/31] mailinfo: libify Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 29/31] mailinfo: handle charset conversion errors in the caller Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 30/31] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
2015-10-14 20:45   ` [PATCH v2 31/31] am: make direct call to mailinfo Junio C Hamano
2015-10-19  7:28   ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 01/34] mailinfo: remove a no-op call convert_to_utf8(it, "") Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 02/34] mailinfo: fold decode_header_bq() into decode_header() Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 03/34] mailinfo: fix an off-by-one error in the boundary stack Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 04/34] mailinfo: explicitly close file handle to the patch output Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 05/34] mailinfo: move handle_boundary() lower Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 06/34] mailinfo: get rid of function-local static states Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 07/34] mailinfo: do not let handle_body() touch global "line" directly Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 08/34] mailinfo: do not let handle_boundary() " Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 09/34] mailinfo: do not let find_boundary() " Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 10/34] mailinfo: move global "line" into mailinfo() function Junio C Hamano
2015-10-19 22:57       ` Eric Sunshine
2015-10-20  5:19         ` Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 11/34] mailinfo: introduce "struct mailinfo" to hold globals Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 12/34] mailinfo: move keep_subject & keep_non_patch_bracket to struct mailinfo Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 13/34] mailinfo: move global "FILE *fin, *fout" " Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 14/34] mailinfo: move filter/header stage " Junio C Hamano
2015-10-21 20:20       ` Stefan Beller
2015-10-19  7:28     ` [PATCH v3 15/34] mailinfo: move patch_lines " Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 16/34] mailinfo: move add_message_id and message_id " Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 17/34] mailinfo: move use_scissors and use_inbody_headers " Junio C Hamano
2015-10-21 20:24       ` Stefan Beller
2015-10-21 21:02         ` Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 18/34] mailinfo: move metainfo_charset " Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 19/34] mailinfo: move check for metainfo_charset to convert_to_utf8() Junio C Hamano
2015-10-19 22:57       ` Eric Sunshine
2015-10-19  7:28     ` [PATCH v3 20/34] mailinfo: move transfer_encoding to struct mailinfo Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 21/34] mailinfo: move charset " Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 22/34] mailinfo: move cmitmsg and patchfile " Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 23/34] mailinfo: move [ps]_hdr_data " Junio C Hamano
2015-10-21 20:30       ` Stefan Beller
2015-10-19  7:28     ` [PATCH v3 24/34] mailinfo: move content/content_top " Junio C Hamano
2015-10-21 20:36       ` Stefan Beller
2015-10-21 21:04         ` Junio C Hamano
2015-10-21 21:08           ` Stefan Beller
2015-10-19  7:28     ` [PATCH v3 25/34] mailinfo: handle_commit_msg() shouldn't be called after finding patchbreak Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 26/34] mailinfo: keep the parsed log message in a strbuf Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 27/34] mailinfo: move read_one_header_line() closer to its callers Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 28/34] mailinfo: move check_header() after the helpers it uses Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 29/34] mailinfo: move cleanup_space() before its users Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 30/34] mailinfo: move definition of MAX_HDR_PARSED closer to its use Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 31/34] mailinfo: libify Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 32/34] mailinfo: handle charset conversion errors in the caller Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 33/34] mailinfo: remove calls to exit() and die() deep in the callchain Junio C Hamano
2015-10-19  7:28     ` [PATCH v3 34/34] am: make direct call to mailinfo Junio C Hamano
2015-10-20 21:24     ` [PATCH v3 00/34] libify mailinfo and call it directly from am Junio C Hamano
2015-10-20 22:01       ` Stefan Beller
2015-10-20 22:06         ` Junio C Hamano
2015-10-20 22:08           ` Stefan Beller
2015-10-21 15:51       ` Ramsay Jones
2015-10-21 20:04         ` Johannes Sixt
2015-10-21 20:07           ` Junio C Hamano
2015-10-26 14:25           ` Johannes Schindelin
2015-10-26 18:42             ` Junio C Hamano
2015-10-21 23:15     ` [PATCH v4 00/35] " Junio C Hamano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.