{"thread":{"id":"7248","subject":"git-mailinfo fixes/features v3","startedAt":"2007-03-14T20:12:21Z","lastAt":"2007-03-15T14:36:39Z","messageCount":8,"participants":["Don Zickus"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"37100","messageId":"11739031463211-git-send-email-dzickus@redhat.com","threadId":"7248","inReplyTo":null,"subject":"git-mailinfo fixes/features v3","fromName":"Don Zickus","fromEmail":"dzickus@redhat.com","sentAt":"2007-03-14T20:12:21Z","receivedAt":"2007-03-14T20:12:21Z","isPatch":false,"sender":{"key":"dzickus@redhat.com","avatar":null},"body":"Another round of cleanups as noticed by Junio.  \nOnly the the first two patches were touched.\n\n-coding style cleanups\n-better boundary checking\n\nCheers,\nDon\n"},{"id":"37104","messageId":"11739031472131-git-send-email-dzickus@redhat.com","threadId":"7248","inReplyTo":"11739031463211-git-send-email-dzickus@redhat.com","subject":"[PATCH 1/5] builtin-mailinfo.c infrastrcture changes","fromName":"Don Zickus","fromEmail":"dzickus@redhat.com","sentAt":"2007-03-14T20:12:22Z","receivedAt":"2007-03-14T20:12:22Z","isPatch":true,"sender":{"key":"dzickus@redhat.com","avatar":null},"body":"I am working on a project that required parsing through regular mboxes that\ndidn't necessarily have patches embedded in them.  I started by creating my\nown modified copy of git-am and working from there.  Very quickly, I noticed\ngit-mailinfo wasn't able to handle a big chunk of my email.\n\nAfter hacking up numerous solutions and running into more limitations, I\ndecided it was just easier to rewrite a big chunk of it.  The following\npatch has a bunch of fixes and features that I needed in order for me do\nwhat I wanted.\n\nNote: I'm didn't follow any email rfc papers but I don't think any of the\nchanges I did required much knowledge (besides the boundary stuff).\n\nList of major changes/fixes:\n- can't create empty patch files fix\n- empty patch files don't fail, this failure will come inside git-am\n- multipart boundaries are now handled\n- only output inbody headers if a patch exists otherwise assume those\nheaders are part of the reply and instead output the original headers\n- decode and filter base64 patches correctly\n- various other accidental fixes\n\nI believe I didn't break any existing functionality or compatibility (other\nthan what I describe above, which is really only the empty patch file).\n\nI tested this through various mailing list archives and everything seemed to\nparse correctly (a couple thousand emails).\n\nSigned-off-by: Don Zickus <dzickus@redhat.com>\n---\n builtin-mailinfo.c |  520 +++++++++++++++++++++++++++------------------------\n git-am.sh          |    4 +\n git-applymbox.sh   |    4 +\n git-quiltimport.sh |    4 +\n 4 files changed, 287 insertions(+), 245 deletions(-)\n\ndiff --git a/builtin-mailinfo.c b/builtin-mailinfo.c\nindex 766a37e..dacdf77 100644\n--- a/builtin-mailinfo.c\n+++ b/builtin-mailinfo.c\n@@ -11,19 +11,22 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;\n static int keep_subject;\n static const char *metainfo_charset;\n static char line[1000];\n-static char date[1000];\n static char name[1000];\n static char email[1000];\n-static char subject[1000];\n \n static enum  {\n \tTE_DONTCARE, TE_QP, TE_BASE64,\n } transfer_encoding;\n-static char charset[256];\n+static enum  {\n+\tTYPE_TEXT, TYPE_OTHER,\n+} message_type;\n \n-static char multipart_boundary[1000];\n-static int multipart_boundary_len;\n+static char charset[256];\n static int patch_lines;\n+static char **p_hdr_data, **s_hdr_data;\n+\n+#define MAX_HDR_PARSED 10\n+#define MAX_BOUNDARIES 5\n \n static char *sanity_check(char *name, char *email)\n {\n@@ -137,15 +140,13 @@ static int handle_from(char *in_line)\n \treturn 1;\n }\n \n-static int handle_date(char *line)\n+static int handle_header(char *line, char *data, int ofs)\n {\n-\tstrcpy(date, line);\n-\treturn 0;\n-}\n+\tif (!line || !data)\n+\t\treturn 1;\n+\n+\tstrcpy(data, line+ofs);\n \n-static int handle_subject(char *line)\n-{\n-\tstrcpy(subject, line);\n \treturn 0;\n }\n \n@@ -177,17 +178,35 @@ static int slurp_attr(const char *line, const char *name, char *attr)\n \treturn 1;\n }\n \n-static int handle_subcontent_type(char *line)\n+struct content_type {\n+\tchar *boundary;\n+\tint boundary_len;\n+};\n+\n+static struct content_type content[MAX_BOUNDARIES];\n+\n+static struct content_type *content_top = content;\n+\n+static int handle_content_type(char *line)\n {\n-\t/* We do not want to mess with boundary.  Note that we do not\n-\t * handle nested multipart.\n+\tchar boundary[256];\n+\n+\t/* the only time this return less than zero is when \n+\t   /line/ does not contain \"text/\"\n \t */\n-\tif (strcasestr(line, \"boundary=\")) {\n-\t\tfprintf(stderr, \"Not handling nested multipart message.\\n\");\n-\t\texit(1);\n+\tif (strcasestr(line, \"text/\") == NULL)\n+\t\t message_type = TYPE_OTHER;\n+\tif (slurp_attr(line, \"boundary=\", boundary + 2)) {\n+\t\tmemcpy(boundary, \"--\", 2);\n+\t\tif (content_top++ >= &content[MAX_BOUNDARIES]) {\n+\t\t\tfprintf(stderr, \"Too many boundaries to handle\\n\");\n+\t\t\texit(1);\n+\t\t}\n+\t\tcontent_top->boundary_len = strlen(boundary);\n+\t\tcontent_top->boundary = xmalloc(content_top->boundary_len+1);\n+\t\tstrcpy(content_top->boundary, boundary);\n \t}\n-\tslurp_attr(line, \"charset=\", charset);\n-\tif (*charset) {\n+\tif (slurp_attr(line, \"charset=\", charset)) {\n \t\tint i, c;\n \t\tfor (i = 0; (c = charset[i]) != 0; i++)\n \t\t\tcharset[i] = tolower(c);\n@@ -195,17 +214,6 @@ static int handle_subcontent_type(char *line)\n \treturn 0;\n }\n \n-static int handle_content_type(char *line)\n-{\n-\t*multipart_boundary = 0;\n-\tif (slurp_attr(line, \"boundary=\", multipart_boundary + 2)) {\n-\t\tmemcpy(multipart_boundary, \"--\", 2);\n-\t\tmultipart_boundary_len = strlen(multipart_boundary);\n-\t}\n-\tslurp_attr(line, \"charset=\", charset);\n-\treturn 0;\n-}\n-\n static int handle_content_transfer_encoding(char *line)\n {\n \tif (strcasestr(line, \"base64\"))\n@@ -219,7 +227,7 @@ static int handle_content_transfer_encoding(char *line)\n \n static int is_multipart_boundary(const char *line)\n {\n-\treturn (!memcmp(line, multipart_boundary, multipart_boundary_len));\n+\treturn (!memcmp(line, content_top->boundary, content_top->boundary_len));\n }\n \n static int eatspace(char *line)\n@@ -230,62 +238,6 @@ static int eatspace(char *line)\n \treturn len;\n }\n \n-#define SEEN_FROM 01\n-#define SEEN_DATE 02\n-#define SEEN_SUBJECT 04\n-#define SEEN_BOGUS_UNIX_FROM 010\n-#define SEEN_PREFIX  020\n-\n-/* First lines of body can have From:, Date:, and Subject: or empty */\n-static void handle_inbody_header(int *seen, char *line)\n-{\n-\tif (*seen & SEEN_PREFIX)\n-\t\treturn;\n-\tif (isspace(*line)) {\n-\t\tchar *cp;\n-\t\tfor (cp = line + 1; *cp; cp++) {\n-\t\t\tif (!isspace(*cp))\n-\t\t\t\tbreak;\n-\t\t}\n-\t\tif (!*cp)\n-\t\t\treturn;\n-\t}\n-\tif (!memcmp(\">From\", line, 5) && isspace(line[5])) {\n-\t\tif (!(*seen & SEEN_BOGUS_UNIX_FROM)) {\n-\t\t\t*seen |= SEEN_BOGUS_UNIX_FROM;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\tif (!memcmp(\"From:\", line, 5) && isspace(line[5])) {\n-\t\tif (!(*seen & SEEN_FROM) && handle_from(line+6)) {\n-\t\t\t*seen |= SEEN_FROM;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\tif (!memcmp(\"Date:\", line, 5) && isspace(line[5])) {\n-\t\tif (!(*seen & SEEN_DATE)) {\n-\t\t\thandle_date(line+6);\n-\t\t\t*seen |= SEEN_DATE;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\tif (!memcmp(\"Subject:\", line, 8) && isspace(line[8])) {\n-\t\tif (!(*seen & SEEN_SUBJECT)) {\n-\t\t\thandle_subject(line+9);\n-\t\t\t*seen |= SEEN_SUBJECT;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\tif (!memcmp(\"[PATCH]\", line, 7) && isspace(line[7])) {\n-\t\tif (!(*seen & SEEN_SUBJECT)) {\n-\t\t\thandle_subject(line);\n-\t\t\t*seen |= SEEN_SUBJECT;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\t*seen |= SEEN_PREFIX;\n-}\n-\n static char *cleanup_subject(char *subject)\n {\n \tif (keep_subject)\n@@ -341,57 +293,62 @@ static void cleanup_space(char *buf)\n }\n \n static void decode_header(char *it);\n-typedef int (*header_fn_t)(char *);\n-struct header_def {\n-\tconst char *name;\n-\theader_fn_t func;\n-\tint namelen;\n+static char *header[MAX_HDR_PARSED] = {\n+\t\"From\",\"Subject\",\"Date\",\n };\n \n-static void check_header(char *line, struct header_def *header)\n+static int check_header(char *line, char **hdr_data)\n {\n \tint i;\n \n-\tif (header[0].namelen <= 0) {\n-\t\tfor (i = 0; header[i].name; i++)\n-\t\t\theader[i].namelen = strlen(header[i].name);\n-\t}\n-\tfor (i = 0; header[i].name; i++) {\n-\t\tint len = header[i].namelen;\n-\t\tif (!strncasecmp(line, header[i].name, len) &&\n+\t/* search for the interesting parts */\n+\tfor (i = 0; header[i]; i++) {\n+\t\tint len = strlen(header[i]);\n+\t\tif (!hdr_data[i] &&\n+\t\t    !strncasecmp(line, header[i], len) &&\n \t\t    line[len] == ':' && isspace(line[len + 1])) {\n \t\t\t/* Unwrap inline B and Q encoding, and optionally\n \t\t\t * normalize the meta information to utf8.\n \t\t\t */\n \t\t\tdecode_header(line + len + 2);\n-\t\t\theader[i].func(line + len + 2);\n-\t\t\tbreak;\n+\t\t\thdr_data[i] = xmalloc(1000 * sizeof(char));\n+\t\t\tif (! handle_header(line, hdr_data[i], len + 2)) {\n+\t\t\t\treturn 1;\n+\t\t\t}\n \t\t}\n \t}\n-}\n \n-static void check_subheader_line(char *line)\n-{\n-\tstatic struct header_def header[] = {\n-\t\t{ \"Content-Type\", handle_subcontent_type },\n-\t\t{ \"Content-Transfer-Encoding\",\n-\t\t  handle_content_transfer_encoding },\n-\t\t{ NULL },\n-\t};\n-\tcheck_header(line, header);\n-}\n-static void check_header_line(char *line)\n-{\n-\tstatic struct header_def header[] = {\n-\t\t{ \"From\", handle_from },\n-\t\t{ \"Date\", handle_date },\n-\t\t{ \"Subject\", handle_subject },\n-\t\t{ \"Content-Type\", handle_content_type },\n-\t\t{ \"Content-Transfer-Encoding\",\n-\t\t  handle_content_transfer_encoding },\n-\t\t{ NULL },\n-\t};\n-\tcheck_header(line, header);\n+\t/* Content stuff */\n+\tif (!strncasecmp(line, \"Content-Type\", 12) &&\n+\t\tline[12] == ':' && isspace(line[12 + 1])) {\n+\t\tdecode_header(line + 12 + 2);\n+\t\tif (! handle_content_type(line)) {\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\tif (!strncasecmp(line, \"Content-Transfer-Encoding\", 25) &&\n+\t\tline[25] == ':' && isspace(line[25 + 1])) {\n+\t\tdecode_header(line + 25 + 2);\n+\t\tif (! handle_content_transfer_encoding(line)) {\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\n+\t/* for inbody stuff */\n+\tif (!memcmp(\">From\", line, 5) && isspace(line[5]))\n+\t\treturn 1;\n+\tif (!memcmp(\"[PATCH]\", line, 7) && isspace(line[7])) {\n+\t\tfor (i=0; header[i]; i++) {\n+\t\t\tif (!memcmp(\"Subject: \", header[i], 9)) {\n+\t\t\t\tif (! handle_header(line, hdr_data[i], 0)) {\n+\t\t\t\t\treturn 1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* no match */\n+\treturn 0;\n }\n \n static int is_rfc2822_header(char *line)\n@@ -647,147 +604,222 @@ static void decode_transfer_encoding(char *line)\n \t}\n }\n \n-static void handle_info(void)\n+static int handle_filter(char *line);\n+\n+static int find_boundary(void)\n {\n-\tchar *sub;\n+\twhile(fgets(line, sizeof(line), fin) != NULL) {\n+\t\tif (is_multipart_boundary(line))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int handle_boundary(void)\n+{\n+again:\n+\tif (!memcmp(line+content_top->boundary_len, \"--\", 2)) {\n+\t\t/* we hit an end boundary */\n+\t\t/* pop the current boundary off the stack */\n+\t\tfree(content_top->boundary);\n+\t\t\n+\t\t/* technically won't happen as is_multipart_boundary()\n+\t\t   will fail first.  But just in case..\n+\t\t */\n+\t\tif (content_top-- < content) {\n+\t\t\tfprintf(stderr, \"Detected mismatched boundaries, \"\n+\t\t\t\t\t\"can't recover\\n\");\n+\t\t\texit(1);\n+\t\t}\n+\t\thandle_filter(\"\\n\");\n+\n+\t\t/* skip to the next boundary */\n+\t\tif (!find_boundary())\n+\t\t\treturn 0;\n+\t\tgoto again;\n+\t}\n \n-\tsub = cleanup_subject(subject);\n-\tcleanup_space(name);\n-\tcleanup_space(date);\n-\tcleanup_space(email);\n-\tcleanup_space(sub);\n+\t/* set some defaults */\n+\ttransfer_encoding = TE_DONTCARE;\n+\tcharset[0] = 0;\n+\tmessage_type = TYPE_TEXT;\n \n-\tfprintf(fout, \"Author: %s\\nEmail: %s\\nSubject: %s\\nDate: %s\\n\\n\",\n-\t       name, email, sub, date);\n+\t/* slurp in this section's info */\n+\twhile (read_one_header_line(line, sizeof(line), fin))\n+\t\tcheck_header(line, p_hdr_data);\n+\n+\t/* eat the blank line after section info */\n+\treturn (fgets(line, sizeof(line), fin) != NULL);\n }\n \n-/* We are inside message body and have read line[] already.\n- * Spit out the commit log.\n- */\n-static int handle_commit_msg(int *seen)\n+static int handle_commit_msg(char *line)\n {\n+\tstatic int still_looking=1;\n+\n \tif (!cmitmsg)\n \t\treturn 0;\n-\tdo {\n-\t\tif (!memcmp(\"diff -\", line, 6) ||\n-\t\t    !memcmp(\"---\", line, 3) ||\n-\t\t    !memcmp(\"Index: \", line, 7))\n-\t\t\tbreak;\n-\t\tif ((multipart_boundary[0] && is_multipart_boundary(line))) {\n-\t\t\t/* We come here when the first part had only\n-\t\t\t * the commit message without any patch.  We\n-\t\t\t * pretend we have not seen this line yet, and\n-\t\t\t * go back to the loop.\n-\t\t\t */\n-\t\t\treturn 1;\n-\t\t}\n \n-\t\t/* Unwrap transfer encoding and optionally\n-\t\t * normalize the log message to UTF-8.\n-\t\t */\n-\t\tdecode_transfer_encoding(line);\n-\t\tif (metainfo_charset)\n-\t\t\tconvert_to_utf8(line, charset);\n+\tif (still_looking) {\n+\t\tchar *cp=line;\n+\t\tif (isspace(*line)) {\n+\t\t\tfor (cp = line + 1; *cp; cp++) {\n+\t\t\t\tif (!isspace(*cp))\n+\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (!*cp)\n+\t\t\t\treturn 0;\n+\t\t}\n+\t\tif ((still_looking = check_header(cp, s_hdr_data)) != 0)\n+\t\t\treturn 0;\n+\t}\n \n-\t\thandle_inbody_header(seen, line);\n-\t\tif (!(*seen & SEEN_PREFIX))\n-\t\t\tcontinue;\n+\tif (!memcmp(\"diff -\", line, 6) ||\n+\t    !memcmp(\"---\", line, 3) ||\n+\t    !memcmp(\"Index: \", line, 7)) {\n+\t\tfclose(cmitmsg);\n+\t\tcmitmsg = NULL;\n+\t\treturn 1;\n+\t}\n \n-\t\tfputs(line, cmitmsg);\n-\t} while (fgets(line, sizeof(line), fin) != NULL);\n-\tfclose(cmitmsg);\n-\tcmitmsg = NULL;\n+\tfputs(line, cmitmsg);\n \treturn 0;\n }\n \n-/* We have done the commit message and have the first\n- * line of the patch in line[].\n- */\n-static void handle_patch(void)\n+static int handle_patch(char *line)\n {\n-\tdo {\n-\t\tif (multipart_boundary[0] && is_multipart_boundary(line))\n-\t\t\tbreak;\n-\t\t/* Only unwrap transfer encoding but otherwise do not\n-\t\t * do anything.  We do *NOT* want UTF-8 conversion\n-\t\t * here; we are dealing with the user payload.\n-\t\t */\n-\t\tdecode_transfer_encoding(line);\n-\t\tfputs(line, patchfile);\n-\t\tpatch_lines++;\n-\t} while (fgets(line, sizeof(line), fin) != NULL);\n+\tfputs(line, patchfile);\n+\tpatch_lines++;\n+\treturn 0;\n }\n \n-/* multipart boundary and transfer encoding are set up for us, and we\n- * are at the end of the sub header.  do equivalent of handle_body up\n- * to the next boundary without closing patchfile --- we will expect\n- * that the first part to contain commit message and a patch, and\n- * handle other parts as pure patches.\n- */\n-static int handle_multipart_one_part(int *seen)\n+static int handle_filter(char *line)\n {\n-\tint n = 0;\n+\tstatic int filter=0;\n \n-\twhile (fgets(line, sizeof(line), fin) != NULL) {\n-\tagain:\n-\t\tn++;\n-\t\tif (is_multipart_boundary(line))\n+\t/* filter tells us which part we left off on\n+\t * a non-zero return indicates we hit a filter point\n+\t */\n+\tswitch (filter) {\n+\tcase 0:\n+\t\tif (!handle_commit_msg(line))\n \t\t\tbreak;\n-\t\tif (handle_commit_msg(seen))\n-\t\t\tgoto again;\n-\t\thandle_patch();\n-\t\tbreak;\n+\t\tfilter++;\n+\tcase 1:\n+\t\tif (!handle_patch(line))\n+\t\t\tbreak;\n+\t\tfilter++;\n+\tdefault:\n+\t\treturn 1;\n \t}\n-\tif (n == 0)\n-\t\treturn -1;\n+\n \treturn 0;\n }\n \n-static void handle_multipart_body(void)\n+static void handle_body(void)\n {\n-\tint seen = 0;\n-\tint part_num = 0;\n+\tint rc=0;\n+\tstatic char newline[2000];\n+\tstatic char *np=newline;\n \n \t/* Skip up to the first boundary */\n-\twhile (fgets(line, sizeof(line), fin) != NULL)\n-\t\tif (is_multipart_boundary(line)) {\n-\t\t\tpart_num = 1;\n+\tif (content_top->boundary) {\n+\t\tif (!find_boundary())\n+\t\t\treturn;\n+\t}\n+\n+\tdo {\n+\t\t/* process any boundary lines */\n+\t\tif (content_top->boundary && is_multipart_boundary(line)) {\n+\t\t\t/* flush any leftover */\n+\t\t\tif ((transfer_encoding == TE_BASE64)  &&\n+\t\t\t    (np != newline)) {\n+\t\t\t\thandle_filter(newline);\n+\t\t\t}\n+\t\t\tif (!handle_boundary())\n+\t\t\t\treturn;\n+\t\t}\n+\n+\t\t/* Unwrap transfer encoding and optionally\n+\t\t * normalize the log message to UTF-8.\n+\t\t */\n+\t\tdecode_transfer_encoding(line);\n+\t\tif (metainfo_charset)\n+\t\t\tconvert_to_utf8(line, charset);\n+\n+\t\tswitch (transfer_encoding) {\n+\t\tcase TE_BASE64:\n+\t\t{\n+\t\t\tchar *op=line;\n+\n+\t\t\t/* binary data most likely doesn't have newlines */\n+\t\t\tif (message_type != TYPE_TEXT) {\n+\t\t\t\trc=handle_filter(line);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\t/* this is a decoded line that may contain\n+\t\t\t * multiple new lines.  Pass only one chunk\n+\t\t\t * at a time to handle_filter()\n+\t\t\t */\n+\n+\t\t\tdo {\n+\t\t\t\twhile (*op != '\\n' && *op != 0)\n+\t\t\t\t\t*np++ = *op++;\n+\t\t\t\t*np = *op;\n+\t\t\t\tif (*np != 0) {\n+\t\t\t\t\t/* should be sitting on a new line */\n+\t\t\t\t\t*(++np) = 0;\n+\t\t\t\t\top++;\n+\t\t\t\t\trc=handle_filter(newline);\n+\t\t\t\t\tnp=newline;\n+\t\t\t\t}\n+\t\t\t} while (*op != 0);\n+\t\t\t/* the partial chunk is saved in newline and\n+\t\t\t * will be appended by the next iteration of fgets\n+\t\t\t */\n \t\t\tbreak;\n \t\t}\n-\tif (!part_num)\n-\t\treturn;\n-\t/* We are on boundary line.  Start slurping the subhead. */\n-\twhile (1) {\n-\t\tint hdr = read_one_header_line(line, sizeof(line), fin);\n-\t\tif (!hdr) {\n-\t\t\tif (handle_multipart_one_part(&seen) < 0)\n-\t\t\t\treturn;\n-\t\t\t/* Reset per part headers */\n-\t\t\ttransfer_encoding = TE_DONTCARE;\n-\t\t\tcharset[0] = 0;\n+\t\tdefault:\n+\t\t\trc=handle_filter(line);\n \t\t}\n-\t\telse\n-\t\t\tcheck_subheader_line(line);\n-\t}\n-\tfclose(patchfile);\n-\tif (!patch_lines) {\n-\t\tfprintf(stderr, \"No patch found\\n\");\n-\t\texit(1);\n-\t}\n+\t\tif (rc)\n+\t\t\t/* nothing left to filter */\n+\t\t\tbreak;\n+\t} while (fgets(line, sizeof(line), fin));\n+\n+\treturn;\n }\n \n-/* Non multipart message */\n-static void handle_body(void)\n+static void handle_info(void)\n {\n-\tint seen = 0;\n-\n-\thandle_commit_msg(&seen);\n-\thandle_patch();\n-\tfclose(patchfile);\n-\tif (!patch_lines) {\n-\t\tfprintf(stderr, \"No patch found\\n\");\n-\t\texit(1);\n+\tchar *sub;\n+\tchar *hdr;\n+\tint i;\n+\n+\tfor (i=0; header[i]; i++) {\n+\n+\t\t/* only print inbody headers if we output a patch file */\n+\t\tif (patch_lines && s_hdr_data[i])\n+\t\t\thdr=s_hdr_data[i];\n+\t\telse if (p_hdr_data[i])\n+\t\t\thdr=p_hdr_data[i];\n+\t\telse\n+\t\t\tcontinue;\n+\n+\t\tif (!memcmp(header[i], \"Subject\", 7)) {\n+\t\t\tsub = cleanup_subject(hdr);\n+\t\t\tcleanup_space(sub);\n+\t\t\tfprintf(fout, \"Subject: %s\\n\", sub);\n+\t\t} else if (!memcmp(header[i], \"From\", 4)) {\n+\t\t\thandle_from(hdr);\n+\t\t\tfprintf(fout, \"Author: %s\\n\", name);\n+\t\t\tfprintf(fout, \"Email: %s\\n\", email);\n+\t\t} else {\n+\t\t\tcleanup_space(hdr);\n+\t\t\tfprintf(fout, \"%s: %s\\n\", header[i], hdr);\n+\t\t}\n \t}\n+\tfprintf(fout, \"\\n\");\n }\n \n int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,\n@@ -809,18 +841,16 @@ int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,\n \t\tfclose(cmitmsg);\n \t\treturn -1;\n \t}\n-\twhile (1) {\n-\t\tint hdr = read_one_header_line(line, sizeof(line), fin);\n-\t\tif (!hdr) {\n-\t\t\tif (multipart_boundary[0])\n-\t\t\t\thandle_multipart_body();\n-\t\t\telse\n-\t\t\t\thandle_body();\n-\t\t\thandle_info();\n-\t\t\tbreak;\n-\t\t}\n-\t\tcheck_header_line(line);\n-\t}\n+\n+\tp_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));\n+\ts_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));\n+\n+\t/* process the email header */\n+\twhile (read_one_header_line(line, sizeof(line), fin))\n+\t\tcheck_header(line, p_hdr_data);\n+\n+\thandle_body();\n+\thandle_info();\n \n \treturn 0;\n }\ndiff --git a/git-am.sh b/git-am.sh\nindex 2c73d11..847a44f 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -290,6 +290,10 @@ do\n \t\tgit-mailinfo $keep $utf8 \"$dotest/msg\" \"$dotest/patch\" \\\n \t\t\t<\"$dotest/$msgnum\" >\"$dotest/info\" ||\n \t\t\tstop_here $this\n+\t\ttest -s $dotest/patch || { \n+\t\t\techo \"Patch is empty.  Was is split wrong?\"\n+\t\t\tstop_here $this\n+\t\t}\n \t\tgit-stripspace < \"$dotest/msg\" > \"$dotest/msg-clean\"\n \t\t;;\n \tesac\ndiff --git a/git-applymbox.sh b/git-applymbox.sh\nindex 1f68599..2cbdc7e 100755\n--- a/git-applymbox.sh\n+++ b/git-applymbox.sh\n@@ -77,6 +77,10 @@ do\n     *)\n \t    git-mailinfo $keep_subject $utf8 \\\n \t\t.dotest/msg .dotest/patch <$i >.dotest/info || exit 1\n+\t    test -s $dotest/patch || {\n+\t\techo \"Patch is empty.  Was is split wrong?\"\n+\t\tstop_here $this\n+\t    }\n \t    git-stripspace < .dotest/msg > .dotest/msg-clean\n \t    ;;\n     esac\ndiff --git a/git-quiltimport.sh b/git-quiltimport.sh\nindex 671a5ff..08ac9bb 100755\n--- a/git-quiltimport.sh\n+++ b/git-quiltimport.sh\n@@ -73,6 +73,10 @@ mkdir $tmp_dir || exit 2\n for patch_name in $(cat \"$QUILT_PATCHES/series\" | grep -v '^#'); do\n \techo $patch_name\n \t(cat $QUILT_PATCHES/$patch_name | git-mailinfo \"$tmp_msg\" \"$tmp_patch\" > \"$tmp_info\") || exit 3\n+\ttest -s $dotest/patch || {\n+\t\techo \"Patch is empty.  Was is split wrong?\"\n+\t\tstop_here $this\n+\t}\n \n \t# Parse the author information\n \texport GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' \"$tmp_info\")\n-- \n1.5.0.2.211.g2ca9-dirty\n"},{"id":"37103","messageId":"11739031471426-git-send-email-dzickus@redhat.com","threadId":"7248","inReplyTo":"11739031463211-git-send-email-dzickus@redhat.com","subject":"[PATCH 2/5] add the ability to select more email header fields to output","fromName":"Don Zickus","fromEmail":"dzickus@redhat.com","sentAt":"2007-03-14T20:12:23Z","receivedAt":"2007-03-14T20:12:23Z","isPatch":true,"sender":{"key":"dzickus@redhat.com","avatar":null},"body":"This is useful when scripts need more than just the basic email headers to\nparse.  By specifying the \"-x=\" option, one can search and output any header\nfield they want.\n\nSigned-off-by: Don Zickus <dzickus@redhat.com>\n---\n builtin-mailinfo.c |   10 ++++++++--\n 1 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin-mailinfo.c b/builtin-mailinfo.c\nindex dacdf77..dd0f563 100644\n--- a/builtin-mailinfo.c\n+++ b/builtin-mailinfo.c\n@@ -856,11 +856,12 @@ int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,\n }\n \n static const char mailinfo_usage[] =\n-\t\"git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info\";\n+\t\"git-mailinfo [-k] [-u | --encoding=<encoding>] [-x=<field>] msg patch <mail >info\";\n \n int cmd_mailinfo(int argc, const char **argv, const char *prefix)\n {\n \tconst char *def_charset;\n+\tint top;\n \n \t/* NEEDSWORK: might want to do the optional .git/ directory\n \t * discovery\n@@ -870,6 +871,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)\n \tdef_charset = (git_commit_encoding ? git_commit_encoding : \"utf-8\");\n \tmetainfo_charset = def_charset;\n \n+\tfor (top=0; header[top]; top++){ ; }\n+\n \twhile (1 < argc && argv[1][0] == '-') {\n \t\tif (!strcmp(argv[1], \"-k\"))\n \t\t\tkeep_subject = 1;\n@@ -879,7 +882,10 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)\n \t\t\tmetainfo_charset = NULL;\n \t\telse if (!prefixcmp(argv[1], \"--encoding=\"))\n \t\t\tmetainfo_charset = argv[1] + 11;\n-\t\telse\n+\t\telse if (!prefixcmp(argv[1], \"-x=\")) {\n+\t\t\theader[top] = xmalloc(256*sizeof(char));\n+\t\t\tstrncpy(header[top++], argv[1]+3, 256);\n+\t\t} else\n \t\t\tusage(mailinfo_usage);\n \t\targc--; argv++;\n \t}\n-- \n1.5.0.2.211.g2ca9-dirty\n"},{"id":"37102","messageId":"1173903147792-git-send-email-dzickus@redhat.com","threadId":"7248","inReplyTo":"11739031463211-git-send-email-dzickus@redhat.com","subject":"[PATCH 3/5] restrict the patch filtering","fromName":"Don Zickus","fromEmail":"dzickus@redhat.com","sentAt":"2007-03-14T20:12:24Z","receivedAt":"2007-03-14T20:12:24Z","isPatch":true,"sender":{"key":"dzickus@redhat.com","avatar":null},"body":"I have come across many emails that use long strings of '-'s as separators\nfor ideas.  This patch below limits the separator to only 3 '-', with the\nintent that long string of '-'s will stay in the commit msg and not in the\npatch file.\n\nSigned-off-by: Don Zickus <dzickus@redhat.com>\nAcked-by: Linus Torvalds <torvalds@linux-foundation.org>\n\n---\nI purposedly separated this patch out because I wasn't sure if anyone would\nhave objections to it.  I tested it on numerous emails with and with patches\nand didn't see any issues.\n---\n builtin-mailinfo.c |   37 ++++++++++++++++++++++++++++++++++---\n 1 files changed, 34 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin-mailinfo.c b/builtin-mailinfo.c\nindex dd0f563..a8d5b60 100644\n--- a/builtin-mailinfo.c\n+++ b/builtin-mailinfo.c\n@@ -652,6 +652,39 @@ again:\n \treturn (fgets(line, sizeof(line), fin) != NULL);\n }\n \n+static inline int patchbreak(const char *line)\n+{\n+\t/* Beginning of a \"diff -\" header? */\n+\tif (!memcmp(\"diff -\", line, 6))\n+\t\treturn 1;\n+\n+\t/* CVS \"Index: \" line? */\n+\tif (!memcmp(\"Index: \", line, 7))\n+\t\treturn 1;\n+\n+\t/*\n+\t * \"--- <filename>\" starts patches without headers\n+\t * \"---<sp>*\" is a manual separator\n+\t */\n+\tif (!memcmp(\"---\", line, 3)) {\n+\t\tline += 3;\n+\t\t/* space followed by a filename? */\n+\t\tif (line[0] == ' ' && !isspace(line[1]))\n+\t\t\treturn 1;\n+\t\t/* Just whitespace? */\n+\t\tfor (;;) {\n+\t\t\tunsigned char c = *line++;\n+\t\t\tif (c == '\\n')\n+\t\t\t\treturn 1;\n+\t\t\tif (!isspace(c))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\treturn 0;\n+}\n+\n+\n static int handle_commit_msg(char *line)\n {\n \tstatic int still_looking=1;\n@@ -673,9 +706,7 @@ static int handle_commit_msg(char *line)\n \t\t\treturn 0;\n \t}\n \n-\tif (!memcmp(\"diff -\", line, 6) ||\n-\t    !memcmp(\"---\", line, 3) ||\n-\t    !memcmp(\"Index: \", line, 7)) {\n+\tif (patchbreak(line)) {\n \t\tfclose(cmitmsg);\n \t\tcmitmsg = NULL;\n \t\treturn 1;\n-- \n1.5.0.2.211.g2ca9-dirty\n"},{"id":"37101","messageId":"11739031473249-git-send-email-dzickus@redhat.com","threadId":"7248","inReplyTo":"11739031463211-git-send-email-dzickus@redhat.com","subject":"[PATCH 4/5] Add a couple more test cases to the suite.","fromName":"Don Zickus","fromEmail":"dzickus@redhat.com","sentAt":"2007-03-14T20:12:25Z","receivedAt":"2007-03-14T20:12:25Z","isPatch":true,"sender":{"key":"dzickus@redhat.com","avatar":null},"body":"They handle cases where there is no attached patch.\n\nSigned-off-by: Don Zickus <dzickus@redhat.com>\n---\n t/t5100-mailinfo.sh |    2 +-\n t/t5100/info0007    |    5 +++++\n t/t5100/info0008    |    5 +++++\n t/t5100/msg0007     |    2 ++\n t/t5100/msg0008     |    4 ++++\n t/t5100/sample.mbox |   18 ++++++++++++++++++\n 6 files changed, 35 insertions(+), 1 deletions(-)\n create mode 100644 t/t5100/info0007\n create mode 100644 t/t5100/info0008\n create mode 100644 t/t5100/msg0007\n create mode 100644 t/t5100/msg0008\n create mode 100644 t/t5100/patch0007\n create mode 100644 t/t5100/patch0008\n\ndiff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh\nindex 4d2b781..ca96918 100755\n--- a/t/t5100-mailinfo.sh\n+++ b/t/t5100-mailinfo.sh\n@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \\\n \t'git-mailsplit -o. ../t5100/sample.mbox >last &&\n \tlast=`cat last` &&\n \techo total is $last &&\n-\ttest `cat last` = 6'\n+\ttest `cat last` = 8'\n \n for mail in `echo 00*`\n do\ndiff --git a/t/t5100/info0007 b/t/t5100/info0007\nnew file mode 100644\nindex 0000000..49bb0fe\n--- /dev/null\n+++ b/t/t5100/info0007\n@@ -0,0 +1,5 @@\n+Author: A U Thor\n+Email: a.u.thor@example.com\n+Subject: another patch\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+\ndiff --git a/t/t5100/info0008 b/t/t5100/info0008\nnew file mode 100644\nindex 0000000..e8a2951\n--- /dev/null\n+++ b/t/t5100/info0008\n@@ -0,0 +1,5 @@\n+Author: Junio C Hamano\n+Email: junio@kernel.org\n+Subject: another patch\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+\ndiff --git a/t/t5100/msg0007 b/t/t5100/msg0007\nnew file mode 100644\nindex 0000000..71b23c0\n--- /dev/null\n+++ b/t/t5100/msg0007\n@@ -0,0 +1,2 @@\n+Here is an empty patch from A U Thor.\n+\ndiff --git a/t/t5100/msg0008 b/t/t5100/msg0008\nnew file mode 100644\nindex 0000000..a80ecb9\n--- /dev/null\n+++ b/t/t5100/msg0008\n@@ -0,0 +1,4 @@\n+>Here is an empty patch from A U Thor.\n+\n+Hey you forgot the patch!\n+\ndiff --git a/t/t5100/patch0007 b/t/t5100/patch0007\nnew file mode 100644\nindex 0000000..e69de29\ndiff --git a/t/t5100/patch0008 b/t/t5100/patch0008\nnew file mode 100644\nindex 0000000..e69de29\ndiff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox\nindex 86bfc27..b80c981 100644\n--- a/t/t5100/sample.mbox\n+++ b/t/t5100/sample.mbox\n@@ -386,3 +386,21 @@ index 9123cdc..918dcf8 100644\n -- \n 1.4.0.g6f2b\n \n+From nobody Mon Sep 17 00:00:00 2001\n+From: A U Thor <a.u.thor@example.com>\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+Subject: [PATCH] another patch\n+\n+Here is an empty patch from A U Thor.\n+\n+From nobody Mon Sep 17 00:00:00 2001\n+From: Junio C Hamano <junio@kernel.org>\n+Date: Fri, 9 Jun 2006 00:44:16 -0700\n+Subject: re: [PATCH] another patch\n+\n+From: A U Thor <a.u.thor@example.com>\n+Subject: [PATCH] another patch\n+>Here is an empty patch from A U Thor.\n+\n+Hey you forgot the patch!\n+\n-- \n1.5.0.2.211.g2ca9-dirty\n"},{"id":"37105","messageId":"11739031471626-git-send-email-dzickus@redhat.com","threadId":"7248","inReplyTo":"11739031463211-git-send-email-dzickus@redhat.com","subject":"[PATCH 5/5] fix a utf8 issue in t5100/patch005","fromName":"Don Zickus","fromEmail":"dzickus@redhat.com","sentAt":"2007-03-14T20:12:26Z","receivedAt":"2007-03-14T20:12:26Z","isPatch":true,"sender":{"key":"dzickus@redhat.com","avatar":null},"body":"This issue popped up when testing my changes.  I believe the patch is the\nintended output that git-mailinfo should provide.\n\nSigned-off-by: Don Zickus <dzickus@redhat.com>\n---\n t/t5100/patch0005 |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/t/t5100/patch0005 b/t/t5100/patch0005\nindex 7d24b24..e7d6f66 100644\n--- a/t/t5100/patch0005\n+++ b/t/t5100/patch0005\n@@ -61,7 +61,7 @@ diff --git a/git-cvsimport-script b/git-cvsimport-script\n  \t\tpush(@old,$fn);\n \n -- \n-David Kågedal\n+David KÃ¥gedal\n -\n To unsubscribe from this list: send the line \"unsubscribe git\" in\n the body of a message to majordomo@vger.kernel.org\n-- \n1.5.0.2.211.g2ca9-dirty\n"},{"id":"37163","messageId":"20070315143535.GB11029@redhat.com","threadId":"7248","inReplyTo":"11739031472131-git-send-email-dzickus@redhat.com","subject":"Re: [PATCH 1/5] builtin-mailinfo.c infrastrcture changes","fromName":"Don Zickus","fromEmail":"dzickus@redhat.com","sentAt":"2007-03-15T14:35:35Z","receivedAt":"2007-03-15T14:35:35Z","isPatch":true,"sender":{"key":"dzickus@redhat.com","avatar":null},"body":"I am working on a project that required parsing through regular mboxes that\ndidn't necessarily have patches embedded in them.  I started by creating my\nown modified copy of git-am and working from there.  Very quickly, I noticed\ngit-mailinfo wasn't able to handle a big chunk of my email.\n\nAfter hacking up numerous solutions and running into more limitations, I\ndecided it was just easier to rewrite a big chunk of it.  The following\npatch has a bunch of fixes and features that I needed in order for me do\nwhat I wanted.\n\nNote: I'm didn't follow any email rfc papers but I don't think any of the\nchanges I did required much knowledge (besides the boundary stuff).\n\nList of major changes/fixes:\n- can't create empty patch files fix\n- empty patch files don't fail, this failure will come inside git-am\n- multipart boundaries are now handled\n- only output inbody headers if a patch exists otherwise assume those\nheaders are part of the reply and instead output the original headers\n- decode and filter base64 patches correctly\n- various other accidental fixes\n\nI believe I didn't break any existing functionality or compatibility (other\nthan what I describe above, which is really only the empty patch file).\n\nI tested this through various mailing list archives and everything seemed to\nparse correctly (a couple thousand emails).\n\nSigned-off-by: Don Zickus <dzickus@redhat.com>\n---\n\nAccidentally sent out the wrong patch yesterday..\n\n---\n builtin-mailinfo.c |  520 +++++++++++++++++++++++++++------------------------\n git-am.sh          |    4 +\n git-applymbox.sh   |    4 +\n git-quiltimport.sh |    4 +\n 4 files changed, 287 insertions(+), 245 deletions(-)\n\ndiff --git a/builtin-mailinfo.c b/builtin-mailinfo.c\nindex 766a37e..a5eea82 100644\n--- a/builtin-mailinfo.c\n+++ b/builtin-mailinfo.c\n@@ -11,19 +11,22 @@ static FILE *cmitmsg, *patchfile, *fin, *fout;\n static int keep_subject;\n static const char *metainfo_charset;\n static char line[1000];\n-static char date[1000];\n static char name[1000];\n static char email[1000];\n-static char subject[1000];\n \n static enum  {\n \tTE_DONTCARE, TE_QP, TE_BASE64,\n } transfer_encoding;\n-static char charset[256];\n+static enum  {\n+\tTYPE_TEXT, TYPE_OTHER,\n+} message_type;\n \n-static char multipart_boundary[1000];\n-static int multipart_boundary_len;\n+static char charset[256];\n static int patch_lines;\n+static char **p_hdr_data, **s_hdr_data;\n+\n+#define MAX_HDR_PARSED 10\n+#define MAX_BOUNDARIES 5\n \n static char *sanity_check(char *name, char *email)\n {\n@@ -137,15 +140,13 @@ static int handle_from(char *in_line)\n \treturn 1;\n }\n \n-static int handle_date(char *line)\n+static int handle_header(char *line, char *data, int ofs)\n {\n-\tstrcpy(date, line);\n-\treturn 0;\n-}\n+\tif (!line || !data)\n+\t\treturn 1;\n+\n+\tstrcpy(data, line+ofs);\n \n-static int handle_subject(char *line)\n-{\n-\tstrcpy(subject, line);\n \treturn 0;\n }\n \n@@ -177,17 +178,35 @@ static int slurp_attr(const char *line, const char *name, char *attr)\n \treturn 1;\n }\n \n-static int handle_subcontent_type(char *line)\n+struct content_type {\n+\tchar *boundary;\n+\tint boundary_len;\n+};\n+\n+static struct content_type content[MAX_BOUNDARIES];\n+\n+static struct content_type *content_top = content;\n+\n+static int handle_content_type(char *line)\n {\n-\t/* We do not want to mess with boundary.  Note that we do not\n-\t * handle nested multipart.\n+\tchar boundary[256];\n+\n+\t/* the only time this return less than zero is when \n+\t   /line/ does not contain \"text/\"\n \t */\n-\tif (strcasestr(line, \"boundary=\")) {\n-\t\tfprintf(stderr, \"Not handling nested multipart message.\\n\");\n-\t\texit(1);\n+\tif (strcasestr(line, \"text/\") == NULL)\n+\t\t message_type = TYPE_OTHER;\n+\tif (slurp_attr(line, \"boundary=\", boundary + 2)) {\n+\t\tmemcpy(boundary, \"--\", 2);\n+\t\tif (content_top++ >= &content[MAX_BOUNDARIES]) {\n+\t\t\tfprintf(stderr, \"Too many boundaries to handle\\n\");\n+\t\t\texit(1);\n+\t\t}\n+\t\tcontent_top->boundary_len = strlen(boundary);\n+\t\tcontent_top->boundary = xmalloc(content_top->boundary_len+1);\n+\t\tstrcpy(content_top->boundary, boundary);\n \t}\n-\tslurp_attr(line, \"charset=\", charset);\n-\tif (*charset) {\n+\tif (slurp_attr(line, \"charset=\", charset)) {\n \t\tint i, c;\n \t\tfor (i = 0; (c = charset[i]) != 0; i++)\n \t\t\tcharset[i] = tolower(c);\n@@ -195,17 +214,6 @@ static int handle_subcontent_type(char *line)\n \treturn 0;\n }\n \n-static int handle_content_type(char *line)\n-{\n-\t*multipart_boundary = 0;\n-\tif (slurp_attr(line, \"boundary=\", multipart_boundary + 2)) {\n-\t\tmemcpy(multipart_boundary, \"--\", 2);\n-\t\tmultipart_boundary_len = strlen(multipart_boundary);\n-\t}\n-\tslurp_attr(line, \"charset=\", charset);\n-\treturn 0;\n-}\n-\n static int handle_content_transfer_encoding(char *line)\n {\n \tif (strcasestr(line, \"base64\"))\n@@ -219,7 +227,7 @@ static int handle_content_transfer_encoding(char *line)\n \n static int is_multipart_boundary(const char *line)\n {\n-\treturn (!memcmp(line, multipart_boundary, multipart_boundary_len));\n+\treturn (!memcmp(line, content_top->boundary, content_top->boundary_len));\n }\n \n static int eatspace(char *line)\n@@ -230,62 +238,6 @@ static int eatspace(char *line)\n \treturn len;\n }\n \n-#define SEEN_FROM 01\n-#define SEEN_DATE 02\n-#define SEEN_SUBJECT 04\n-#define SEEN_BOGUS_UNIX_FROM 010\n-#define SEEN_PREFIX  020\n-\n-/* First lines of body can have From:, Date:, and Subject: or empty */\n-static void handle_inbody_header(int *seen, char *line)\n-{\n-\tif (*seen & SEEN_PREFIX)\n-\t\treturn;\n-\tif (isspace(*line)) {\n-\t\tchar *cp;\n-\t\tfor (cp = line + 1; *cp; cp++) {\n-\t\t\tif (!isspace(*cp))\n-\t\t\t\tbreak;\n-\t\t}\n-\t\tif (!*cp)\n-\t\t\treturn;\n-\t}\n-\tif (!memcmp(\">From\", line, 5) && isspace(line[5])) {\n-\t\tif (!(*seen & SEEN_BOGUS_UNIX_FROM)) {\n-\t\t\t*seen |= SEEN_BOGUS_UNIX_FROM;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\tif (!memcmp(\"From:\", line, 5) && isspace(line[5])) {\n-\t\tif (!(*seen & SEEN_FROM) && handle_from(line+6)) {\n-\t\t\t*seen |= SEEN_FROM;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\tif (!memcmp(\"Date:\", line, 5) && isspace(line[5])) {\n-\t\tif (!(*seen & SEEN_DATE)) {\n-\t\t\thandle_date(line+6);\n-\t\t\t*seen |= SEEN_DATE;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\tif (!memcmp(\"Subject:\", line, 8) && isspace(line[8])) {\n-\t\tif (!(*seen & SEEN_SUBJECT)) {\n-\t\t\thandle_subject(line+9);\n-\t\t\t*seen |= SEEN_SUBJECT;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\tif (!memcmp(\"[PATCH]\", line, 7) && isspace(line[7])) {\n-\t\tif (!(*seen & SEEN_SUBJECT)) {\n-\t\t\thandle_subject(line);\n-\t\t\t*seen |= SEEN_SUBJECT;\n-\t\t\treturn;\n-\t\t}\n-\t}\n-\t*seen |= SEEN_PREFIX;\n-}\n-\n static char *cleanup_subject(char *subject)\n {\n \tif (keep_subject)\n@@ -341,57 +293,62 @@ static void cleanup_space(char *buf)\n }\n \n static void decode_header(char *it);\n-typedef int (*header_fn_t)(char *);\n-struct header_def {\n-\tconst char *name;\n-\theader_fn_t func;\n-\tint namelen;\n+static char *header[MAX_HDR_PARSED] = {\n+\t\"From\",\"Subject\",\"Date\",\n };\n \n-static void check_header(char *line, struct header_def *header)\n+static int check_header(char *line, char **hdr_data)\n {\n \tint i;\n \n-\tif (header[0].namelen <= 0) {\n-\t\tfor (i = 0; header[i].name; i++)\n-\t\t\theader[i].namelen = strlen(header[i].name);\n-\t}\n-\tfor (i = 0; header[i].name; i++) {\n-\t\tint len = header[i].namelen;\n-\t\tif (!strncasecmp(line, header[i].name, len) &&\n+\t/* search for the interesting parts */\n+\tfor (i = 0; header[i]; i++) {\n+\t\tint len = strlen(header[i]);\n+\t\tif (!hdr_data[i] &&\n+\t\t    !strncasecmp(line, header[i], len) &&\n \t\t    line[len] == ':' && isspace(line[len + 1])) {\n \t\t\t/* Unwrap inline B and Q encoding, and optionally\n \t\t\t * normalize the meta information to utf8.\n \t\t\t */\n \t\t\tdecode_header(line + len + 2);\n-\t\t\theader[i].func(line + len + 2);\n-\t\t\tbreak;\n+\t\t\thdr_data[i] = xmalloc(1000 * sizeof(char));\n+\t\t\tif (! handle_header(line, hdr_data[i], len + 2)) {\n+\t\t\t\treturn 1;\n+\t\t\t}\n \t\t}\n \t}\n-}\n \n-static void check_subheader_line(char *line)\n-{\n-\tstatic struct header_def header[] = {\n-\t\t{ \"Content-Type\", handle_subcontent_type },\n-\t\t{ \"Content-Transfer-Encoding\",\n-\t\t  handle_content_transfer_encoding },\n-\t\t{ NULL },\n-\t};\n-\tcheck_header(line, header);\n-}\n-static void check_header_line(char *line)\n-{\n-\tstatic struct header_def header[] = {\n-\t\t{ \"From\", handle_from },\n-\t\t{ \"Date\", handle_date },\n-\t\t{ \"Subject\", handle_subject },\n-\t\t{ \"Content-Type\", handle_content_type },\n-\t\t{ \"Content-Transfer-Encoding\",\n-\t\t  handle_content_transfer_encoding },\n-\t\t{ NULL },\n-\t};\n-\tcheck_header(line, header);\n+\t/* Content stuff */\n+\tif (!strncasecmp(line, \"Content-Type\", 12) &&\n+\t\tline[12] == ':' && isspace(line[12 + 1])) {\n+\t\tdecode_header(line + 12 + 2);\n+\t\tif (! handle_content_type(line)) {\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\tif (!strncasecmp(line, \"Content-Transfer-Encoding\", 25) &&\n+\t\tline[25] == ':' && isspace(line[25 + 1])) {\n+\t\tdecode_header(line + 25 + 2);\n+\t\tif (! handle_content_transfer_encoding(line)) {\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\n+\t/* for inbody stuff */\n+\tif (!memcmp(\">From\", line, 5) && isspace(line[5]))\n+\t\treturn 1;\n+\tif (!memcmp(\"[PATCH]\", line, 7) && isspace(line[7])) {\n+\t\tfor (i = 0; header[i]; i++) {\n+\t\t\tif (!memcmp(\"Subject: \", header[i], 9)) {\n+\t\t\t\tif (! handle_header(line, hdr_data[i], 0)) {\n+\t\t\t\t\treturn 1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* no match */\n+\treturn 0;\n }\n \n static int is_rfc2822_header(char *line)\n@@ -647,147 +604,222 @@ static void decode_transfer_encoding(char *line)\n \t}\n }\n \n-static void handle_info(void)\n+static int handle_filter(char *line);\n+\n+static int find_boundary(void)\n {\n-\tchar *sub;\n+\twhile(fgets(line, sizeof(line), fin) != NULL) {\n+\t\tif (is_multipart_boundary(line))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int handle_boundary(void)\n+{\n+again:\n+\tif (!memcmp(line+content_top->boundary_len, \"--\", 2)) {\n+\t\t/* we hit an end boundary */\n+\t\t/* pop the current boundary off the stack */\n+\t\tfree(content_top->boundary);\n+\t\t\n+\t\t/* technically won't happen as is_multipart_boundary()\n+\t\t   will fail first.  But just in case..\n+\t\t */\n+\t\tif (content_top-- < content) {\n+\t\t\tfprintf(stderr, \"Detected mismatched boundaries, \"\n+\t\t\t\t\t\"can't recover\\n\");\n+\t\t\texit(1);\n+\t\t}\n+\t\thandle_filter(\"\\n\");\n+\n+\t\t/* skip to the next boundary */\n+\t\tif (!find_boundary())\n+\t\t\treturn 0;\n+\t\tgoto again;\n+\t}\n \n-\tsub = cleanup_subject(subject);\n-\tcleanup_space(name);\n-\tcleanup_space(date);\n-\tcleanup_space(email);\n-\tcleanup_space(sub);\n+\t/* set some defaults */\n+\ttransfer_encoding = TE_DONTCARE;\n+\tcharset[0] = 0;\n+\tmessage_type = TYPE_TEXT;\n \n-\tfprintf(fout, \"Author: %s\\nEmail: %s\\nSubject: %s\\nDate: %s\\n\\n\",\n-\t       name, email, sub, date);\n+\t/* slurp in this section's info */\n+\twhile (read_one_header_line(line, sizeof(line), fin))\n+\t\tcheck_header(line, p_hdr_data);\n+\n+\t/* eat the blank line after section info */\n+\treturn (fgets(line, sizeof(line), fin) != NULL);\n }\n \n-/* We are inside message body and have read line[] already.\n- * Spit out the commit log.\n- */\n-static int handle_commit_msg(int *seen)\n+static int handle_commit_msg(char *line)\n {\n+\tstatic int still_looking=1;\n+\n \tif (!cmitmsg)\n \t\treturn 0;\n-\tdo {\n-\t\tif (!memcmp(\"diff -\", line, 6) ||\n-\t\t    !memcmp(\"---\", line, 3) ||\n-\t\t    !memcmp(\"Index: \", line, 7))\n-\t\t\tbreak;\n-\t\tif ((multipart_boundary[0] && is_multipart_boundary(line))) {\n-\t\t\t/* We come here when the first part had only\n-\t\t\t * the commit message without any patch.  We\n-\t\t\t * pretend we have not seen this line yet, and\n-\t\t\t * go back to the loop.\n-\t\t\t */\n-\t\t\treturn 1;\n-\t\t}\n \n-\t\t/* Unwrap transfer encoding and optionally\n-\t\t * normalize the log message to UTF-8.\n-\t\t */\n-\t\tdecode_transfer_encoding(line);\n-\t\tif (metainfo_charset)\n-\t\t\tconvert_to_utf8(line, charset);\n+\tif (still_looking) {\n+\t\tchar *cp=line;\n+\t\tif (isspace(*line)) {\n+\t\t\tfor (cp = line + 1; *cp; cp++) {\n+\t\t\t\tif (!isspace(*cp))\n+\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (!*cp)\n+\t\t\t\treturn 0;\n+\t\t}\n+\t\tif ((still_looking = check_header(cp, s_hdr_data)) != 0)\n+\t\t\treturn 0;\n+\t}\n \n-\t\thandle_inbody_header(seen, line);\n-\t\tif (!(*seen & SEEN_PREFIX))\n-\t\t\tcontinue;\n+\tif (!memcmp(\"diff -\", line, 6) ||\n+\t    !memcmp(\"---\", line, 3) ||\n+\t    !memcmp(\"Index: \", line, 7)) {\n+\t\tfclose(cmitmsg);\n+\t\tcmitmsg = NULL;\n+\t\treturn 1;\n+\t}\n \n-\t\tfputs(line, cmitmsg);\n-\t} while (fgets(line, sizeof(line), fin) != NULL);\n-\tfclose(cmitmsg);\n-\tcmitmsg = NULL;\n+\tfputs(line, cmitmsg);\n \treturn 0;\n }\n \n-/* We have done the commit message and have the first\n- * line of the patch in line[].\n- */\n-static void handle_patch(void)\n+static int handle_patch(char *line)\n {\n-\tdo {\n-\t\tif (multipart_boundary[0] && is_multipart_boundary(line))\n-\t\t\tbreak;\n-\t\t/* Only unwrap transfer encoding but otherwise do not\n-\t\t * do anything.  We do *NOT* want UTF-8 conversion\n-\t\t * here; we are dealing with the user payload.\n-\t\t */\n-\t\tdecode_transfer_encoding(line);\n-\t\tfputs(line, patchfile);\n-\t\tpatch_lines++;\n-\t} while (fgets(line, sizeof(line), fin) != NULL);\n+\tfputs(line, patchfile);\n+\tpatch_lines++;\n+\treturn 0;\n }\n \n-/* multipart boundary and transfer encoding are set up for us, and we\n- * are at the end of the sub header.  do equivalent of handle_body up\n- * to the next boundary without closing patchfile --- we will expect\n- * that the first part to contain commit message and a patch, and\n- * handle other parts as pure patches.\n- */\n-static int handle_multipart_one_part(int *seen)\n+static int handle_filter(char *line)\n {\n-\tint n = 0;\n+\tstatic int filter=0;\n \n-\twhile (fgets(line, sizeof(line), fin) != NULL) {\n-\tagain:\n-\t\tn++;\n-\t\tif (is_multipart_boundary(line))\n+\t/* filter tells us which part we left off on\n+\t * a non-zero return indicates we hit a filter point\n+\t */\n+\tswitch (filter) {\n+\tcase 0:\n+\t\tif (!handle_commit_msg(line))\n \t\t\tbreak;\n-\t\tif (handle_commit_msg(seen))\n-\t\t\tgoto again;\n-\t\thandle_patch();\n-\t\tbreak;\n+\t\tfilter++;\n+\tcase 1:\n+\t\tif (!handle_patch(line))\n+\t\t\tbreak;\n+\t\tfilter++;\n+\tdefault:\n+\t\treturn 1;\n \t}\n-\tif (n == 0)\n-\t\treturn -1;\n+\n \treturn 0;\n }\n \n-static void handle_multipart_body(void)\n+static void handle_body(void)\n {\n-\tint seen = 0;\n-\tint part_num = 0;\n+\tint rc=0;\n+\tstatic char newline[2000];\n+\tstatic char *np=newline;\n \n \t/* Skip up to the first boundary */\n-\twhile (fgets(line, sizeof(line), fin) != NULL)\n-\t\tif (is_multipart_boundary(line)) {\n-\t\t\tpart_num = 1;\n+\tif (content_top->boundary) {\n+\t\tif (!find_boundary())\n+\t\t\treturn;\n+\t}\n+\n+\tdo {\n+\t\t/* process any boundary lines */\n+\t\tif (content_top->boundary && is_multipart_boundary(line)) {\n+\t\t\t/* flush any leftover */\n+\t\t\tif ((transfer_encoding == TE_BASE64)  &&\n+\t\t\t    (np != newline)) {\n+\t\t\t\thandle_filter(newline);\n+\t\t\t}\n+\t\t\tif (!handle_boundary())\n+\t\t\t\treturn;\n+\t\t}\n+\n+\t\t/* Unwrap transfer encoding and optionally\n+\t\t * normalize the log message to UTF-8.\n+\t\t */\n+\t\tdecode_transfer_encoding(line);\n+\t\tif (metainfo_charset)\n+\t\t\tconvert_to_utf8(line, charset);\n+\n+\t\tswitch (transfer_encoding) {\n+\t\tcase TE_BASE64:\n+\t\t{\n+\t\t\tchar *op=line;\n+\n+\t\t\t/* binary data most likely doesn't have newlines */\n+\t\t\tif (message_type != TYPE_TEXT) {\n+\t\t\t\trc=handle_filter(line);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\t/* this is a decoded line that may contain\n+\t\t\t * multiple new lines.  Pass only one chunk\n+\t\t\t * at a time to handle_filter()\n+\t\t\t */\n+\n+\t\t\tdo {\n+\t\t\t\twhile (*op != '\\n' && *op != 0)\n+\t\t\t\t\t*np++ = *op++;\n+\t\t\t\t*np = *op;\n+\t\t\t\tif (*np != 0) {\n+\t\t\t\t\t/* should be sitting on a new line */\n+\t\t\t\t\t*(++np) = 0;\n+\t\t\t\t\top++;\n+\t\t\t\t\trc=handle_filter(newline);\n+\t\t\t\t\tnp=newline;\n+\t\t\t\t}\n+\t\t\t} while (*op != 0);\n+\t\t\t/* the partial chunk is saved in newline and\n+\t\t\t * will be appended by the next iteration of fgets\n+\t\t\t */\n \t\t\tbreak;\n \t\t}\n-\tif (!part_num)\n-\t\treturn;\n-\t/* We are on boundary line.  Start slurping the subhead. */\n-\twhile (1) {\n-\t\tint hdr = read_one_header_line(line, sizeof(line), fin);\n-\t\tif (!hdr) {\n-\t\t\tif (handle_multipart_one_part(&seen) < 0)\n-\t\t\t\treturn;\n-\t\t\t/* Reset per part headers */\n-\t\t\ttransfer_encoding = TE_DONTCARE;\n-\t\t\tcharset[0] = 0;\n+\t\tdefault:\n+\t\t\trc=handle_filter(line);\n \t\t}\n-\t\telse\n-\t\t\tcheck_subheader_line(line);\n-\t}\n-\tfclose(patchfile);\n-\tif (!patch_lines) {\n-\t\tfprintf(stderr, \"No patch found\\n\");\n-\t\texit(1);\n-\t}\n+\t\tif (rc)\n+\t\t\t/* nothing left to filter */\n+\t\t\tbreak;\n+\t} while (fgets(line, sizeof(line), fin));\n+\n+\treturn;\n }\n \n-/* Non multipart message */\n-static void handle_body(void)\n+static void handle_info(void)\n {\n-\tint seen = 0;\n-\n-\thandle_commit_msg(&seen);\n-\thandle_patch();\n-\tfclose(patchfile);\n-\tif (!patch_lines) {\n-\t\tfprintf(stderr, \"No patch found\\n\");\n-\t\texit(1);\n+\tchar *sub;\n+\tchar *hdr;\n+\tint i;\n+\n+\tfor (i = 0; header[i]; i++) {\n+\n+\t\t/* only print inbody headers if we output a patch file */\n+\t\tif (patch_lines && s_hdr_data[i])\n+\t\t\thdr=s_hdr_data[i];\n+\t\telse if (p_hdr_data[i])\n+\t\t\thdr=p_hdr_data[i];\n+\t\telse\n+\t\t\tcontinue;\n+\n+\t\tif (!memcmp(header[i], \"Subject\", 7)) {\n+\t\t\tsub = cleanup_subject(hdr);\n+\t\t\tcleanup_space(sub);\n+\t\t\tfprintf(fout, \"Subject: %s\\n\", sub);\n+\t\t} else if (!memcmp(header[i], \"From\", 4)) {\n+\t\t\thandle_from(hdr);\n+\t\t\tfprintf(fout, \"Author: %s\\n\", name);\n+\t\t\tfprintf(fout, \"Email: %s\\n\", email);\n+\t\t} else {\n+\t\t\tcleanup_space(hdr);\n+\t\t\tfprintf(fout, \"%s: %s\\n\", header[i], hdr);\n+\t\t}\n \t}\n+\tfprintf(fout, \"\\n\");\n }\n \n int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,\n@@ -809,18 +841,16 @@ int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,\n \t\tfclose(cmitmsg);\n \t\treturn -1;\n \t}\n-\twhile (1) {\n-\t\tint hdr = read_one_header_line(line, sizeof(line), fin);\n-\t\tif (!hdr) {\n-\t\t\tif (multipart_boundary[0])\n-\t\t\t\thandle_multipart_body();\n-\t\t\telse\n-\t\t\t\thandle_body();\n-\t\t\thandle_info();\n-\t\t\tbreak;\n-\t\t}\n-\t\tcheck_header_line(line);\n-\t}\n+\n+\tp_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));\n+\ts_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *));\n+\n+\t/* process the email header */\n+\twhile (read_one_header_line(line, sizeof(line), fin))\n+\t\tcheck_header(line, p_hdr_data);\n+\n+\thandle_body();\n+\thandle_info();\n \n \treturn 0;\n }\ndiff --git a/git-am.sh b/git-am.sh\nindex 2c73d11..847a44f 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -290,6 +290,10 @@ do\n \t\tgit-mailinfo $keep $utf8 \"$dotest/msg\" \"$dotest/patch\" \\\n \t\t\t<\"$dotest/$msgnum\" >\"$dotest/info\" ||\n \t\t\tstop_here $this\n+\t\ttest -s $dotest/patch || { \n+\t\t\techo \"Patch is empty.  Was is split wrong?\"\n+\t\t\tstop_here $this\n+\t\t}\n \t\tgit-stripspace < \"$dotest/msg\" > \"$dotest/msg-clean\"\n \t\t;;\n \tesac\ndiff --git a/git-applymbox.sh b/git-applymbox.sh\nindex 1f68599..2cbdc7e 100755\n--- a/git-applymbox.sh\n+++ b/git-applymbox.sh\n@@ -77,6 +77,10 @@ do\n     *)\n \t    git-mailinfo $keep_subject $utf8 \\\n \t\t.dotest/msg .dotest/patch <$i >.dotest/info || exit 1\n+\t    test -s $dotest/patch || {\n+\t\techo \"Patch is empty.  Was is split wrong?\"\n+\t\tstop_here $this\n+\t    }\n \t    git-stripspace < .dotest/msg > .dotest/msg-clean\n \t    ;;\n     esac\ndiff --git a/git-quiltimport.sh b/git-quiltimport.sh\nindex 671a5ff..08ac9bb 100755\n--- a/git-quiltimport.sh\n+++ b/git-quiltimport.sh\n@@ -73,6 +73,10 @@ mkdir $tmp_dir || exit 2\n for patch_name in $(cat \"$QUILT_PATCHES/series\" | grep -v '^#'); do\n \techo $patch_name\n \t(cat $QUILT_PATCHES/$patch_name | git-mailinfo \"$tmp_msg\" \"$tmp_patch\" > \"$tmp_info\") || exit 3\n+\ttest -s $dotest/patch || {\n+\t\techo \"Patch is empty.  Was is split wrong?\"\n+\t\tstop_here $this\n+\t}\n \n \t# Parse the author information\n \texport GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' \"$tmp_info\")\n-- \n1.5.0.2.213.g18c8-dirty\n"},{"id":"37164","messageId":"20070315143639.GC11029@redhat.com","threadId":"7248","inReplyTo":"11739031471426-git-send-email-dzickus@redhat.com","subject":"Re: [PATCH 2/5] add the ability to select more email header fields to output","fromName":"Don Zickus","fromEmail":"dzickus@redhat.com","sentAt":"2007-03-15T14:36:39Z","receivedAt":"2007-03-15T14:36:39Z","isPatch":true,"sender":{"key":"dzickus@redhat.com","avatar":null},"body":"This is useful when scripts need more than just the basic email headers to\nparse.  By specifying the \"-x=\" option, one can search and output any header\nfield they want.\n\nSigned-off-by: Don Zickus <dzickus@redhat.com>\n---\n\n Accidentally sent out the wrong patch yesterday.\n\n---\n builtin-mailinfo.c |   22 ++++++++++++++++------\n 1 files changed, 16 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin-mailinfo.c b/builtin-mailinfo.c\nindex a5eea82..8ac6ef4 100644\n--- a/builtin-mailinfo.c\n+++ b/builtin-mailinfo.c\n@@ -302,7 +302,7 @@ static int check_header(char *line, char **hdr_data)\n \tint i;\n \n \t/* search for the interesting parts */\n-\tfor (i = 0; header[i]; i++) {\n+\tfor (i = 0; header[i] && i < MAX_HDR_PARSED; i++) {\n \t\tint len = strlen(header[i]);\n \t\tif (!hdr_data[i] &&\n \t\t    !strncasecmp(line, header[i], len) &&\n@@ -338,8 +338,8 @@ static int check_header(char *line, char **hdr_data)\n \tif (!memcmp(\">From\", line, 5) && isspace(line[5]))\n \t\treturn 1;\n \tif (!memcmp(\"[PATCH]\", line, 7) && isspace(line[7])) {\n-\t\tfor (i = 0; header[i]; i++) {\n-\t\t\tif (!memcmp(\"Subject: \", header[i], 9)) {\n+\t\tfor (i = 0; header[i] && i < MAX_HDR_PARSED; i++) {\n+\t\t\tif (!memcmp(\"Subject\", header[i], 7)) {\n \t\t\t\tif (! handle_header(line, hdr_data[i], 0)) {\n \t\t\t\t\treturn 1;\n \t\t\t\t}\n@@ -796,7 +796,7 @@ static void handle_info(void)\n \tchar *hdr;\n \tint i;\n \n-\tfor (i = 0; header[i]; i++) {\n+\tfor (i = 0; header[i] && i < MAX_HDR_PARSED; i++) {\n \n \t\t/* only print inbody headers if we output a patch file */\n \t\tif (patch_lines && s_hdr_data[i])\n@@ -856,11 +856,12 @@ int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,\n }\n \n static const char mailinfo_usage[] =\n-\t\"git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info\";\n+\t\"git-mailinfo [-k] [-u | --encoding=<encoding>] [-x=<field>] msg patch <mail >info\";\n \n int cmd_mailinfo(int argc, const char **argv, const char *prefix)\n {\n \tconst char *def_charset;\n+\tint top;\n \n \t/* NEEDSWORK: might want to do the optional .git/ directory\n \t * discovery\n@@ -870,6 +871,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)\n \tdef_charset = (git_commit_encoding ? git_commit_encoding : \"utf-8\");\n \tmetainfo_charset = def_charset;\n \n+\tfor (top = 0; header[top] && top < MAX_HDR_PARSED; top++){ ; }\n+\n \twhile (1 < argc && argv[1][0] == '-') {\n \t\tif (!strcmp(argv[1], \"-k\"))\n \t\t\tkeep_subject = 1;\n@@ -879,7 +882,14 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)\n \t\t\tmetainfo_charset = NULL;\n \t\telse if (!prefixcmp(argv[1], \"--encoding=\"))\n \t\t\tmetainfo_charset = argv[1] + 11;\n-\t\telse\n+\t\telse if (!prefixcmp(argv[1], \"-x=\")) {\n+\t\t\tif (top >= MAX_HDR_PARSED) {\n+\t\t\t\tfprintf(stderr, \"too many headers to parse\\n\");\n+\t\t\t\texit(1);\n+\t\t\t}\n+\t\t\theader[top] = xmalloc(256*sizeof(char));\n+\t\t\tstrncpy(header[top++], argv[1]+3, 256);\n+\t\t} else\n \t\t\tusage(mailinfo_usage);\n \t\targc--; argv++;\n \t}\n-- \n1.5.0.2.213.g18c8-dirty\n"}]}