* Another stupid program, this one parsing the headers of an
* email to figure out authorship and subject
*/
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#ifndef NO_ICONV
-#include <iconv.h>
-#endif
-#include "git-compat-util.h"
#include "cache.h"
#include "builtin.h"
+#include "utf8.h"
static FILE *cmitmsg, *patchfile, *fin, *fout;
*/
int ch;
char *cp = line;
+
+ /* Count mbox From headers as headers */
+ if (!memcmp(line, "From ", 5) || !memcmp(line, ">From ", 6))
+ return 1;
+
while ((ch = *cp++)) {
if (ch == ':')
return cp != line;
return 0;
}
+/*
+ * sz is size of 'line' buffer in bytes. Must be reasonably
+ * long enough to hold one physical real-world e-mail line.
+ */
static int read_one_header_line(char *line, int sz, FILE *in)
{
- int ofs = 0;
- while (ofs < sz) {
- int peek, len;
- if (fgets(line + ofs, sz - ofs, in) == NULL)
- break;
- len = eatspace(line + ofs);
- if ((len == 0) || !is_rfc2822_header(line)) {
- /* Re-add the newline */
- line[ofs + len] = '\n';
- line[ofs + len + 1] = '\0';
- break;
- }
- ofs += len;
- /* Yuck, 2822 header "folding" */
+ int len;
+
+ /*
+ * We will read at most (sz-1) bytes and then potentially
+ * re-add NUL after it. Accessing line[sz] after this is safe
+ * and we can allow len to grow up to and including sz.
+ */
+ sz--;
+
+ /* Get the first part of the line. */
+ if (!fgets(line, sz, in))
+ return 0;
+
+ /*
+ * Is it an empty line or not a valid rfc2822 header?
+ * If so, stop here, and return false ("not a header")
+ */
+ len = eatspace(line);
+ if (!len || !is_rfc2822_header(line)) {
+ /* Re-add the newline */
+ line[len] = '\n';
+ line[len + 1] = '\0';
+ return 0;
+ }
+
+ /*
+ * Now we need to eat all the continuation lines..
+ * Yuck, 2822 header "folding"
+ */
+ for (;;) {
+ int peek, addlen;
+ static char continuation[1000];
+
peek = fgetc(in); ungetc(peek, in);
if (peek != ' ' && peek != '\t')
break;
+ if (!fgets(continuation, sizeof(continuation), in))
+ break;
+ addlen = eatspace(continuation);
+ if (len < sz - 1) {
+ if (addlen >= sz - len)
+ addlen = sz - len - 1;
+ memcpy(line + len, continuation, addlen);
+ len += addlen;
+ }
}
- /* Count mbox From headers as headers */
- if (!ofs && (!memcmp(line, "From ", 5) || !memcmp(line, ">From ", 6)))
- ofs = 1;
- return ofs;
-}
+ line[len] = 0;
-static unsigned hexval(int c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- return ~0;
+ return 1;
}
static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047)
return 0;
}
-static void convert_to_utf8(char *line, char *charset)
+static void convert_to_utf8(char *line, const char *charset)
{
-#ifndef NO_ICONV
- char *in, *out;
- size_t insize, outsize, nrc;
- char outbuf[4096]; /* cheat */
- static char latin_one[] = "latin1";
- char *input_charset = *charset ? charset : latin_one;
- iconv_t conv = iconv_open(metainfo_charset, input_charset);
+ static const char latin_one[] = "latin1";
+ const char *input_charset = *charset ? charset : latin_one;
+ char *out = reencode_string(line, metainfo_charset, input_charset);
- if (conv == (iconv_t) -1) {
- static int warned_latin1_once = 0;
- if (input_charset != latin_one) {
- fprintf(stderr, "cannot convert from %s to %s\n",
- input_charset, metainfo_charset);
- *charset = 0;
- }
- else if (!warned_latin1_once) {
- warned_latin1_once = 1;
- fprintf(stderr, "tried to convert from %s to %s, "
- "but your iconv does not work with it.\n",
- input_charset, metainfo_charset);
- }
- return;
- }
- in = line;
- insize = strlen(in);
- out = outbuf;
- outsize = sizeof(outbuf);
- nrc = iconv(conv, &in, &insize, &out, &outsize);
- iconv_close(conv);
- if (nrc == (size_t) -1)
- return;
- *out = 0;
- strcpy(line, outbuf);
-#endif
+ if (!out)
+ die("cannot convert from %s to %s\n",
+ input_charset, metainfo_charset);
+ strcpy(line, out);
+ free(out);
}
static int decode_header_bq(char *it)
int cmd_mailinfo(int argc, const char **argv, const char *prefix)
{
+ const char *def_charset;
+
/* NEEDSWORK: might want to do the optional .git/ directory
* discovery
*/
git_config(git_default_config);
+ def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8");
+ metainfo_charset = def_charset;
+
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "-k"))
keep_subject = 1;
else if (!strcmp(argv[1], "-u"))
- metainfo_charset = git_commit_encoding;
- else if (!strncmp(argv[1], "--encoding=", 11))
+ metainfo_charset = def_charset;
+ else if (!strcmp(argv[1], "-n"))
+ metainfo_charset = NULL;
+ else if (!prefixcmp(argv[1], "--encoding="))
metainfo_charset = argv[1] + 11;
else
usage(mailinfo_usage);