344902f4469d84fea8ee6c7b97c314fa6406a03a
[platform/upstream/glib.git] / 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 #define _(s) (s)
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   const gint len = len < 0 ? strlen (opsysstring) : len;
523   wchar_t *wcs, wc;
524   gchar *result, *bp;
525   const wchar_t *wcp;
526
527   wcs = g_new (wchar_t, len);
528   wclen = MultiByteToWideChar (CP_ACP, 0, opsysstring, len, wcs, len);
529
530   wcp = wcs;
531   total_len = 0;
532   for (i = 0; i < wclen; i++)
533     {
534       wc = *wcp++;
535
536       if (wc < 0x80)
537         total_len += 1;
538       else if (wc < 0x800)
539         total_len += 2;
540       else if (wc < 0x10000)
541         total_len += 3;
542       else if (wc < 0x200000)
543         total_len += 4;
544       else if (wc < 0x4000000)
545         total_len += 5;
546       else
547         total_len += 6;
548     }
549
550   result = g_malloc (total_len + 1);
551   
552   wcp = wcs;
553   bp = result;
554   for (i = 0; i < wclen; i++)
555     {
556       wc = *wcp++;
557
558       if (wc < 0x80)
559         {
560           first = 0;
561           clen = 1;
562         }
563       else if (wc < 0x800)
564         {
565           first = 0xc0;
566           clen = 2;
567         }
568       else if (wc < 0x10000)
569         {
570           first = 0xe0;
571           clen = 3;
572         }
573       else if (wc < 0x200000)
574         {
575           first = 0xf0;
576           clen = 4;
577         }
578       else if (wc < 0x4000000)
579         {
580           first = 0xf8;
581           clen = 5;
582         }
583       else
584         {
585           first = 0xfc;
586           clen = 6;
587         }
588       
589       /* Woo-hoo! */
590       switch (clen)
591         {
592         case 6: bp[5] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
593         case 5: bp[4] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
594         case 4: bp[3] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
595         case 3: bp[2] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
596         case 2: bp[1] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
597         case 1: bp[0] = wc | first;
598         }
599
600       bp += clen;
601     }
602   *bp = 0;
603
604   g_free (wcs);
605
606   if (bytes_read)
607     *bytes_read = len;
608   if (bytes_written)
609     *bytes_written = total_len;
610   
611   return result;
612
613 #else
614
615   char *charset, *str;
616
617   if (g_get_charset (&charset))
618     return g_strdup (opsysstring);
619
620   str = g_convert (opsysstring, len, 
621                    "UTF-8", charset, bytes_read, bytes_written, error);
622   
623   return str;
624 #endif
625 }
626
627 /**
628  * g_locale_from_utf8:
629  * @utf8string:    a UTF-8 encoded string 
630  * @len:           the length of the string, or -1 if the string is
631  *                 NULL-terminated.
632  * @bytes_read:    location to store the number of bytes in the
633  *                 input string that were successfully converted, or %NULL.
634  *                 Even if the conversion was succesful, this may be 
635  *                 less than len if there were partial characters
636  *                 at the end of the input. If the error
637  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
638  *                 stored will the byte fofset after the last valid
639  *                 input sequence.
640  * @bytes_written: the stored in the output buffer (not including the
641  *                 terminating nul.
642  * @error: location to store the error occuring, or %NULL to ignore
643  *                 errors. Any of the errors in #GConvertError may occur.
644  * 
645  * Converts a string from UTF-8 to the encoding used for strings by
646  * the C runtime (usually the same as that used by the operating
647  * system) in the current locale.
648  * 
649  * Return value: The converted string, or %NULL on an error.
650  **/
651 gchar *
652 g_locale_from_utf8 (const gchar *utf8string,
653                     gint         len,
654                     gint        *bytes_read,
655                     gint        *bytes_written,
656                     GError     **error)
657 {
658 #ifdef G_OS_WIN32
659
660   gint i, mask, clen, mblen;
661   const gint len = len < 0 ? strlen (utf8string) : len;
662   wchar_t *wcs, *wcp;
663   gchar *result;
664   guchar *cp, *end, c;
665   gint n;
666   
667   /* First convert to wide chars */
668   cp = (guchar *) utf8string;
669   end = cp + len;
670   n = 0;
671   wcs = g_new (wchar_t, len + 1);
672   wcp = wcs;
673   while (cp != end)
674     {
675       mask = 0;
676       c = *cp;
677
678       if (c < 0x80)
679         {
680           clen = 1;
681           mask = 0x7f;
682         }
683       else if ((c & 0xe0) == 0xc0)
684         {
685           clen = 2;
686           mask = 0x1f;
687         }
688       else if ((c & 0xf0) == 0xe0)
689         {
690           clen = 3;
691           mask = 0x0f;
692         }
693       else if ((c & 0xf8) == 0xf0)
694         {
695           clen = 4;
696           mask = 0x07;
697         }
698       else if ((c & 0xfc) == 0xf8)
699         {
700           clen = 5;
701           mask = 0x03;
702         }
703       else if ((c & 0xfc) == 0xfc)
704         {
705           clen = 6;
706           mask = 0x01;
707         }
708       else
709         {
710           g_free (wcs);
711           return NULL;
712         }
713
714       if (cp + clen > end)
715         {
716           g_free (wcs);
717           return NULL;
718         }
719
720       *wcp = (cp[0] & mask);
721       for (i = 1; i < clen; i++)
722         {
723           if ((cp[i] & 0xc0) != 0x80)
724             {
725               g_free (wcs);
726               return NULL;
727             }
728           *wcp <<= 6;
729           *wcp |= (cp[i] & 0x3f);
730         }
731
732       cp += clen;
733       wcp++;
734       n++;
735     }
736   if (cp != end)
737     {
738       g_free (wcs);
739       return NULL;
740     }
741
742   /* n is the number of wide chars constructed */
743
744   /* Convert to a string in the current ANSI codepage */
745
746   result = g_new (gchar, 3 * n + 1);
747   mblen = WideCharToMultiByte (CP_ACP, 0, wcs, n, result, 3*n, NULL, NULL);
748   result[mblen] = 0;
749   g_free (wcs);
750
751   if (bytes_read)
752     *bytes_read = len;
753   if (bytes_written)
754     *bytes_written = mblen;
755   
756   return result;
757
758 #else
759
760   gchar *charset, *str;
761
762   if (g_get_charset (&charset))
763     return g_strdup (utf8string);
764
765   str = g_convert (utf8string, strlen (utf8string), 
766                    charset, "UTF-8", bytes_read, bytes_written, error);
767
768   return str;
769   
770 #endif
771 }
772
773 /**
774  * g_filename_to_utf8:
775  * @opsysstring:   a string in the encoding for filenames
776  * @len:           the length of the string, or -1 if the string is
777  *                 NULL-terminated.
778  * @bytes_read:    location to store the number of bytes in the
779  *                 input string that were successfully converted, or %NULL.
780  *                 Even if the conversion was succesful, this may be 
781  *                 less than len if there were partial characters
782  *                 at the end of the input. If the error
783  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
784  *                 stored will the byte fofset after the last valid
785  *                 input sequence.
786  * @bytes_written: the stored in the output buffer (not including the
787  *                 terminating nul.
788  * @error: location to store the error occuring, or %NULL to ignore
789  *                 errors. Any of the errors in #GConvertError may occur.
790  * 
791  * Converts a string which is in the encoding used for filenames
792  * into a UTF-8 string.
793  * 
794  * Return value: The converted string, or %NULL on an error.
795  **/
796 gchar*
797 g_filename_to_utf8 (const gchar *opsysstring, 
798                     gint         len,
799                     gint        *bytes_read,
800                     gint        *bytes_written,
801                     GError     **error)
802 {
803 #ifdef G_OS_WIN32
804   return g_locale_to_utf8 (opsysstring, len,
805                            bytes_read, bytes_written,
806                            error);
807 #else
808   if (getenv ("G_BROKEN_FILENAMES"))
809     return g_locale_to_utf8 (opsysstring, len,
810                              bytes_read, bytes_written,
811                              error);
812
813   if (bytes_read || bytes_written)
814     {
815       gint len = strlen (opsysstring);
816
817       if (bytes_read)
818         *bytes_read = len;
819       if (bytes_written)
820         *bytes_written = len;
821     }
822   
823   if (len < 0)
824     return g_strdup (opsysstring);
825   else
826     return g_strndup (opsysstring, len);
827 #endif
828 }
829
830 /**
831  * g_filename_from_utf8:
832  * @utf8string:    a UTF-8 encoded string 
833  * @len:           the length of the string, or -1 if the string is
834  *                 NULL-terminated.
835  * @bytes_read:    location to store the number of bytes in the
836  *                 input string that were successfully converted, or %NULL.
837  *                 Even if the conversion was succesful, this may be 
838  *                 less than len if there were partial characters
839  *                 at the end of the input. If the error
840  *                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
841  *                 stored will the byte fofset after the last valid
842  *                 input sequence.
843  * @bytes_written: the stored in the output buffer (not including the
844  *                 terminating nul.
845  * @error: location to store the error occuring, or %NULL to ignore
846  *                 errors. Any of the errors in #GConvertError may occur.
847  * 
848  * Converts a string from UTF-8 to the encoding used for filenames.
849  * 
850  * Return value: The converted string, or %NULL on an error.
851  **/
852 gchar*
853 g_filename_from_utf8 (const gchar *utf8string,
854                       gint         len,
855                       gint        *bytes_read,
856                       gint        *bytes_written,
857                       GError     **error)
858 {
859 #ifdef G_OS_WIN32
860   return g_locale_from_utf8 (utf8string, len,
861                              bytes_read, bytes_written,
862                              error);
863 #else
864   if (getenv ("G_BROKEN_FILENAMES"))
865     return g_locale_from_utf8 (utf8string, len,
866                                bytes_read, bytes_written,
867                                error);
868
869   if (bytes_read || bytes_written)
870     {
871       gint len = strlen (utf8string);
872
873       if (bytes_read)
874         *bytes_read = len;
875       if (bytes_written)
876         *bytes_written = len;
877     }
878
879   if (len < 0)
880     return g_strdup (utf8string);
881   else
882     return g_strndup (utf8string, len);
883 #endif
884 }
885
886