Index: gmime/gmime-charset.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-charset.c,v
retrieving revision 1.14
diff -u -r1.14 gmime-charset.c
--- gmime/gmime-charset.c	30 Dec 2002 16:37:56 -0000	1.14
+++ gmime/gmime-charset.c	31 Mar 2004 14:46:14 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -37,8 +37,6 @@
 #include "gmime-charset-map-private.h"
 #include "gmime-charset.h"
 
-#include <glib.h>
-
 #ifdef HAVE_ICONV_DETECT_H
 #include "iconv-detect.h"
 #else /* use old-style detection */
@@ -57,12 +55,17 @@
 #define ICONV_10646 "iso-10646"
 #endif /* USE_ICONV_DETECT */
 
+
+/* a useful website on charset alaises:
+ * http://www.li18nux.org/subgroups/sa/locnameguide/v1.1draft/CodesetAliasTable-V11.html */
+
 struct {
 	char *charset;
 	char *iconv_name;
 } known_iconv_charsets[] = {
 	/* charset name, iconv-friendly name (sometimes case sensitive) */
 	{ "utf-8",           "UTF-8"      },
+	{ "utf8",            "UTF-8"      },
 	
 	/* ANSI_X3.4-1968 is used on some systems and should be
 	   treated the same as US-ASCII */
@@ -79,26 +82,58 @@
 	{ "iso10646",        "UCS-2BE"    },
 	
 	{ "ks_c_5601-1987",  "EUC-KR"     },
+	{ "5601",            "EUC-KR"     },
+	{ "ksc-5601",        "EUC-KR"     },
+	{ "ksc-5601-1987",   "EUC-KR"     },
+	{ "ksc-5601_1987",   "EUC-KR"     },
+	{ "euckr-0",         "EUC-KR"     },
 	
 	/* FIXME: Japanese/Korean/Chinese stuff needs checking */
-	{ "euckr-0",         "EUC-KR"     },
 	{ "big5-0",          "BIG5"       },
 	{ "big5.eten-0",     "BIG5"       },
-	{ "big5hkscs-0",     "BIG5HKCS"   },
+	{ "big5hkscs-0",     "BIG5HKSCS"  },
 	{ "gb2312-0",        "gb2312"     },
 	{ "gb2312.1980-0",   "gb2312"     },
+	{ "euc-cn",          "gb2312"     },
 	{ "gb18030-0",       "gb18030"    },
 	{ "gbk-0",           "GBK"        },
 	
-	{ "eucjp-0",         "eucJP"      },
-	{ "ujis-0",          "ujis"       },
+	{ "eucjp-0",         "eucJP"  	  },  /* should this map to "EUC-JP" instead? */
+	{ "ujis-0",          "ujis"  	  },  /* we might want to map this to EUC-JP */
 	{ "jisx0208.1983-0", "SJIS"       },
 	{ "jisx0212.1990-0", "SJIS"       },
+	{ "pck",	     "SJIS"       },
 	{ NULL,              NULL         }
 };
 
+/* map CJKR charsets to their language code */
+/* NOTE: only support charset names that will be returned by
+ * g_mime_charset_iconv_name() so that we don't have to keep track of
+ * all the aliases too. */
+static struct {
+	char *charset;
+	char *lang;
+} cjkr_lang_map[] = {
+	{ "Big5",        "zh" },
+	{ "BIG5HKSCS",   "zh" },
+	{ "gb2312",      "zh" },
+	{ "gb18030",     "zh" },
+	{ "gbk",         "zh" },
+	{ "euc-tw",      "zh" },
+	{ "iso-2022-jp", "ja" },
+	{ "Shift-JIS",   "ja" },
+	{ "sjis",        "ja" },
+	{ "ujis",        "ja" },
+	{ "eucJP",       "ja" },
+	{ "euc-jp",      "ja" },
+	{ "euc-kr",      "ko" },
+	{ "koi8-r",      "ru" },
+	{ "koi8-u",      "uk" }
+};
+
 static GHashTable *iconv_charsets = NULL;
 static char *locale_charset = NULL;
+static char *locale_lang = NULL;
 
 #ifdef G_THREADS_ENABLED
 static GStaticMutex charset_lock = G_STATIC_MUTEX_INIT;
@@ -117,12 +152,68 @@
 	g_free (value);
 }
 
-static void
-g_mime_charset_shutdown (void)
+
+/**
+ * g_mime_charset_map_shutdown:
+ *
+ * Frees internal lookup tables created in #g_mime_charset_map_init().
+ **/
+void
+g_mime_charset_map_shutdown (void)
 {
+	if (!iconv_charsets)
+		return;
+	
 	g_hash_table_foreach (iconv_charsets, iconv_charset_free, NULL);
 	g_hash_table_destroy (iconv_charsets);
+	iconv_charsets = NULL;
+	
 	g_free (locale_charset);
+	locale_charset = NULL;
+	
+	g_free (locale_lang);
+	locale_lang = NULL;
+}
+
+
+static void
+locale_parse_lang (const char *locale)
+{
+	char *codeset, *lang;
+	
+	if ((codeset = strchr (locale, '.')))
+		lang = g_strndup (locale, codeset - locale);
+	else
+		lang = g_strdup (locale);
+	
+	/* validate the language */
+	if (strlen (lang) >= 2) {
+		if (lang[2] == '-' || lang[2] == '_') {
+			/* canonicalise the lang */
+			lang[0] = g_ascii_tolower (lang[0]);
+			lang[1] = g_ascii_tolower (lang[1]);
+			
+			/* validate the country code */
+			if (strlen (lang + 3) > 2) {
+				/* invalid country code */
+				lang[2] = '\0';
+			} else {
+				lang[2] = '-';
+				lang[3] = g_ascii_toupper (lang[3]);
+				lang[4] = g_ascii_toupper (lang[4]);
+			}
+		} else if (lang[2] != '\0') {
+			/* invalid language */
+			g_free (lang);
+			lang = NULL;
+		}
+		
+		locale_lang = lang;
+	} else {
+		/* invalid language */
+		locale_lang = NULL;
+		g_free (lang);
+	}
 }
 
 
@@ -130,7 +221,7 @@
  * g_mime_charset_map_init:
  *
  * Initializes the locale charset variable for later calls to
- * gmime_charset_locale_name. Only really needs to be called for non-
+ * #g_mime_locale_charset(). Only really needs to be called for non-
  * iso-8859-1 locales.
  **/
 void
@@ -145,16 +236,18 @@
 	iconv_charsets = g_hash_table_new (g_str_hash, g_str_equal);
 	
 	for (i = 0; known_iconv_charsets[i].charset != NULL; i++) {
-		charset = g_strdup (known_iconv_charsets[i].charset);
+		charset = g_ascii_strdown (known_iconv_charsets[i].charset, -1);
 		iconv_name = g_strdup (known_iconv_charsets[i].iconv_name);
-		g_strdown (charset);
 		g_hash_table_insert (iconv_charsets, charset, iconv_name);
 	}
 	
 #ifdef HAVE_CODESET
-	locale_charset = g_strdup (nl_langinfo (CODESET));
-	g_strdown (locale_charset);
-#else
+	if ((locale_charset = nl_langinfo (CODESET)) && locale_charset[0])
+		locale_charset = g_ascii_strdown (locale_charset, -1);
+	else
+		locale_charset = NULL;
+#endif
+	
 	locale = setlocale (LC_ALL, NULL);
 	
 	if (!locale || !strcmp (locale, "C") || !strcmp (locale, "POSIX")) {
@@ -163,6 +256,7 @@
 		 * set.  */
 		
 		locale_charset = NULL;
+		locale_lang = NULL;
 	} else {
 		/* A locale name is typically of  the  form  language[_terri-
 		 * tory][.codeset][@modifier],  where  language is an ISO 639
@@ -172,34 +266,37 @@
 		 */
 		char *codeset, *p;
 		
-		codeset = strchr (locale, '.');
-		if (codeset) {
-			codeset++;
-			
-			/* ; is a hack for debian systems and / is a hack for Solaris systems */
-			for (p = codeset; *p && !strchr ("@;/", *p); p++);
-			locale_charset = g_strndup (codeset, (unsigned) (p - codeset));
-			g_strdown (locale_charset);
-		} else {
-			/* charset unknown */
-			locale_charset = NULL;
+		if (!locale_charset) {
+			codeset = strchr (locale, '.');
+			if (codeset) {
+				codeset++;
+				
+				/* ; is a hack for debian systems and / is a hack for Solaris systems */
+				p = codeset;
+				while (*p && !strchr ("@;/", *p))
+					p++;
+				
+				locale_charset = g_ascii_strdown (codeset, p - codeset);
+			} else {
+				/* charset unknown */
+				locale_charset = NULL;
+			}
 		}
+		
+		locale_parse_lang (locale);
 	}
-#endif
-	
-	g_atexit (g_mime_charset_shutdown);
 }
 
 
 /**
- * g_mime_charset_locale_name:
+ * g_mime_locale_charset:
  *
  * Gets the user's locale charset (or iso-8859-1 by default).
  *
  * Returns the user's locale charset (or iso-8859-1 by default).
  **/
 const char *
-g_mime_charset_locale_name (void)
+g_mime_locale_charset (void)
 {
 	CHARSET_LOCK ();
 	if (!iconv_charsets)
@@ -211,7 +308,68 @@
 
 
 /**
- * g_mime_charset_name:
+ * g_mime_locale_language:
+ *
+ * Gets the user's locale language code (or %NULL by default).
+ *
+ * Returns the user's locale language code (or %NULL by default).
+ **/
+const char *
+g_mime_locale_language (void)
+{
+	CHARSET_LOCK ();
+	if (!iconv_charsets)
+		g_mime_charset_map_init ();
+	CHARSET_UNLOCK ();
+	
+	return locale_lang;
+}
+
+
+/**
+ * g_mime_charset_language:
+ * @charset: charset name
+ *
+ * Attempts to find a specific language code that is specific to
+ * @charset. Currently only handles CJK and Russian/Ukranian
+ * charset->lang mapping. Everything else will return %NULL.
+ *
+ * Returns a language code that is specific to @charset, or NULL on
+ * fail.
+ **/
+const char *
+g_mime_charset_language (const char *charset)
+{
+	int i;
+	
+	if (!charset)
+		return NULL;
+	
+	for (i = 0; i < G_N_ELEMENTS (cjkr_lang_map); i++) {
+		if (!strcasecmp (cjkr_lang_map[i].charset, charset))
+			return cjkr_lang_map[i].lang;
+	}
+	
+	return NULL;
+}
+
+
+static const char *
+strdown (char *str)
+{
+	register char *s = str;
+	
+	while (*s) {
+		if (*s >= 'A' && *s <= 'Z')
+			*s += 0x20;
+		s++;
+	}
+	
+	return str;
+}
+
+/**
+ * g_mime_charset_iconv_name:
  * @charset: charset name
  *
  * Attempts to find an iconv-friendly charset name for @charset.
@@ -219,7 +377,7 @@
  * Returns an iconv-friendly charset name for @charset.
  **/
 const char *
-g_mime_charset_name (const char *charset)
+g_mime_charset_iconv_name (const char *charset)
 {
 	char *name, *iconv_name, *buf;
 	
@@ -228,7 +386,7 @@
 	
 	name = g_alloca (strlen (charset) + 1);
 	strcpy (name, charset);
-	g_strdown (name);
+	strdown (name);
 	
 	CHARSET_LOCK ();
 	if (!iconv_charsets)
@@ -304,6 +462,190 @@
 }
 
 
+static const char *iso_charsets[] = {
+	"us-ascii",
+	"iso-8859-1",
+	"iso-8859-2",
+	"iso-8859-3",
+	"iso-8859-4",
+	"iso-8859-5",
+	"iso-8859-6",
+	"iso-8859-7",
+	"iso-8859-8",
+	"iso-8859-9",
+	"iso-8859-10",
+	"iso-8859-11",
+	"iso-8859-12",
+	"iso-8859-13",
+	"iso-8859-14",
+	"iso-8859-15",
+	"iso-8859-16"
+};
+
+static const char *windows_charsets[] = {
+	"windows-cp1250",
+	"windows-cp1251",
+	"windows-cp1252",
+	"windows-cp1253",
+	"windows-cp1254",
+	"windows-cp1255",
+	"windows-cp1256",
+	"windows-cp1257",
+	"windows-cp1258",
+	"windows-cp1259"
+};
+
+
+/**
+ * g_mime_charset_canon_name:
+ * @charset: charset name
+ *
+ * Attempts to find a canonical charset name for @charset.
+ *
+ * Note: Will normally return the same value as
+ * #g_mime_charset_iconv_name() unless the system iconv does not use
+ * the canonical ISO charset names (such as using ISO8859-1 rather
+ * than the canonical form ISO-8859-1).
+ *
+ * Returns a canonical charset name for @charset.
+ **/
+const char *
+g_mime_charset_canon_name (const char *charset)
+{
+	const char *ptr;
+	char *endptr;
+	int iso;
+	
+	if (!charset)
+		return NULL;
+	
+	charset = g_mime_charset_iconv_name (charset);
+	if (strncasecmp (charset, "iso", 3) == 0) {
+		ptr = charset + 3;
+		if (*ptr == '-' || *ptr == '_')
+			ptr++;
+		
+		if (strncmp (ptr, "8859", 4) != 0)
+			return charset;
+		
+		ptr += 4;
+		if (*ptr == '-' || *ptr == '_')
+			ptr++;
+		
+		iso = strtoul (ptr, &endptr, 10);
+		if (endptr == ptr || *endptr != '\0')
+			return charset;
+		
+		if (iso > G_N_ELEMENTS (iso_charsets))
+			return charset;
+		
+		return iso_charsets[iso];
+	} else if (!strncmp (charset, "CP125", 5)) {
+		ptr = charset + 5;
+		if (*ptr >= '0' && *ptr <= '9')
+			return windows_charsets[*ptr - '0'];
+	}
+	
+	return charset;
+}
+
+
+/**
+ * g_mime_charset_name:
+ * @charset: charset name
+ *
+ * Attempts to find an iconv-friendly charset name for @charset.
+ *
+ * Note: This function is deprecated. Use #g_mime_charset_iconv_name
+ * instead.
+ *
+ * Returns an iconv-friendly charset name for @charset.
+ **/
+const char *
+g_mime_charset_name (const char *charset)
+{
+	return g_mime_charset_iconv_name (charset);
+}
+
+
+/**
+ * g_mime_charset_locale_name:
+ *
+ * Gets the user's locale charset (or iso-8859-1 by default).
+ *
+ * Note: This function is deprecated. Use #g_mime_locale_charset
+ * instead.
+ *
+ * Returns the user's locale charset (or iso-8859-1 by default).
+ **/
+const char *
+g_mime_charset_locale_name (void)
+{
+	return g_mime_locale_charset ();
+}
+
+
+/**
+ * g_mime_charset_iso_to_windows:
+ * @isocharset: ISO-8859-# charset
+ *
+ * Maps the ISO-8859-# charset to the equivalent Windows-CP125#
+ * charset.
+ *
+ * Returns equivalent Windows charset.
+ **/
+const char *
+g_mime_charset_iso_to_windows (const char *isocharset)
+{
+	/* According to http://czyborra.com/charsets/codepages.html,
+	 * the charset mapping is as follows:
+	 *
+	 * us-ascii    maps to windows-cp1252
+	 * iso-8859-1  maps to windows-cp1252
+	 * iso-8859-2  maps to windows-cp1250
+	 * iso-8859-3  maps to windows-cp????
+	 * iso-8859-4  maps to windows-cp????
+	 * iso-8859-5  maps to windows-cp1251
+	 * iso-8859-6  maps to windows-cp1256
+	 * iso-8859-7  maps to windows-cp1253
+	 * iso-8859-8  maps to windows-cp1255
+	 * iso-8859-9  maps to windows-cp1254
+	 * iso-8859-10 maps to windows-cp????
+	 * iso-8859-11 maps to windows-cp????
+	 * iso-8859-12 maps to windows-cp????
+	 * iso-8859-13 maps to windows-cp1257
+	 *
+	 * Assumptions:
+	 *  - I'm going to assume that since iso-8859-4 and
+	 *    iso-8859-13 are Baltic that it also maps to
+	 *    windows-cp1257.
+	 */
+	
+	isocharset = g_mime_charset_canon_name (isocharset);
+	
+	if (!strcasecmp (isocharset, "iso-8859-1") || !strcasecmp (isocharset, "us-ascii"))
+		return "windows-cp1252";
+	else if (!strcasecmp (isocharset, "iso-8859-2"))
+		return "windows-cp1250";
+	else if (!strcasecmp (isocharset, "iso-8859-4"))
+		return "windows-cp1257";
+	else if (!strcasecmp (isocharset, "iso-8859-5"))
+		return "windows-cp1251";
+	else if (!strcasecmp (isocharset, "iso-8859-6"))
+		return "windows-cp1256";
+	else if (!strcasecmp (isocharset, "iso-8859-7"))
+		return "windows-cp1253";
+	else if (!strcasecmp (isocharset, "iso-8859-8"))
+		return "windows-cp1255";
+	else if (!strcasecmp (isocharset, "iso-8859-9"))
+		return "windows-cp1254";
+	else if (!strcasecmp (isocharset, "iso-8859-13"))
+		return "windows-cp1257";
+	
+	return isocharset;
+}
+
+
 /**
  * g_mime_charset_init:
  * @charset: charset mask
@@ -313,7 +655,7 @@
 void
 g_mime_charset_init (GMimeCharset *charset)
 {
-	charset->mask = ~0;
+	charset->mask = (unsigned int) ~0;
 	charset->level = 0;
 }
 
@@ -372,11 +714,16 @@
 static const char *
 charset_best_mask (unsigned int mask)
 {
+	const char *lang;
 	int i;
 	
-	for (i = 0; i < sizeof (charinfo) / sizeof (charinfo[0]); i++) {
-		if (charinfo[i].bit & mask)
-			return charinfo[i].name;
+	for (i = 0; i < G_N_ELEMENTS (charinfo); i++) {
+		if (charinfo[i].bit & mask) {
+			lang = g_mime_charset_language (charinfo[i].name);
+			
+			if (!lang || (locale_lang && !strncmp (locale_lang, lang, 2)))
+				return charinfo[i].name;
+		}
 	}
 	
 	return "UTF-8";
@@ -470,7 +817,7 @@
 	{ "gb2312",       1, 0 },  /* Simplified Chinese */
 	{ "Big5",         1, 0 },  /* Traditional Chinese */
 	{ "euc-tw",       1, 0 },
-	{ NULL, 0, 0}
+	{ NULL,           0, 0 }
 };
 
 unsigned int encoding_map[256 * 256];
@@ -510,7 +857,7 @@
 				inbuf++;
 				inleft--;
 			} else {
-				g_warning ("iconv (%s->UCS4, ..., %d, ..., %d):%s",
+				g_warning ("iconv (%s->UCS4, ..., %d, ..., %d): %s",
 					   tables[j].name, inleft, outleft,
 					   g_strerror (errno));
 				exit (1);
Index: gmime/gmime-charset.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-charset.h,v
retrieving revision 1.6
diff -u -r1.6 gmime-charset.h
--- gmime/gmime-charset.h	30 Dec 2002 16:37:57 -0000	1.6
+++ gmime/gmime-charset.h	31 Mar 2004 14:46:14 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,19 +24,32 @@
 #ifndef __GMIME_CHARSET_H__
 #define __GMIME_CHARSET_H__
 
+#include <glib.h>
+#include <sys/types.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-#include <sys/types.h>
-
 void        g_mime_charset_map_init (void);
+void        g_mime_charset_map_shutdown (void);
 
-const char *g_mime_charset_locale_name (void);
+const char *g_mime_locale_charset (void);
+const char *g_mime_locale_language (void);
+
+const char *g_mime_charset_language (const char *charset);
 
+const char *g_mime_charset_canon_name (const char *charset);
+const char *g_mime_charset_iconv_name (const char *charset);
+
+#ifndef GMIME_DISABLE_DEPRECATED
 const char *g_mime_charset_name (const char *charset);
+const char *g_mime_charset_locale_name (void);
+#endif
+
+const char *g_mime_charset_iso_to_windows (const char *isocharset);
+
 
 typedef struct _GMimeCharset {
 	unsigned int mask;
@@ -50,6 +63,7 @@
 const char *g_mime_charset_best_name (GMimeCharset *charset);
 
 const char *g_mime_charset_best (const char *in, size_t inlen);
+
 
 #ifdef __cplusplus
 }
