Add gettext support.
[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 #include "glib.h"
29 #include "config.h"
30
31 #ifdef G_OS_WIN32
32 #include <windows.h>
33 #endif
34
35 #include "glibintl.h"
36
37 GQuark 
38 g_convert_error_quark()
39 {
40   static GQuark quark;
41   if (!quark)
42     quark = g_quark_from_static_string ("g_convert_error");
43
44   return quark;
45 }
46
47 #if defined(USE_LIBICONV) && !defined (_LIBICONV_H)
48 #error libiconv in use but included iconv.h not from libiconv
49 #endif
50 #if !defined(USE_LIBICONV) && defined (_LIBICONV_H)
51 #error libiconv not in use but included iconv.h is from libiconv
52 #endif
53
54 GIConv
55 g_iconv_open (const gchar  *to_codeset,
56               const gchar  *from_codeset)
57 {
58   iconv_t cd = iconv_open (to_codeset, from_codeset);
59   
60   return (GIConv)cd;
61 }
62
63 size_t 
64 g_iconv (GIConv   converter,
65          gchar  **inbuf,
66          size_t  *inbytes_left,
67          gchar  **outbuf,
68          size_t  *outbytes_left)
69 {
70   iconv_t cd = (iconv_t)converter;
71
72   return iconv (cd, inbuf, inbytes_left, outbuf, outbytes_left);
73 }
74
75 gint
76 g_iconv_close (GIConv converter)
77 {
78   iconv_t cd = (iconv_t)converter;
79
80   return iconv_close (cd);
81 }
82
83 static GIConv
84 open_converter (const gchar *to_codeset,
85                 const gchar *from_codeset,
86                 GError     **error)
87 {
88   GIConv cd = g_iconv_open (to_codeset, from_codeset);
89
90   if (cd == (iconv_t) -1)
91     {
92       /* Something went wrong.  */
93       if (errno == EINVAL)
94         g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
95                      _("Conversion from character set `%s' to `%s' is not suppo\rted"),
96                      from_codeset, to_codeset);
97       else
98         g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
99                      _("Could not open converter from `%s' to `%s': %s"),
100                      from_codeset, to_codeset, strerror (errno));
101     }
102
103   return cd;
104
105 }
106
107 /**
108  * g_convert:
109  * @str:           the string to convert
110  * @len:           the length of the string
111  * @to_codeset:    name of character set into which to convert @str
112  * @from_codeset:  character set of @str.
113  * @bytes_read:    location to store the number of bytes in the
114  *                 input string that were successfully converted, or %NULL.
115  *                 Even if the conversion was succesful, this may be 
116  *                 less than len if there were partial characters
117  *                 at the end of the input. If the error
118  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
119  *                 stored will the byte fofset after the last valid
120  *                 input sequence.
121  * @bytes_written: the stored in the output buffer (not including the
122  *                 terminating nul.
123  * @error:         location to store the error occuring, or %NULL to ignore
124  *                 errors. Any of the errors in #GConvertError may occur.
125  *
126  * Convert a string from one character set to another.
127  *
128  * Return value: If the conversion was successful, a newly allocated
129  *               NUL-terminated string, which must be freed with
130  *               g_free. Otherwise %NULL and @error will be set.
131  **/
132 gchar*
133 g_convert (const gchar *str,
134            gint         len,
135            const gchar *to_codeset,
136            const gchar *from_codeset,
137            gint        *bytes_read,
138            gint        *bytes_written,
139            GError     **error)
140 {
141   gchar *dest;
142   gchar *outp;
143   const gchar *p;
144   size_t inbytes_remaining;
145   size_t outbytes_remaining;
146   size_t err;
147   GIConv cd;
148   size_t outbuf_size;
149   gboolean have_error = FALSE;
150   
151   g_return_val_if_fail (str != NULL, NULL);
152   g_return_val_if_fail (to_codeset != NULL, NULL);
153   g_return_val_if_fail (from_codeset != NULL, NULL);
154      
155   cd = open_converter (to_codeset, from_codeset, error);
156
157   if (cd == (GIConv) -1)
158     {
159       if (bytes_read)
160         *bytes_read = 0;
161       
162       if (bytes_written)
163         *bytes_written = 0;
164       
165       return NULL;
166     }
167
168   if (len < 0)
169     len = strlen (str);
170
171   p = str;
172   inbytes_remaining = len;
173
174   /* Due to a GLIBC bug, round outbuf_size up to a multiple of 4 */
175   /* + 1 for nul in case len == 1 */
176   outbuf_size = ((len + 3) & ~3) + 1;
177   
178   outbytes_remaining = outbuf_size - 1; /* -1 for nul */
179   outp = dest = g_malloc (outbuf_size);
180
181  again:
182   
183   err = g_iconv (cd, (char **)&p, &inbytes_remaining, &outp, &outbytes_remaining);
184
185   if (err == (size_t) -1)
186     {
187       switch (errno)
188         {
189         case EINVAL:
190           /* Incomplete text, do not report an error */
191           break;
192         case E2BIG:
193           {
194             size_t used = outp - dest;
195
196             /* glibc's iconv can return E2BIG even if there is space
197              * remaining if an internal buffer is exhausted. The
198              * folllowing is a heuristic to catch this. The 16 is
199              * pretty arbitrary.
200              */
201             if (used + 16 > outbuf_size)
202               {
203                 outbuf_size = (outbuf_size - 1) * 2 + 1;
204                 dest = g_realloc (dest, outbuf_size);
205                 
206                 outp = dest + used;
207                 outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
208               }
209
210             goto again;
211           }
212         case EILSEQ:
213           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
214                        _("Invalid byte sequence in conversion input"));
215           have_error = TRUE;
216           break;
217         default:
218           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
219                        _("Error during conversion: %s"),
220                        strerror (errno));
221           have_error = TRUE;
222           break;
223         }
224     }
225
226   *outp = '\0';
227   
228   g_iconv_close (cd);
229
230   if (bytes_read)
231     *bytes_read = p - str;
232   else
233     {
234       if ((p - str) != len) 
235         {
236           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
237                        _("Partial character sequence at end of input"));
238           have_error = TRUE;
239         }
240     }
241
242   if (bytes_written)
243     *bytes_written = outp - dest;       /* Doesn't include '\0' */
244
245   if (have_error)
246     {
247       g_free (dest);
248       return NULL;
249     }
250   else
251     return dest;
252 }
253
254 /**
255  * g_convert_with_fallback:
256  * @str:          the string to convert
257  * @len:          the length of the string
258  * @to_codeset:   name of character set into which to convert @str
259  * @from_codeset: character set of @str.
260  * @fallback:     UTF-8 string to use in place of character not
261  *                present in the target encoding. (This must be
262  *                in the target encoding), if %NULL, characters
263  *                not in the target encoding will be represented
264  *                as Unicode escapes \x{XXXX} or \x{XXXXXX}.
265  * @bytes_read:   location to store the number of bytes in the
266  *                input string that were successfully converted, or %NULL.
267  *                Even if the conversion was succesful, this may be 
268  *                less than len if there were partial characters
269  *                at the end of the input. If the error
270  *                G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
271  *                stored will the byte fofset after the last valid
272  *                input sequence.
273  * @bytes_written: the stored in the output buffer (not including the
274  *                 terminating nul.
275  * @error:        location to store the error occuring, or %NULL to ignore
276  *                errors. Any of the errors in #GConvertError may occur.
277  *
278  * Convert a string from one character set to another, possibly
279  * including fallback sequences for characters not representable
280  * in the output. Note that it is not guaranteed that the specification
281  * for the fallback sequences in @fallback will be honored. Some
282  * systems may do a approximate conversion from @from_codeset
283  * to @to_codeset in their iconv() functions, in which case GLib
284  * will simply return that approximate conversion.
285  *
286  * Return value: If the conversion was successful, a newly allocated
287  *               NUL-terminated string, which must be freed with
288  *               g_free. Otherwise %NULL and @error will be set.
289  **/
290 gchar*
291 g_convert_with_fallback (const gchar *str,
292                          gint         len,
293                          const gchar *to_codeset,
294                          const gchar *from_codeset,
295                          gchar       *fallback,
296                          gint        *bytes_read,
297                          gint        *bytes_written,
298                          GError     **error)
299 {
300   gchar *utf8;
301   gchar *dest;
302   gchar *outp;
303   const gchar *insert_str = NULL;
304   const gchar *p;
305   int inbytes_remaining;
306   const gchar *save_p = NULL;
307   size_t save_inbytes = 0;
308   size_t outbytes_remaining;
309   size_t err;
310   GIConv cd;
311   size_t outbuf_size;
312   gboolean have_error = FALSE;
313   gboolean done = FALSE;
314
315   GError *local_error = NULL;
316   
317   g_return_val_if_fail (str != NULL, NULL);
318   g_return_val_if_fail (to_codeset != NULL, NULL);
319   g_return_val_if_fail (from_codeset != NULL, NULL);
320      
321   if (len < 0)
322     len = strlen (str);
323   
324   /* Try an exact conversion; we only proceed if this fails
325    * due to an illegal sequence in the input string.
326    */
327   dest = g_convert (str, len, to_codeset, from_codeset, 
328                     bytes_read, bytes_written, &local_error);
329   if (!local_error)
330     return dest;
331
332   if (!g_error_matches (local_error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
333     {
334       g_propagate_error (error, local_error);
335       return NULL;
336     }
337   else
338     g_error_free (local_error);
339
340   /* No go; to proceed, we need a converter from "UTF-8" to
341    * to_codeset, and the string as UTF-8.
342    */
343   cd = open_converter (to_codeset, "UTF-8", error);
344   if (cd == (GIConv) -1)
345     {
346       if (bytes_read)
347         *bytes_read = 0;
348       
349       if (bytes_written)
350         *bytes_written = 0;
351       
352       return NULL;
353     }
354
355   utf8 = g_convert (str, len, "UTF-8", from_codeset, 
356                     bytes_read, &inbytes_remaining, error);
357   if (!utf8)
358     return NULL;
359
360   /* Now the heart of the code. We loop through the UTF-8 string, and
361    * whenever we hit an offending character, we form fallback, convert
362    * the fallback to the target codeset, and then go back to
363    * converting the original string after finishing with the fallback.
364    *
365    * The variables save_p and save_inbytes store the input state
366    * for the original string while we are converting the fallback
367    */
368   p = utf8;
369   /* Due to a GLIBC bug, round outbuf_size up to a multiple of 4 */
370   /* + 1 for nul in case len == 1 */
371   outbuf_size = ((len + 3) & ~3) + 1;
372   outbytes_remaining = outbuf_size - 1; /* -1 for nul */
373   outp = dest = g_malloc (outbuf_size);
374
375   while (!done && !have_error)
376     {
377       size_t inbytes_tmp = inbytes_remaining;
378       err = g_iconv (cd, (char **)&p, &inbytes_tmp, &outp, &outbytes_remaining);
379       inbytes_remaining = inbytes_tmp;
380
381       if (err == (size_t) -1)
382         {
383           switch (errno)
384             {
385             case EINVAL:
386               g_assert_not_reached();
387               break;
388             case E2BIG:
389               {
390                 size_t used = outp - dest;
391
392                 /* glibc's iconv can return E2BIG even if there is space
393                  * remaining if an internal buffer is exhausted. The
394                  * folllowing is a heuristic to catch this. The 16 is
395                  * pretty arbitrary.
396                  */
397                 if (used + 16 > outbuf_size)
398                   {
399                     outbuf_size = (outbuf_size - 1) * 2 + 1;
400                     dest = g_realloc (dest, outbuf_size);
401                     
402                     outp = dest + used;
403                     outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
404                   }
405                 
406                 break;
407               }
408             case EILSEQ:
409               if (save_p)
410                 {
411                   /* Error converting fallback string - fatal
412                    */
413                   g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
414                                _("Cannot convert fallback '%s' to codeset '%s'"),
415                                insert_str, to_codeset);
416                   have_error = TRUE;
417                   break;
418                 }
419               else
420                 {
421                   if (!fallback)
422                     { 
423                       gunichar ch = g_utf8_get_char (p);
424                       insert_str = g_strdup_printf ("\\x{%0*X}",
425                                                     (ch < 0x10000) ? 4 : 6,
426                                                     ch);
427                     }
428                   else
429                     insert_str = fallback;
430                   
431                   save_p = g_utf8_next_char (p);
432                   save_inbytes = inbytes_remaining - (save_p - p);
433                   p = insert_str;
434                   inbytes_remaining = strlen (p);
435                 }
436               break;
437             default:
438               g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
439                            _("Error during conversion: %s"),
440                            strerror (errno));
441               have_error = TRUE;
442               break;
443             }
444         }
445       else
446         {
447           if (save_p)
448             {
449               if (!fallback)
450                 g_free ((gchar *)insert_str);
451               p = save_p;
452               inbytes_remaining = save_inbytes;
453               save_p = NULL;
454             }
455           else
456             done = TRUE;
457         }
458     }
459
460   /* Cleanup
461    */
462   *outp = '\0';
463   
464   g_iconv_close (cd);
465
466   if (bytes_written)
467     *bytes_written = outp - str;        /* Doesn't include '\0' */
468
469   g_free (utf8);
470
471   if (have_error)
472     {
473       if (save_p && !fallback)
474         g_free ((gchar *)insert_str);
475       g_free (dest);
476       return NULL;
477     }
478   else
479     return dest;
480 }
481
482 /*
483  * g_locale_to_utf8
484  *
485  * 
486  */
487
488 /**
489  * g_locale_to_utf8:
490  * @opsysstring:   a string in the encoding of the current locale
491  * @len:           the length of the string, or -1 if the string is
492  *                 NULL-terminated.
493  * @bytes_read:    location to store the number of bytes in the
494  *                 input string that were successfully converted, or %NULL.
495  *                 Even if the conversion was succesful, this may be 
496  *                 less than len if there were partial characters
497  *                 at the end of the input. If the error
498  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
499  *                 stored will the byte fofset after the last valid
500  *                 input sequence.
501  * @bytes_written: the stored in the output buffer (not including the
502  *                 terminating nul.
503  * @error: location to store the error occuring, or %NULL to ignore
504  *                 errors. Any of the errors in #GConvertError may occur.
505  * 
506  * Converts a string which is in the encoding used for strings by
507  * the C runtime (usually the same as that used by the operating
508  * system) in the current locale into a UTF-8 string.
509  * 
510  * Return value: The converted string, or %NULL on an error.
511  **/
512 gchar *
513 g_locale_to_utf8 (const gchar  *opsysstring,
514                   gint          len,
515                   gint         *bytes_read,
516                   gint         *bytes_written,
517                   GError      **error)
518 {
519 #ifdef G_OS_WIN32
520
521   gint i, clen, total_len, wclen, first;
522   wchar_t *wcs, wc;
523   gchar *result, *bp;
524   const wchar_t *wcp;
525
526   if (len == -1)
527     len = strlen (opsysstring);
528   
529   wcs = g_new (wchar_t, len);
530   wclen = MultiByteToWideChar (CP_ACP, 0, opsysstring, len, wcs, len);
531
532   wcp = wcs;
533   total_len = 0;
534   for (i = 0; i < wclen; i++)
535     {
536       wc = *wcp++;
537
538       if (wc < 0x80)
539         total_len += 1;
540       else if (wc < 0x800)
541         total_len += 2;
542       else if (wc < 0x10000)
543         total_len += 3;
544       else if (wc < 0x200000)
545         total_len += 4;
546       else if (wc < 0x4000000)
547         total_len += 5;
548       else
549         total_len += 6;
550     }
551
552   result = g_malloc (total_len + 1);
553   
554   wcp = wcs;
555   bp = result;
556   for (i = 0; i < wclen; i++)
557     {
558       wc = *wcp++;
559
560       if (wc < 0x80)
561         {
562           first = 0;
563           clen = 1;
564         }
565       else if (wc < 0x800)
566         {
567           first = 0xc0;
568           clen = 2;
569         }
570       else if (wc < 0x10000)
571         {
572           first = 0xe0;
573           clen = 3;
574         }
575       else if (wc < 0x200000)
576         {
577           first = 0xf0;
578           clen = 4;
579         }
580       else if (wc < 0x4000000)
581         {
582           first = 0xf8;
583           clen = 5;
584         }
585       else
586         {
587           first = 0xfc;
588           clen = 6;
589         }
590       
591       /* Woo-hoo! */
592       switch (clen)
593         {
594         case 6: bp[5] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
595         case 5: bp[4] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
596         case 4: bp[3] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
597         case 3: bp[2] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
598         case 2: bp[1] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
599         case 1: bp[0] = wc | first;
600         }
601
602       bp += clen;
603     }
604   *bp = 0;
605
606   g_free (wcs);
607
608   if (bytes_read)
609     *bytes_read = len;
610   if (bytes_written)
611     *bytes_written = total_len;
612   
613   return result;
614
615 #else
616
617   char *charset, *str;
618
619   if (g_get_charset (&charset))
620     return g_strdup (opsysstring);
621
622   str = g_convert (opsysstring, len, 
623                    "UTF-8", charset, bytes_read, bytes_written, error);
624   
625   return str;
626 #endif
627 }
628
629 /**
630  * g_locale_from_utf8:
631  * @utf8string:    a UTF-8 encoded string 
632  * @len:           the length of the string, or -1 if the string is
633  *                 NULL-terminated.
634  * @bytes_read:    location to store the number of bytes in the
635  *                 input string that were successfully converted, or %NULL.
636  *                 Even if the conversion was succesful, this may be 
637  *                 less than len if there were partial characters
638  *                 at the end of the input. If the error
639  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
640  *                 stored will the byte fofset after the last valid
641  *                 input sequence.
642  * @bytes_written: the stored in the output buffer (not including the
643  *                 terminating nul.
644  * @error: location to store the error occuring, or %NULL to ignore
645  *                 errors. Any of the errors in #GConvertError may occur.
646  * 
647  * Converts a string from UTF-8 to the encoding used for strings by
648  * the C runtime (usually the same as that used by the operating
649  * system) in the current locale.
650  * 
651  * Return value: The converted string, or %NULL on an error.
652  **/
653 gchar *
654 g_locale_from_utf8 (const gchar *utf8string,
655                     gint         len,
656                     gint        *bytes_read,
657                     gint        *bytes_written,
658                     GError     **error)
659 {
660 #ifdef G_OS_WIN32
661
662   gint i, mask, clen, mblen;
663   wchar_t *wcs, *wcp;
664   gchar *result;
665   guchar *cp, *end, c;
666   gint n;
667   
668   if (len == -1)
669     len = strlen (utf8string);
670   
671   /* First convert to wide chars */
672   cp = (guchar *) utf8string;
673   end = cp + len;
674   n = 0;
675   wcs = g_new (wchar_t, len + 1);
676   wcp = wcs;
677   while (cp != end)
678     {
679       mask = 0;
680       c = *cp;
681
682       if (c < 0x80)
683         {
684           clen = 1;
685           mask = 0x7f;
686         }
687       else if ((c & 0xe0) == 0xc0)
688         {
689           clen = 2;
690           mask = 0x1f;
691         }
692       else if ((c & 0xf0) == 0xe0)
693         {
694           clen = 3;
695           mask = 0x0f;
696         }
697       else if ((c & 0xf8) == 0xf0)
698         {
699           clen = 4;
700           mask = 0x07;
701         }
702       else if ((c & 0xfc) == 0xf8)
703         {
704           clen = 5;
705           mask = 0x03;
706         }
707       else if ((c & 0xfc) == 0xfc)
708         {
709           clen = 6;
710           mask = 0x01;
711         }
712       else
713         {
714           g_free (wcs);
715           return NULL;
716         }
717
718       if (cp + clen > end)
719         {
720           g_free (wcs);
721           return NULL;
722         }
723
724       *wcp = (cp[0] & mask);
725       for (i = 1; i < clen; i++)
726         {
727           if ((cp[i] & 0xc0) != 0x80)
728             {
729               g_free (wcs);
730               return NULL;
731             }
732           *wcp <<= 6;
733           *wcp |= (cp[i] & 0x3f);
734         }
735
736       cp += clen;
737       wcp++;
738       n++;
739     }
740   if (cp != end)
741     {
742       g_free (wcs);
743       return NULL;
744     }
745
746   /* n is the number of wide chars constructed */
747
748   /* Convert to a string in the current ANSI codepage */
749
750   result = g_new (gchar, 3 * n + 1);
751   mblen = WideCharToMultiByte (CP_ACP, 0, wcs, n, result, 3*n, NULL, NULL);
752   result[mblen] = 0;
753   g_free (wcs);
754
755   if (bytes_read)
756     *bytes_read = len;
757   if (bytes_written)
758     *bytes_written = mblen;
759   
760   return result;
761
762 #else
763
764   gchar *charset, *str;
765
766   if (g_get_charset (&charset))
767     return g_strdup (utf8string);
768
769   str = g_convert (utf8string, strlen (utf8string), 
770                    charset, "UTF-8", bytes_read, bytes_written, error);
771
772   return str;
773   
774 #endif
775 }
776
777 /**
778  * g_filename_to_utf8:
779  * @opsysstring:   a string in the encoding for filenames
780  * @len:           the length of the string, or -1 if the string is
781  *                 NULL-terminated.
782  * @bytes_read:    location to store the number of bytes in the
783  *                 input string that were successfully converted, or %NULL.
784  *                 Even if the conversion was succesful, this may be 
785  *                 less than len if there were partial characters
786  *                 at the end of the input. If the error
787  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
788  *                 stored will the byte fofset after the last valid
789  *                 input sequence.
790  * @bytes_written: the stored in the output buffer (not including the
791  *                 terminating nul.
792  * @error: location to store the error occuring, or %NULL to ignore
793  *                 errors. Any of the errors in #GConvertError may occur.
794  * 
795  * Converts a string which is in the encoding used for filenames
796  * into a UTF-8 string.
797  * 
798  * Return value: The converted string, or %NULL on an error.
799  **/
800 gchar*
801 g_filename_to_utf8 (const gchar *opsysstring, 
802                     gint         len,
803                     gint        *bytes_read,
804                     gint        *bytes_written,
805                     GError     **error)
806 {
807 #ifdef G_OS_WIN32
808   return g_locale_to_utf8 (opsysstring, len,
809                            bytes_read, bytes_written,
810                            error);
811 #else
812   if (getenv ("G_BROKEN_FILENAMES"))
813     return g_locale_to_utf8 (opsysstring, len,
814                              bytes_read, bytes_written,
815                              error);
816
817   if (bytes_read || bytes_written)
818     {
819       gint len = strlen (opsysstring);
820
821       if (bytes_read)
822         *bytes_read = len;
823       if (bytes_written)
824         *bytes_written = len;
825     }
826   
827   if (len < 0)
828     return g_strdup (opsysstring);
829   else
830     return g_strndup (opsysstring, len);
831 #endif
832 }
833
834 /**
835  * g_filename_from_utf8:
836  * @utf8string:    a UTF-8 encoded string 
837  * @len:           the length of the string, or -1 if the string is
838  *                 NULL-terminated.
839  * @bytes_read:    location to store the number of bytes in the
840  *                 input string that were successfully converted, or %NULL.
841  *                 Even if the conversion was succesful, this may be 
842  *                 less than len if there were partial characters
843  *                 at the end of the input. If the error
844  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
845  *                 stored will the byte fofset after the last valid
846  *                 input sequence.
847  * @bytes_written: the stored in the output buffer (not including the
848  *                 terminating nul.
849  * @error: location to store the error occuring, or %NULL to ignore
850  *                 errors. Any of the errors in #GConvertError may occur.
851  * 
852  * Converts a string from UTF-8 to the encoding used for filenames.
853  * 
854  * Return value: The converted string, or %NULL on an error.
855  **/
856 gchar*
857 g_filename_from_utf8 (const gchar *utf8string,
858                       gint         len,
859                       gint        *bytes_read,
860                       gint        *bytes_written,
861                       GError     **error)
862 {
863 #ifdef G_OS_WIN32
864   return g_locale_from_utf8 (utf8string, len,
865                              bytes_read, bytes_written,
866                              error);
867 #else
868   if (getenv ("G_BROKEN_FILENAMES"))
869     return g_locale_from_utf8 (utf8string, len,
870                                bytes_read, bytes_written,
871                                error);
872
873   if (bytes_read || bytes_written)
874     {
875       gint len = strlen (utf8string);
876
877       if (bytes_read)
878         *bytes_read = len;
879       if (bytes_written)
880         *bytes_written = len;
881     }
882
883   if (len < 0)
884     return g_strdup (utf8string);
885   else
886     return g_strndup (utf8string, len);
887 #endif
888 }
889
890