Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-iconv-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29
30 #include "gmime-iconv-utils.h"
31 #include "gmime-charset.h"
32
33 #ifdef ENABLE_WARNINGS
34 #define w(x) x
35 #else
36 #define w(x)
37 #endif /* ENABLE_WARNINGS */
38
39
40 /**
41  * SECTION: gmime-iconv-utils
42  * @title: gmime-iconv-utils
43  * @short_description: High-level routines for converting text from one charset to another
44  * @see_also:
45  *
46  * Charset conversion utility functions.
47  **/
48
49
50 #ifdef G_THREADS_ENABLED
51 static GStaticMutex lock = G_STATIC_MUTEX_INIT;
52 #define LOCK()   g_static_mutex_lock (&lock)
53 #define UNLOCK() g_static_mutex_unlock (&lock)
54 #else
55 #define LOCK()
56 #define UNLOCK()
57 #endif /* G_THREADS_ENABLED */
58
59 static iconv_t utf8_to_locale = (iconv_t) -1;
60 static iconv_t locale_to_utf8 = (iconv_t) -1;
61
62
63 static void
64 iconv_utils_init (void)
65 {
66         static gboolean initialized = FALSE;
67         const char *utf8, *locale;
68         
69         if (initialized)
70                 return;
71         
72         g_mime_charset_map_init ();
73         
74         utf8 = g_mime_charset_iconv_name ("UTF-8");
75         
76         if (!(locale = g_mime_locale_charset ()))
77                 locale = "US-ASCII";
78         
79         if ((locale = g_mime_charset_iconv_name (locale))) {
80                 utf8_to_locale = iconv_open (locale, utf8);
81                 locale_to_utf8 = iconv_open (utf8, locale);
82         }
83         
84         initialized = TRUE;
85 }
86
87
88 /**
89  * g_mime_iconv_strndup:
90  * @cd: conversion descriptor
91  * @str: string in source charset
92  * @n: number of bytes to convert
93  *
94  * Allocates a new string buffer containing the first @n bytes of @str
95  * converted to the destination charset as described by the conversion
96  * descriptor @cd.
97  *
98  * Returns: a new string buffer containing the first @n bytes of
99  * @str converted to the destination charset as described by the
100  * conversion descriptor @cd.
101  **/
102 char *
103 g_mime_iconv_strndup (iconv_t cd, const char *str, size_t n)
104 {
105         size_t inleft, outleft, converted = 0;
106         char *out, *outbuf;
107         const char *inbuf;
108         size_t outlen;
109         int errnosav;
110         
111         if (cd == (iconv_t) -1)
112                 return g_strndup (str, n);
113         
114         outlen = n * 2 + 16;
115         out = g_malloc (outlen + 4);
116         
117         inbuf = str;
118         inleft = n;
119         
120         do {
121                 errno = 0;
122                 outbuf = out + converted;
123                 outleft = outlen - converted;
124                 
125                 converted = iconv (cd, (char **) &inbuf, &inleft, &outbuf, &outleft);
126                 if (converted != (size_t) -1 || errno == EINVAL) {
127                         /*
128                          * EINVAL  An  incomplete  multibyte sequence has been encounĀ­
129                          *         tered in the input.
130                          *
131                          * We'll just have to ignore it...
132                          */
133                         break;
134                 }
135                 
136                 if (errno != E2BIG) {
137                         errnosav = errno;
138                         
139                         w(g_warning ("g_mime_iconv_strndup: %s at byte %lu",
140                                      strerror (errno), n - inleft));
141                         
142                         g_free (out);
143                         
144                         /* reset the cd */
145                         iconv (cd, NULL, NULL, NULL, NULL);
146                         
147                         errno = errnosav;
148                         
149                         return NULL;
150                 }
151                 
152                 /*
153                  * E2BIG   There is not sufficient room at *outbuf.
154                  *
155                  * We just need to grow our outbuffer and try again.
156                  */
157                 
158                 converted = outbuf - out;
159                 outlen += inleft * 2 + 16;
160                 out = g_realloc (out, outlen + 4);
161                 outbuf = out + converted;
162         } while (TRUE);
163         
164         /* flush the iconv conversion */
165         while (iconv (cd, NULL, NULL, &outbuf, &outleft) == (size_t) -1) {
166                 if (errno != E2BIG)
167                         break;
168                 
169                 outlen += 16;
170                 converted = outbuf - out;
171                 out = g_realloc (out, outlen + 4);
172                 outleft = outlen - converted;
173                 outbuf = out + converted;
174         }
175         
176         /* Note: not all charsets can be nul-terminated with a single
177            nul byte. UCS2, for example, needs 2 nul bytes and UCS4
178            needs 4. I hope that 4 nul bytes is enough to terminate all
179            multibyte charsets? */
180         
181         /* nul-terminate the string */
182         memset (outbuf, 0, 4);
183         
184         /* reset the cd */
185         iconv (cd, NULL, NULL, NULL, NULL);
186         
187         return out;
188 }
189
190
191 /**
192  * g_mime_iconv_strdup:
193  * @cd: conversion descriptor
194  * @str: string in source charset
195  *
196  * Allocates a new string buffer containing @str converted to the
197  * destination charset described in @cd.
198  *
199  * Returns: a new string buffer containing the original string
200  * converted to the new charset.
201  **/
202 char *
203 g_mime_iconv_strdup (iconv_t cd, const char *str)
204 {
205         return g_mime_iconv_strndup (cd, str, strlen (str));
206 }
207
208
209 /**
210  * g_mime_iconv_locale_to_utf8:
211  * @str: string in locale charset
212  *
213  * Allocates a new string buffer containing @str in UTF-8.
214  *
215  * Returns: a new string buffer containing @str converted to UTF-8.
216  **/
217 char *
218 g_mime_iconv_locale_to_utf8 (const char *str)
219 {
220         char *buf;
221         
222         LOCK ();
223         iconv_utils_init ();
224         
225         buf = g_mime_iconv_strdup (locale_to_utf8, str);
226         UNLOCK ();
227         
228         return buf;
229 }
230
231
232 /**
233  * g_mime_iconv_locale_to_utf8_length:
234  * @str: string in locale charset
235  * @n: number of bytes to convert
236  *
237  * Allocates a new string buffer containing the first @n bytes of
238  * @str converted to UTF-8.
239  *
240  * Returns: a new string buffer containing the first @n bytes of
241  * @str converted to UTF-8.
242  **/
243 char *
244 g_mime_iconv_locale_to_utf8_length (const char *str, size_t n)
245 {
246         char *buf;
247         
248         LOCK ();
249         iconv_utils_init ();
250         
251         buf = g_mime_iconv_strndup (locale_to_utf8, str, n);
252         UNLOCK ();
253         
254         return buf;
255 }
256
257
258 /**
259  * g_mime_iconv_utf8_to_locale:
260  * @str: string in UTF-8 charset
261  *
262  * Allocates a new string buffer containing @str converted to the
263  * user's locale charset.
264  *
265  * Returns: a new string buffer containing @str converted to the
266  * user's locale charset.
267  **/
268 char *
269 g_mime_iconv_utf8_to_locale (const char *str)
270 {
271         char *buf;
272         
273         LOCK ();
274         iconv_utils_init ();
275         
276         buf = g_mime_iconv_strdup (utf8_to_locale, str);
277         UNLOCK ();
278         
279         return buf;
280 }
281
282
283 /**
284  * g_mime_iconv_utf8_to_locale_length:
285  * @str: string in UTF-8 charset
286  * @n: number of bytes to convert
287  *
288  * Allocates a new string buffer containing the first @n bytes of
289  * @str converted to the user's locale charset.
290  *
291  * Returns: a new string buffer containing the first @n bytes of
292  * @str converted to the user's locale charset.
293  **/
294 char *
295 g_mime_iconv_utf8_to_locale_length (const char *str, size_t n)
296 {
297         char *buf;
298         
299         LOCK ();
300         iconv_utils_init ();
301         
302         buf = g_mime_iconv_strndup (utf8_to_locale, str, n);
303         UNLOCK ();
304         
305         return buf;
306 }