summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin-mailinfo.c39
-rw-r--r--utf8.c54
-rw-r--r--utf8.h6
3 files changed, 69 insertions, 30 deletions
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index e6472293d4..507b93f6a7 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -4,6 +4,7 @@
*/
#include "cache.h"
#include "builtin.h"
+#include "utf8.h"
static FILE *cmitmsg, *patchfile, *fin, *fout;
@@ -510,40 +511,18 @@ static int decode_b_segment(char *in, char *ot, char *ep)
static void convert_to_utf8(char *line, 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);
-
- 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);
- }
+ char *out = reencode_string(line, metainfo_charset, input_charset);
+
+ if (!out) {
+ fprintf(stderr, "cannot convert from %s to %s\n",
+ input_charset, metainfo_charset);
+ *charset = 0;
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
+ strcpy(line, out);
+ free(out);
}
static int decode_header_bq(char *it)
diff --git a/utf8.c b/utf8.c
index 8fa62571aa..1eedd8b61a 100644
--- a/utf8.c
+++ b/utf8.c
@@ -276,3 +276,57 @@ void print_wrapped_text(const char *text, int indent, int indent2, int width)
}
}
}
+
+/*
+ * Given a buffer and its encoding, return it re-encoded
+ * with iconv. If the conversion fails, returns NULL.
+ */
+#ifndef NO_ICONV
+char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)
+{
+ iconv_t conv;
+ size_t insz, outsz, outalloc;
+ char *out, *outpos, *cp;
+
+ if (!in_encoding)
+ return NULL;
+ conv = iconv_open(out_encoding, in_encoding);
+ if (conv == (iconv_t) -1)
+ return NULL;
+ insz = strlen(in);
+ outsz = insz;
+ outalloc = outsz + 1; /* for terminating NUL */
+ out = xmalloc(outalloc);
+ outpos = out;
+ cp = (char *)in;
+
+ while (1) {
+ size_t cnt = iconv(conv, &cp, &insz, &outpos, &outsz);
+
+ if (cnt == -1) {
+ size_t sofar;
+ if (errno != E2BIG) {
+ free(out);
+ iconv_close(conv);
+ return NULL;
+ }
+ /* insz has remaining number of bytes.
+ * since we started outsz the same as insz,
+ * it is likely that insz is not enough for
+ * converting the rest.
+ */
+ sofar = outpos - out;
+ outalloc = sofar + insz * 2 + 32;
+ out = xrealloc(out, outalloc);
+ outpos = out + sofar;
+ outsz = outalloc - sofar - 1;
+ }
+ else {
+ *outpos = '\0';
+ break;
+ }
+ }
+ iconv_close(conv);
+ return out;
+}
+#endif
diff --git a/utf8.h b/utf8.h
index a0d7f591ad..cae2a8e665 100644
--- a/utf8.h
+++ b/utf8.h
@@ -5,4 +5,10 @@ int utf8_width(const char **start);
int is_utf8(const char *text);
void print_wrapped_text(const char *text, int indent, int indent2, int len);
+#ifndef NO_ICONV
+char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);
+#else
+#define reencode_string(a,b,c) NULL
+#endif
+
#endif