Remove g_filename_{to,from}_utf8
[platform/upstream/glib.git] / glib / gconvert.c
1 /* GLIB - Library of useful routines for C programming
2  *
3  * gconvert.c: Convert between character sets using iconv
4  * Copyright Red Hat Inc., 2000
5  * Authors: Havoc Pennington <hp@redhat.com>, Owen Taylor <otaylor@redhat.com
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <iconv.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27
28 #ifdef G_OS_WIN32
29 #include <windows.h>
30 #endif
31
32 #include "glib.h"
33
34 #define _(s) (s)
35
36 GQuark 
37 g_convert_error_quark()
38 {
39   static GQuark quark;
40   if (!quark)
41     quark = g_quark_from_static_string ("g_convert_error");
42   return quark;
43 }
44
45 static iconv_t
46 open_converter (const gchar *to_codeset,
47                 const gchar *from_codeset,
48                 GError     **error)
49 {
50   iconv_t cd = iconv_open (to_codeset, from_codeset);
51
52   if (cd == (iconv_t) -1)
53     {
54       /* Something went wrong.  */
55       if (errno == EINVAL)
56         g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
57                      _("Conversion from character set `%s' to `%s' is not supported"),
58                      from_codeset, to_codeset);
59       else
60         g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
61                      _("Could not open converter from `%s' to `%s': %s"),
62                      from_codeset, to_codeset, strerror (errno));
63     }
64
65   return cd;
66
67 }
68
69 /**
70  * g_convert:
71  * @str:           the string to convert
72  * @len:           the length of the string
73  * @to_codeset:    name of character set into which to convert @str
74  * @from_codeset:  character set of @str.
75  * @bytes_read:    location to store the number of bytes in the
76  *                 input string that were successfully converted, or %NULL.
77  *                 Even if the conversion was succesful, this may be 
78  *                 less than len if there were partial characters
79  *                 at the end of the input. If the error
80  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
81  *                 stored will the byte fofset after the last valid
82  *                 input sequence.
83  * @bytes_written: the stored in the output buffer (not including the
84  *                 terminating nul.
85  * @error:         location to store the error occuring, or %NULL to ignore
86  *                 errors. Any of the errors in #GConvertError may occur.
87  *
88  * Convert a string from one character set to another.
89  *
90  * Return value: If the conversion was successful, a newly allocated
91  *               NUL-terminated string, which must be freed with
92  *               g_free. Otherwise %NULL and @error will be set.
93  **/
94 gchar*
95 g_convert (const gchar *str,
96            gint         len,
97            const gchar *to_codeset,
98            const gchar *from_codeset,
99            gint        *bytes_read,
100            gint        *bytes_written,
101            GError     **error)
102 {
103   gchar *dest;
104   gchar *outp;
105   const gchar *p;
106   size_t inbytes_remaining;
107   size_t outbytes_remaining;
108   size_t err;
109   iconv_t cd;
110   size_t outbuf_size;
111   gboolean have_error = FALSE;
112   
113   g_return_val_if_fail (str != NULL, NULL);
114   g_return_val_if_fail (to_codeset != NULL, NULL);
115   g_return_val_if_fail (from_codeset != NULL, NULL);
116      
117   cd = open_converter (to_codeset, from_codeset, error);
118
119   if (cd == (iconv_t) -1)
120     {
121       if (bytes_read)
122         *bytes_read = 0;
123       
124       if (bytes_written)
125         *bytes_written = 0;
126       
127       return NULL;
128     }
129
130   if (len < 0)
131     len = strlen (str);
132
133   p = str;
134   inbytes_remaining = len;
135   outbuf_size = len + 1; /* + 1 for nul in case len == 1 */
136   outbytes_remaining = outbuf_size - 1; /* -1 for nul */
137   outp = dest = g_malloc (outbuf_size);
138
139  again:
140   
141   err = iconv (cd, &p, &inbytes_remaining, &outp, &outbytes_remaining);
142
143   if (err == (size_t) -1)
144     {
145       switch (errno)
146         {
147         case EINVAL:
148           /* Incomplete text, do not report an error */
149           break;
150         case E2BIG:
151           {
152             size_t used = outp - dest;
153             outbuf_size *= 2;
154             dest = g_realloc (dest, outbuf_size);
155
156             outp = dest + used;
157             outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
158
159             goto again;
160           }
161         case EILSEQ:
162           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
163                        _("Invalid byte sequence in conversion input"));
164           have_error = TRUE;
165           break;
166         default:
167           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
168                        _("Error during conversion: %s"),
169                        strerror (errno));
170           have_error = TRUE;
171           break;
172         }
173     }
174
175   *outp = '\0';
176   
177   iconv_close (cd);
178
179   if (bytes_read)
180     *bytes_read = p - str;
181   else
182     {
183       if ((p - str) != len) 
184         {
185           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
186                        _("Partial character sequence at end of input"));
187           have_error = TRUE;
188         }
189     }
190
191   if (bytes_written)
192     *bytes_written = outp - dest;       /* Doesn't include '\0' */
193
194   if (have_error)
195     {
196       g_free (dest);
197       return NULL;
198     }
199   else
200     return dest;
201 }
202
203 /**
204  * g_convert_with_fallback:
205  * @str:          the string to convert
206  * @len:          the length of the string
207  * @to_codeset:   name of character set into which to convert @str
208  * @from_codeset: character set of @str.
209  * @fallback:     UTF-8 string to use in place of character not
210  *                present in the target encoding. (This must be
211  *                in the target encoding), if %NULL, characters
212  *                not in the target encoding will be represented
213  *                as Unicode escapes \x{XXXX} or \x{XXXXXX}.
214  * @bytes_read:   location to store the number of bytes in the
215  *                input string that were successfully converted, or %NULL.
216  *                Even if the conversion was succesful, this may be 
217  *                less than len if there were partial characters
218  *                at the end of the input. If the error
219  *                G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
220  *                stored will the byte fofset after the last valid
221  *                input sequence.
222  * @bytes_written: the stored in the output buffer (not including the
223  *                 terminating nul.
224  * @error:        location to store the error occuring, or %NULL to ignore
225  *                errors. Any of the errors in #GConvertError may occur.
226  *
227  * Convert a string from one character set to another, possibly
228  * including fallback sequences for characters not representable
229  * in the output. Note that it is not guaranteed that the specification
230  * for the fallback sequences in @fallback will be honored. Some
231  * systems may do a approximate conversion from @from_codeset
232  * to @to_codeset in their iconv() functions, in which case GLib
233  * will simply return that approximate conversion.
234  *
235  * Return value: If the conversion was successful, a newly allocated
236  *               NUL-terminated string, which must be freed with
237  *               g_free. Otherwise %NULL and @error will be set.
238  **/
239 gchar*
240 g_convert_with_fallback (const gchar *str,
241                          gint         len,
242                          const gchar *to_codeset,
243                          const gchar *from_codeset,
244                          gchar       *fallback,
245                          gint        *bytes_read,
246                          gint        *bytes_written,
247                          GError     **error)
248 {
249   gchar *utf8;
250   gchar *dest;
251   gchar *outp;
252   const gchar *insert_str = NULL;
253   const gchar *p;
254   int inbytes_remaining;
255   const gchar *save_p = NULL;
256   size_t save_inbytes = 0;
257   size_t outbytes_remaining;
258   size_t err;
259   iconv_t cd;
260   size_t outbuf_size;
261   gboolean have_error = FALSE;
262   gboolean done = FALSE;
263
264   GError *local_error = NULL;
265   
266   g_return_val_if_fail (str != NULL, NULL);
267   g_return_val_if_fail (to_codeset != NULL, NULL);
268   g_return_val_if_fail (from_codeset != NULL, NULL);
269      
270   if (len < 0)
271     len = strlen (str);
272   
273   /* Try an exact conversion; we only proceed if this fails
274    * due to an illegal sequence in the input string.
275    */
276   dest = g_convert (str, len, to_codeset, from_codeset, 
277                     bytes_read, bytes_written, &local_error);
278   if (!local_error)
279     return dest;
280
281   if (!g_error_matches (local_error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
282     {
283       g_propagate_error (error, local_error);
284       return NULL;
285     }
286   else
287     g_error_free (local_error);
288
289   /* No go; to proceed, we need a converter from "UTF-8" to
290    * to_codeset, and the string as UTF-8.
291    */
292   cd = open_converter (to_codeset, "UTF-8", error);
293   if (cd == (iconv_t) -1)
294     {
295       if (bytes_read)
296         *bytes_read = 0;
297       
298       if (bytes_written)
299         *bytes_written = 0;
300       
301       return NULL;
302     }
303
304   utf8 = g_convert (str, len, "UTF-8", from_codeset, 
305                     bytes_read, &inbytes_remaining, error);
306   if (!utf8)
307     return NULL;
308
309   /* Now the heart of the code. We loop through the UTF-8 string, and
310    * whenever we hit an offending character, we form fallback, convert
311    * the fallback to the target codeset, and then go back to
312    * converting the original string after finishing with the fallback.
313    *
314    * The variables save_p and save_inbytes store the input state
315    * for the original string while we are converting the fallback
316    */
317   p = utf8;
318   outbuf_size = len + 1; /* + 1 for nul in case len == 1 */
319   outbytes_remaining = outbuf_size - 1; /* -1 for nul */
320   outp = dest = g_malloc (outbuf_size);
321
322   while (!done && !have_error)
323     {
324       size_t inbytes_tmp = inbytes_remaining;
325       err = iconv (cd, &p, &inbytes_tmp, &outp, &outbytes_remaining);
326       inbytes_remaining = inbytes_tmp;
327
328       if (err == (size_t) -1)
329         {
330           switch (errno)
331             {
332             case EINVAL:
333               g_assert_not_reached();
334               break;
335             case E2BIG:
336               {
337                 size_t used = outp - dest;
338                 outbuf_size *= 2;
339                 dest = g_realloc (dest, outbuf_size);
340                 
341                 outp = dest + used;
342                 outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
343                 
344                 break;
345               }
346             case EILSEQ:
347               if (save_p)
348                 {
349                   /* Error converting fallback string - fatal
350                    */
351                   g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
352                                _("Cannot convert fallback '%s' to codeset '%s'"),
353                                insert_str, to_codeset);
354                   have_error = TRUE;
355                   break;
356                 }
357               else
358                 {
359                   if (!fallback)
360                     { 
361                       gunichar ch = g_utf8_get_char (p);
362                       insert_str = g_strdup_printf ("\\x{%0*X}",
363                                                     (ch < 0x10000) ? 4 : 6,
364                                                     ch);
365                     }
366                   else
367                     insert_str = fallback;
368                   
369                   save_p = g_utf8_next_char (p);
370                   save_inbytes = inbytes_remaining - (save_p - p);
371                   p = insert_str;
372                   inbytes_remaining = strlen (p);
373                 }
374               break;
375             default:
376               g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
377                            _("Error during conversion: %s"),
378                            strerror (errno));
379               have_error = TRUE;
380               break;
381             }
382         }
383       else
384         {
385           if (save_p)
386             {
387               if (!fallback)
388                 g_free ((gchar *)insert_str);
389               p = save_p;
390               inbytes_remaining = save_inbytes;
391               save_p = NULL;
392             }
393           else
394             done = TRUE;
395         }
396     }
397
398   /* Cleanup
399    */
400   *outp = '\0';
401   
402   iconv_close (cd);
403
404   if (bytes_written)
405     *bytes_written = outp - str;        /* Doesn't include '\0' */
406
407   g_free (utf8);
408
409   if (have_error)
410     {
411       if (save_p && !fallback)
412         g_free ((gchar *)insert_str);
413       g_free (dest);
414       return NULL;
415     }
416   else
417     return dest;
418 }
419
420 /*
421  * g_locale_to_utf8
422  *
423  * Converts a string which is in the encoding used for strings by
424  * the C runtime (usually the same as that used by the operating
425  * system) in the current locale into a UTF-8 string.
426  */
427
428 gchar *
429 g_locale_to_utf8 (const gchar *opsysstring, GError **error)
430 {
431 #ifdef G_OS_WIN32
432
433   gint i, clen, wclen, first;
434   const gint len = strlen (opsysstring);
435   wchar_t *wcs, wc;
436   gchar *result, *bp;
437   const wchar_t *wcp;
438
439   wcs = g_new (wchar_t, len);
440   wclen = MultiByteToWideChar (CP_ACP, 0, opsysstring, len, wcs, len);
441
442   wcp = wcs;
443   clen = 0;
444   for (i = 0; i < wclen; i++)
445     {
446       wc = *wcp++;
447
448       if (wc < 0x80)
449         clen += 1;
450       else if (wc < 0x800)
451         clen += 2;
452       else if (wc < 0x10000)
453         clen += 3;
454       else if (wc < 0x200000)
455         clen += 4;
456       else if (wc < 0x4000000)
457         clen += 5;
458       else
459         clen += 6;
460     }
461
462   result = g_malloc (clen + 1);
463   
464   wcp = wcs;
465   bp = result;
466   for (i = 0; i < wclen; i++)
467     {
468       wc = *wcp++;
469
470       if (wc < 0x80)
471         {
472           first = 0;
473           clen = 1;
474         }
475       else if (wc < 0x800)
476         {
477           first = 0xc0;
478           clen = 2;
479         }
480       else if (wc < 0x10000)
481         {
482           first = 0xe0;
483           clen = 3;
484         }
485       else if (wc < 0x200000)
486         {
487           first = 0xf0;
488           clen = 4;
489         }
490       else if (wc < 0x4000000)
491         {
492           first = 0xf8;
493           clen = 5;
494         }
495       else
496         {
497           first = 0xfc;
498           clen = 6;
499         }
500       
501       /* Woo-hoo! */
502       switch (clen)
503         {
504         case 6: bp[5] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
505         case 5: bp[4] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
506         case 4: bp[3] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
507         case 3: bp[2] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
508         case 2: bp[1] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
509         case 1: bp[0] = wc | first;
510         }
511
512       bp += clen;
513     }
514   *bp = 0;
515
516   g_free (wcs);
517
518   return result;
519
520 #else
521
522   char *charset, *str;
523
524   if (g_get_charset (&charset))
525     return g_strdup (opsysstring);
526
527   str = g_convert (opsysstring, strlen (opsysstring), 
528                    "UTF-8", charset, NULL, NULL, error);
529   
530   return str;
531 #endif
532 }
533
534 /*
535  * g_locale_from_utf8
536  *
537  * The reverse of g_locale_to_utf8.
538  */
539
540 gchar *
541 g_locale_from_utf8 (const gchar *utf8string, GError **error)
542 {
543 #ifdef G_OS_WIN32
544
545   gint i, mask, clen, mblen;
546   const gint len = strlen (utf8string);
547   wchar_t *wcs, *wcp;
548   gchar *result;
549   guchar *cp, *end, c;
550   gint n;
551   
552   /* First convert to wide chars */
553   cp = (guchar *) utf8string;
554   end = cp + len;
555   n = 0;
556   wcs = g_new (wchar_t, len + 1);
557   wcp = wcs;
558   while (cp != end)
559     {
560       mask = 0;
561       c = *cp;
562
563       if (c < 0x80)
564         {
565           clen = 1;
566           mask = 0x7f;
567         }
568       else if ((c & 0xe0) == 0xc0)
569         {
570           clen = 2;
571           mask = 0x1f;
572         }
573       else if ((c & 0xf0) == 0xe0)
574         {
575           clen = 3;
576           mask = 0x0f;
577         }
578       else if ((c & 0xf8) == 0xf0)
579         {
580           clen = 4;
581           mask = 0x07;
582         }
583       else if ((c & 0xfc) == 0xf8)
584         {
585           clen = 5;
586           mask = 0x03;
587         }
588       else if ((c & 0xfc) == 0xfc)
589         {
590           clen = 6;
591           mask = 0x01;
592         }
593       else
594         {
595           g_free (wcs);
596           return NULL;
597         }
598
599       if (cp + clen > end)
600         {
601           g_free (wcs);
602           return NULL;
603         }
604
605       *wcp = (cp[0] & mask);
606       for (i = 1; i < clen; i++)
607         {
608           if ((cp[i] & 0xc0) != 0x80)
609             {
610               g_free (wcs);
611               return NULL;
612             }
613           *wcp <<= 6;
614           *wcp |= (cp[i] & 0x3f);
615         }
616
617       cp += clen;
618       wcp++;
619       n++;
620     }
621   if (cp != end)
622     {
623       g_free (wcs);
624       return NULL;
625     }
626
627   /* n is the number of wide chars constructed */
628
629   /* Convert to a string in the current ANSI codepage */
630
631   result = g_new (gchar, 3 * n + 1);
632   mblen = WideCharToMultiByte (CP_ACP, 0, wcs, n, result, 3*n, NULL, NULL);
633   result[mblen] = 0;
634   g_free (wcs);
635
636   return result;
637
638 #else
639
640   gchar *charset, *str;
641
642   if (g_get_charset (&charset))
643     return g_strdup (utf8string);
644
645   str = g_convert (utf8string, strlen (utf8string), 
646                    charset, "UTF-8", NULL, NULL, error);
647
648   return str;
649   
650 #endif
651 }
652
653 /* Filenames are in UTF-8 unless specificially requested otherwise */
654
655 gchar*
656 g_filename_to_utf8 (const gchar *string, GError **error)
657 {
658 #ifdef G_OS_WIN32
659   return g_locale_to_utf8 (string, error);
660 #else
661   if (getenv ("G_BROKEN_FILENAMES"))
662     return g_locale_to_utf8 (string, error);
663
664   return g_strdup (string);
665 #endif
666 }
667
668 gchar*
669 g_filename_from_utf8 (const gchar *string, GError **error)
670 {
671 #ifdef G_OS_WIN32
672   return g_locale_from_utf8 (string, error);
673 #else
674   if (getenv ("G_BROKEN_FILENAMES"))
675     return g_locale_from_utf8 (string, error);
676
677   return g_strdup (string);
678 #endif
679 }
680