From b45974a655e0e41441e5db64c091000171435096 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 23 Dec 2006 23:36:55 -0800
Subject: [PATCH] Move encoding conversion routine out of mailinfo to utf8.c

This moves the body of convert_to_utf8() routine used in mailinfo
to the utf8.c i18n library.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-mailinfo.c | 39 ++++++++-------------------------
 utf8.c             | 54 ++++++++++++++++++++++++++++++++++++++++++++++
 utf8.h             |  6 ++++++
 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
-- 
2.48.1