Replacement for have_broken_filenames() which consults the environment
[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 "config.h"
24
25 #include <iconv.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "glib.h"
32 #include "gprintfint.h"
33 #include "gthreadinit.h"
34
35 #ifdef G_PLATFORM_WIN32
36 #define STRICT
37 #include <windows.h>
38 #undef STRICT
39 #endif
40
41 #include "glibintl.h"
42
43 #if defined(USE_LIBICONV_GNU) && !defined (_LIBICONV_H)
44 #error GNU libiconv in use but included iconv.h not from libiconv
45 #endif
46 #if !defined(USE_LIBICONV_GNU) && defined (_LIBICONV_H)
47 #error GNU libiconv not in use but included iconv.h is from libiconv
48 #endif
49
50 GQuark 
51 g_convert_error_quark (void)
52 {
53   static GQuark quark;
54   if (!quark)
55     quark = g_quark_from_static_string ("g_convert_error");
56
57   return quark;
58 }
59
60 static gboolean
61 try_conversion (const char *to_codeset,
62                 const char *from_codeset,
63                 iconv_t    *cd)
64 {
65   *cd = iconv_open (to_codeset, from_codeset);
66
67   if (*cd == (iconv_t)-1 && errno == EINVAL)
68     return FALSE;
69   else
70     return TRUE;
71 }
72
73 static gboolean
74 try_to_aliases (const char **to_aliases,
75                 const char  *from_codeset,
76                 iconv_t     *cd)
77 {
78   if (to_aliases)
79     {
80       const char **p = to_aliases;
81       while (*p)
82         {
83           if (try_conversion (*p, from_codeset, cd))
84             return TRUE;
85
86           p++;
87         }
88     }
89
90   return FALSE;
91 }
92
93 extern const char **_g_charset_get_aliases (const char *canonical_name);
94
95 /**
96  * g_iconv_open:
97  * @to_codeset: destination codeset
98  * @from_codeset: source codeset
99  * 
100  * Same as the standard UNIX routine iconv_open(), but
101  * may be implemented via libiconv on UNIX flavors that lack
102  * a native implementation.
103  * 
104  * GLib provides g_convert() and g_locale_to_utf8() which are likely
105  * more convenient than the raw iconv wrappers.
106  * 
107  * Return value: a "conversion descriptor", or (GIConv)-1 if
108  *  opening the converter failed.
109  **/
110 GIConv
111 g_iconv_open (const gchar  *to_codeset,
112               const gchar  *from_codeset)
113 {
114   iconv_t cd;
115   
116   if (!try_conversion (to_codeset, from_codeset, &cd))
117     {
118       const char **to_aliases = _g_charset_get_aliases (to_codeset);
119       const char **from_aliases = _g_charset_get_aliases (from_codeset);
120
121       if (from_aliases)
122         {
123           const char **p = from_aliases;
124           while (*p)
125             {
126               if (try_conversion (to_codeset, *p, &cd))
127                 goto out;
128
129               if (try_to_aliases (to_aliases, *p, &cd))
130                 goto out;
131
132               p++;
133             }
134         }
135
136       if (try_to_aliases (to_aliases, from_codeset, &cd))
137         goto out;
138     }
139
140  out:
141   return (cd == (iconv_t)-1) ? (GIConv)-1 : (GIConv)cd;
142 }
143
144 /**
145  * g_iconv:
146  * @converter: conversion descriptor from g_iconv_open()
147  * @inbuf: bytes to convert
148  * @inbytes_left: inout parameter, bytes remaining to convert in @inbuf
149  * @outbuf: converted output bytes
150  * @outbytes_left: inout parameter, bytes available to fill in @outbuf
151  * 
152  * Same as the standard UNIX routine iconv(), but
153  * may be implemented via libiconv on UNIX flavors that lack
154  * a native implementation.
155  *
156  * GLib provides g_convert() and g_locale_to_utf8() which are likely
157  * more convenient than the raw iconv wrappers.
158  * 
159  * Return value: count of non-reversible conversions, or -1 on error
160  **/
161 size_t 
162 g_iconv (GIConv   converter,
163          gchar  **inbuf,
164          gsize   *inbytes_left,
165          gchar  **outbuf,
166          gsize   *outbytes_left)
167 {
168   iconv_t cd = (iconv_t)converter;
169
170   return iconv (cd, inbuf, inbytes_left, outbuf, outbytes_left);
171 }
172
173 /**
174  * g_iconv_close:
175  * @converter: a conversion descriptor from g_iconv_open()
176  *
177  * Same as the standard UNIX routine iconv_close(), but
178  * may be implemented via libiconv on UNIX flavors that lack
179  * a native implementation. Should be called to clean up
180  * the conversion descriptor from g_iconv_open() when
181  * you are done converting things.
182  *
183  * GLib provides g_convert() and g_locale_to_utf8() which are likely
184  * more convenient than the raw iconv wrappers.
185  * 
186  * Return value: -1 on error, 0 on success
187  **/
188 gint
189 g_iconv_close (GIConv converter)
190 {
191   iconv_t cd = (iconv_t)converter;
192
193   return iconv_close (cd);
194 }
195
196
197 #define ICONV_CACHE_SIZE   (16)
198
199 struct _iconv_cache_bucket {
200   gchar *key;
201   guint32 refcount;
202   gboolean used;
203   GIConv cd;
204 };
205
206 static GList *iconv_cache_list;
207 static GHashTable *iconv_cache;
208 static GHashTable *iconv_open_hash;
209 static guint iconv_cache_size = 0;
210 G_LOCK_DEFINE_STATIC (iconv_cache_lock);
211
212 /* caller *must* hold the iconv_cache_lock */
213 static void
214 iconv_cache_init (void)
215 {
216   static gboolean initialized = FALSE;
217   
218   if (initialized)
219     return;
220   
221   iconv_cache_list = NULL;
222   iconv_cache = g_hash_table_new (g_str_hash, g_str_equal);
223   iconv_open_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
224   
225   initialized = TRUE;
226 }
227
228
229 /**
230  * iconv_cache_bucket_new:
231  * @key: cache key
232  * @cd: iconv descriptor
233  *
234  * Creates a new cache bucket, inserts it into the cache and
235  * increments the cache size.
236  *
237  * Returns a pointer to the newly allocated cache bucket.
238  **/
239 static struct _iconv_cache_bucket *
240 iconv_cache_bucket_new (const gchar *key, GIConv cd)
241 {
242   struct _iconv_cache_bucket *bucket;
243   
244   bucket = g_new (struct _iconv_cache_bucket, 1);
245   bucket->key = g_strdup (key);
246   bucket->refcount = 1;
247   bucket->used = TRUE;
248   bucket->cd = cd;
249   
250   g_hash_table_insert (iconv_cache, bucket->key, bucket);
251   
252   /* FIXME: if we sorted the list so items with few refcounts were
253      first, then we could expire them faster in iconv_cache_expire_unused () */
254   iconv_cache_list = g_list_prepend (iconv_cache_list, bucket);
255   
256   iconv_cache_size++;
257   
258   return bucket;
259 }
260
261
262 /**
263  * iconv_cache_bucket_expire:
264  * @node: cache bucket's node
265  * @bucket: cache bucket
266  *
267  * Expires a single cache bucket @bucket. This should only ever be
268  * called on a bucket that currently has no used iconv descriptors
269  * open.
270  *
271  * @node is not a required argument. If @node is not supplied, we
272  * search for it ourselves.
273  **/
274 static void
275 iconv_cache_bucket_expire (GList *node, struct _iconv_cache_bucket *bucket)
276 {
277   g_hash_table_remove (iconv_cache, bucket->key);
278   
279   if (node == NULL)
280     node = g_list_find (iconv_cache_list, bucket);
281   
282   g_assert (node != NULL);
283   
284   if (node->prev)
285     {
286       node->prev->next = node->next;
287       if (node->next)
288         node->next->prev = node->prev;
289     }
290   else
291     {
292       iconv_cache_list = node->next;
293       if (node->next)
294         node->next->prev = NULL;
295     }
296   
297   g_list_free_1 (node);
298   
299   g_free (bucket->key);
300   g_iconv_close (bucket->cd);
301   g_free (bucket);
302   
303   iconv_cache_size--;
304 }
305
306
307 /**
308  * iconv_cache_expire_unused:
309  *
310  * Expires as many unused cache buckets as it needs to in order to get
311  * the total number of buckets < ICONV_CACHE_SIZE.
312  **/
313 static void
314 iconv_cache_expire_unused (void)
315 {
316   struct _iconv_cache_bucket *bucket;
317   GList *node, *next;
318   
319   node = iconv_cache_list;
320   while (node && iconv_cache_size >= ICONV_CACHE_SIZE)
321     {
322       next = node->next;
323       
324       bucket = node->data;
325       if (bucket->refcount == 0)
326         iconv_cache_bucket_expire (node, bucket);
327       
328       node = next;
329     }
330 }
331
332 static GIConv
333 open_converter (const gchar *to_codeset,
334                 const gchar *from_codeset,
335                 GError     **error)
336 {
337   struct _iconv_cache_bucket *bucket;
338   gchar *key;
339   GIConv cd;
340   
341   /* create our key */
342   key = g_alloca (strlen (from_codeset) + strlen (to_codeset) + 2);
343   _g_sprintf (key, "%s:%s", from_codeset, to_codeset);
344   
345   G_LOCK (iconv_cache_lock);
346   
347   /* make sure the cache has been initialized */
348   iconv_cache_init ();
349   
350   bucket = g_hash_table_lookup (iconv_cache, key);
351   if (bucket)
352     {
353       if (bucket->used)
354         {
355           cd = g_iconv_open (to_codeset, from_codeset);
356           if (cd == (GIConv) -1)
357             goto error;
358         }
359       else
360         {
361           /* Apparently iconv on Solaris <= 7 segfaults if you pass in
362            * NULL for anything but inbuf; work around that. (NULL outbuf
363            * or NULL *outbuf is allowed by Unix98.)
364            */
365           gsize inbytes_left = 0;
366           gchar *outbuf = NULL;
367           gsize outbytes_left = 0;
368                 
369           cd = bucket->cd;
370           bucket->used = TRUE;
371           
372           /* reset the descriptor */
373           g_iconv (cd, NULL, &inbytes_left, &outbuf, &outbytes_left);
374         }
375       
376       bucket->refcount++;
377     }
378   else
379     {
380       cd = g_iconv_open (to_codeset, from_codeset);
381       if (cd == (GIConv) -1)
382         goto error;
383       
384       iconv_cache_expire_unused ();
385       
386       bucket = iconv_cache_bucket_new (key, cd);
387     }
388   
389   g_hash_table_insert (iconv_open_hash, cd, bucket->key);
390   
391   G_UNLOCK (iconv_cache_lock);
392   
393   return cd;
394   
395  error:
396   
397   G_UNLOCK (iconv_cache_lock);
398   
399   /* Something went wrong.  */
400   if (errno == EINVAL)
401     g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
402                  _("Conversion from character set '%s' to '%s' is not supported"),
403                  from_codeset, to_codeset);
404   else
405     g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
406                  _("Could not open converter from '%s' to '%s': %s"),
407                  from_codeset, to_codeset, g_strerror (errno));
408   
409   return cd;
410 }
411
412 static int
413 close_converter (GIConv converter)
414 {
415   struct _iconv_cache_bucket *bucket;
416   const gchar *key;
417   GIConv cd;
418   
419   cd = converter;
420   
421   if (cd == (GIConv) -1)
422     return 0;
423   
424   G_LOCK (iconv_cache_lock);
425   
426   key = g_hash_table_lookup (iconv_open_hash, cd);
427   if (key)
428     {
429       g_hash_table_remove (iconv_open_hash, cd);
430       
431       bucket = g_hash_table_lookup (iconv_cache, key);
432       g_assert (bucket);
433       
434       bucket->refcount--;
435       
436       if (cd == bucket->cd)
437         bucket->used = FALSE;
438       else
439         g_iconv_close (cd);
440       
441       if (!bucket->refcount && iconv_cache_size > ICONV_CACHE_SIZE)
442         {
443           /* expire this cache bucket */
444           iconv_cache_bucket_expire (NULL, bucket);
445         }
446     }
447   else
448     {
449       G_UNLOCK (iconv_cache_lock);
450       
451       g_warning ("This iconv context wasn't opened using open_converter");
452       
453       return g_iconv_close (converter);
454     }
455   
456   G_UNLOCK (iconv_cache_lock);
457   
458   return 0;
459 }
460
461
462 /**
463  * g_convert:
464  * @str:           the string to convert
465  * @len:           the length of the string
466  * @to_codeset:    name of character set into which to convert @str
467  * @from_codeset:  character set of @str.
468  * @bytes_read:    location to store the number of bytes in the
469  *                 input string that were successfully converted, or %NULL.
470  *                 Even if the conversion was successful, this may be 
471  *                 less than @len if there were partial characters
472  *                 at the end of the input. If the error
473  *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
474  *                 stored will the byte offset after the last valid
475  *                 input sequence.
476  * @bytes_written: the number of bytes stored in the output buffer (not 
477  *                 including the terminating nul).
478  * @error:         location to store the error occuring, or %NULL to ignore
479  *                 errors. Any of the errors in #GConvertError may occur.
480  *
481  * Converts a string from one character set to another.
482  *
483  * Return value: If the conversion was successful, a newly allocated
484  *               nul-terminated string, which must be freed with
485  *               g_free(). Otherwise %NULL and @error will be set.
486  **/
487 gchar*
488 g_convert (const gchar *str,
489            gssize       len,  
490            const gchar *to_codeset,
491            const gchar *from_codeset,
492            gsize       *bytes_read, 
493            gsize       *bytes_written, 
494            GError     **error)
495 {
496   gchar *res;
497   GIConv cd;
498   
499   g_return_val_if_fail (str != NULL, NULL);
500   g_return_val_if_fail (to_codeset != NULL, NULL);
501   g_return_val_if_fail (from_codeset != NULL, NULL);
502   
503   cd = open_converter (to_codeset, from_codeset, error);
504
505   if (cd == (GIConv) -1)
506     {
507       if (bytes_read)
508         *bytes_read = 0;
509       
510       if (bytes_written)
511         *bytes_written = 0;
512       
513       return NULL;
514     }
515
516   res = g_convert_with_iconv (str, len, cd,
517                               bytes_read, bytes_written,
518                               error);
519   
520   close_converter (cd);
521
522   return res;
523 }
524
525 /**
526  * g_convert_with_iconv:
527  * @str:           the string to convert
528  * @len:           the length of the string
529  * @converter:     conversion descriptor from g_iconv_open()
530  * @bytes_read:    location to store the number of bytes in the
531  *                 input string that were successfully converted, or %NULL.
532  *                 Even if the conversion was successful, this may be 
533  *                 less than @len if there were partial characters
534  *                 at the end of the input. If the error
535  *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
536  *                 stored will the byte offset after the last valid
537  *                 input sequence.
538  * @bytes_written: the number of bytes stored in the output buffer (not 
539  *                 including the terminating nul).
540  * @error:         location to store the error occuring, or %NULL to ignore
541  *                 errors. Any of the errors in #GConvertError may occur.
542  *
543  * Converts a string from one character set to another.
544  *
545  * Return value: If the conversion was successful, a newly allocated
546  *               nul-terminated string, which must be freed with
547  *               g_free(). Otherwise %NULL and @error will be set.
548  **/
549 gchar*
550 g_convert_with_iconv (const gchar *str,
551                       gssize       len,
552                       GIConv       converter,
553                       gsize       *bytes_read, 
554                       gsize       *bytes_written, 
555                       GError     **error)
556 {
557   gchar *dest;
558   gchar *outp;
559   const gchar *p;
560   gsize inbytes_remaining;
561   gsize outbytes_remaining;
562   gsize err;
563   gsize outbuf_size;
564   gboolean have_error = FALSE;
565   
566   g_return_val_if_fail (str != NULL, NULL);
567   g_return_val_if_fail (converter != (GIConv) -1, NULL);
568      
569   if (len < 0)
570     len = strlen (str);
571
572   p = str;
573   inbytes_remaining = len;
574   outbuf_size = len + 1; /* + 1 for nul in case len == 1 */
575   
576   outbytes_remaining = outbuf_size - 1; /* -1 for nul */
577   outp = dest = g_malloc (outbuf_size);
578
579  again:
580   
581   err = g_iconv (converter, (char **)&p, &inbytes_remaining, &outp, &outbytes_remaining);
582
583   if (err == (size_t) -1)
584     {
585       switch (errno)
586         {
587         case EINVAL:
588           /* Incomplete text, do not report an error */
589           break;
590         case E2BIG:
591           {
592             size_t used = outp - dest;
593
594             outbuf_size *= 2;
595             dest = g_realloc (dest, outbuf_size);
596                 
597             outp = dest + used;
598             outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
599
600             goto again;
601           }
602         case EILSEQ:
603           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
604                        _("Invalid byte sequence in conversion input"));
605           have_error = TRUE;
606           break;
607         default:
608           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
609                        _("Error during conversion: %s"),
610                        g_strerror (errno));
611           have_error = TRUE;
612           break;
613         }
614     }
615
616   *outp = '\0';
617   
618   if (bytes_read)
619     *bytes_read = p - str;
620   else
621     {
622       if ((p - str) != len) 
623         {
624           if (!have_error)
625             {
626               g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
627                            _("Partial character sequence at end of input"));
628               have_error = TRUE;
629             }
630         }
631     }
632
633   if (bytes_written)
634     *bytes_written = outp - dest;       /* Doesn't include '\0' */
635
636   if (have_error)
637     {
638       g_free (dest);
639       return NULL;
640     }
641   else
642     return dest;
643 }
644
645 /**
646  * g_convert_with_fallback:
647  * @str:          the string to convert
648  * @len:          the length of the string
649  * @to_codeset:   name of character set into which to convert @str
650  * @from_codeset: character set of @str.
651  * @fallback:     UTF-8 string to use in place of character not
652  *                present in the target encoding. (This must be
653  *                in the target encoding), if %NULL, characters
654  *                not in the target encoding will be represented
655  *                as Unicode escapes \uxxxx or \Uxxxxyyyy.
656  * @bytes_read:   location to store the number of bytes in the
657  *                input string that were successfully converted, or %NULL.
658  *                Even if the conversion was successful, this may be 
659  *                less than @len if there were partial characters
660  *                at the end of the input.
661  * @bytes_written: the number of bytes stored in the output buffer (not 
662  *                including the terminating nul).
663  * @error:        location to store the error occuring, or %NULL to ignore
664  *                errors. Any of the errors in #GConvertError may occur.
665  *
666  * Converts a string from one character set to another, possibly
667  * including fallback sequences for characters not representable
668  * in the output. Note that it is not guaranteed that the specification
669  * for the fallback sequences in @fallback will be honored. Some
670  * systems may do a approximate conversion from @from_codeset
671  * to @to_codeset in their iconv() functions, 
672  * in which case GLib will simply return that approximate conversion.
673  *
674  * Return value: If the conversion was successful, a newly allocated
675  *               nul-terminated string, which must be freed with
676  *               g_free(). Otherwise %NULL and @error will be set.
677  **/
678 gchar*
679 g_convert_with_fallback (const gchar *str,
680                          gssize       len,    
681                          const gchar *to_codeset,
682                          const gchar *from_codeset,
683                          gchar       *fallback,
684                          gsize       *bytes_read,
685                          gsize       *bytes_written,
686                          GError     **error)
687 {
688   gchar *utf8;
689   gchar *dest;
690   gchar *outp;
691   const gchar *insert_str = NULL;
692   const gchar *p;
693   gsize inbytes_remaining;   
694   const gchar *save_p = NULL;
695   gsize save_inbytes = 0;
696   gsize outbytes_remaining; 
697   gsize err;
698   GIConv cd;
699   gsize outbuf_size;
700   gboolean have_error = FALSE;
701   gboolean done = FALSE;
702
703   GError *local_error = NULL;
704   
705   g_return_val_if_fail (str != NULL, NULL);
706   g_return_val_if_fail (to_codeset != NULL, NULL);
707   g_return_val_if_fail (from_codeset != NULL, NULL);
708      
709   if (len < 0)
710     len = strlen (str);
711   
712   /* Try an exact conversion; we only proceed if this fails
713    * due to an illegal sequence in the input string.
714    */
715   dest = g_convert (str, len, to_codeset, from_codeset, 
716                     bytes_read, bytes_written, &local_error);
717   if (!local_error)
718     return dest;
719
720   if (!g_error_matches (local_error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
721     {
722       g_propagate_error (error, local_error);
723       return NULL;
724     }
725   else
726     g_error_free (local_error);
727
728   local_error = NULL;
729   
730   /* No go; to proceed, we need a converter from "UTF-8" to
731    * to_codeset, and the string as UTF-8.
732    */
733   cd = open_converter (to_codeset, "UTF-8", error);
734   if (cd == (GIConv) -1)
735     {
736       if (bytes_read)
737         *bytes_read = 0;
738       
739       if (bytes_written)
740         *bytes_written = 0;
741       
742       return NULL;
743     }
744
745   utf8 = g_convert (str, len, "UTF-8", from_codeset, 
746                     bytes_read, &inbytes_remaining, error);
747   if (!utf8)
748     {
749       close_converter (cd);
750       if (bytes_written)
751         *bytes_written = 0;
752       return NULL;
753     }
754
755   /* Now the heart of the code. We loop through the UTF-8 string, and
756    * whenever we hit an offending character, we form fallback, convert
757    * the fallback to the target codeset, and then go back to
758    * converting the original string after finishing with the fallback.
759    *
760    * The variables save_p and save_inbytes store the input state
761    * for the original string while we are converting the fallback
762    */
763   p = utf8;
764
765   outbuf_size = len + 1; /* + 1 for nul in case len == 1 */
766   outbytes_remaining = outbuf_size - 1; /* -1 for nul */
767   outp = dest = g_malloc (outbuf_size);
768
769   while (!done && !have_error)
770     {
771       size_t inbytes_tmp = inbytes_remaining;
772       err = g_iconv (cd, (char **)&p, &inbytes_tmp, &outp, &outbytes_remaining);
773       inbytes_remaining = inbytes_tmp;
774
775       if (err == (size_t) -1)
776         {
777           switch (errno)
778             {
779             case EINVAL:
780               g_assert_not_reached();
781               break;
782             case E2BIG:
783               {
784                 size_t used = outp - dest;
785
786                 outbuf_size *= 2;
787                 dest = g_realloc (dest, outbuf_size);
788                 
789                 outp = dest + used;
790                 outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
791                 
792                 break;
793               }
794             case EILSEQ:
795               if (save_p)
796                 {
797                   /* Error converting fallback string - fatal
798                    */
799                   g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
800                                _("Cannot convert fallback '%s' to codeset '%s'"),
801                                insert_str, to_codeset);
802                   have_error = TRUE;
803                   break;
804                 }
805               else
806                 {
807                   if (!fallback)
808                     { 
809                       gunichar ch = g_utf8_get_char (p);
810                       insert_str = g_strdup_printf (ch < 0x10000 ? "\\u%04x" : "\\U%08x",
811                                                     ch);
812                     }
813                   else
814                     insert_str = fallback;
815                   
816                   save_p = g_utf8_next_char (p);
817                   save_inbytes = inbytes_remaining - (save_p - p);
818                   p = insert_str;
819                   inbytes_remaining = strlen (p);
820                 }
821               break;
822             default:
823               g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
824                            _("Error during conversion: %s"),
825                            g_strerror (errno));
826               have_error = TRUE;
827               break;
828             }
829         }
830       else
831         {
832           if (save_p)
833             {
834               if (!fallback)
835                 g_free ((gchar *)insert_str);
836               p = save_p;
837               inbytes_remaining = save_inbytes;
838               save_p = NULL;
839             }
840           else
841             done = TRUE;
842         }
843     }
844
845   /* Cleanup
846    */
847   *outp = '\0';
848   
849   close_converter (cd);
850
851   if (bytes_written)
852     *bytes_written = outp - dest;       /* Doesn't include '\0' */
853
854   g_free (utf8);
855
856   if (have_error)
857     {
858       if (save_p && !fallback)
859         g_free ((gchar *)insert_str);
860       g_free (dest);
861       return NULL;
862     }
863   else
864     return dest;
865 }
866
867 /*
868  * g_locale_to_utf8
869  *
870  * 
871  */
872
873 static gchar *
874 strdup_len (const gchar *string,
875             gssize       len,
876             gsize       *bytes_written,
877             gsize       *bytes_read,
878             GError      **error)
879          
880 {
881   gsize real_len;
882
883   if (!g_utf8_validate (string, len, NULL))
884     {
885       if (bytes_read)
886         *bytes_read = 0;
887       if (bytes_written)
888         *bytes_written = 0;
889
890       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
891                    _("Invalid byte sequence in conversion input"));
892       return NULL;
893     }
894   
895   if (len < 0)
896     real_len = strlen (string);
897   else
898     {
899       real_len = 0;
900       
901       while (real_len < len && string[real_len])
902         real_len++;
903     }
904   
905   if (bytes_read)
906     *bytes_read = real_len;
907   if (bytes_written)
908     *bytes_written = real_len;
909
910   return g_strndup (string, real_len);
911 }
912
913 /**
914  * g_locale_to_utf8:
915  * @opsysstring:   a string in the encoding of the current locale
916  * @len:           the length of the string, or -1 if the string is
917  *                 nul-terminated.
918  * @bytes_read:    location to store the number of bytes in the
919  *                 input string that were successfully converted, or %NULL.
920  *                 Even if the conversion was successful, this may be 
921  *                 less than @len if there were partial characters
922  *                 at the end of the input. If the error
923  *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
924  *                 stored will the byte offset after the last valid
925  *                 input sequence.
926  * @bytes_written: the number of bytes stored in the output buffer (not 
927  *                 including the terminating nul).
928  * @error:         location to store the error occuring, or %NULL to ignore
929  *                 errors. Any of the errors in #GConvertError may occur.
930  * 
931  * Converts a string which is in the encoding used for strings by
932  * the C runtime (usually the same as that used by the operating
933  * system) in the current locale into a UTF-8 string.
934  * 
935  * Return value: The converted string, or %NULL on an error.
936  **/
937 gchar *
938 g_locale_to_utf8 (const gchar  *opsysstring,
939                   gssize        len,            
940                   gsize        *bytes_read,    
941                   gsize        *bytes_written,
942                   GError      **error)
943 {
944   const char *charset;
945
946   if (g_get_charset (&charset))
947     return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
948   else
949     return g_convert (opsysstring, len, 
950                       "UTF-8", charset, bytes_read, bytes_written, error);
951 }
952
953 /**
954  * g_locale_from_utf8:
955  * @utf8string:    a UTF-8 encoded string 
956  * @len:           the length of the string, or -1 if the string is
957  *                 nul-terminated.
958  * @bytes_read:    location to store the number of bytes in the
959  *                 input string that were successfully converted, or %NULL.
960  *                 Even if the conversion was successful, this may be 
961  *                 less than @len if there were partial characters
962  *                 at the end of the input. If the error
963  *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
964  *                 stored will the byte offset after the last valid
965  *                 input sequence.
966  * @bytes_written: the number of bytes stored in the output buffer (not 
967  *                 including the terminating nul).
968  * @error:         location to store the error occuring, or %NULL to ignore
969  *                 errors. Any of the errors in #GConvertError may occur.
970  * 
971  * Converts a string from UTF-8 to the encoding used for strings by
972  * the C runtime (usually the same as that used by the operating
973  * system) in the current locale.
974  * 
975  * Return value: The converted string, or %NULL on an error.
976  **/
977 gchar *
978 g_locale_from_utf8 (const gchar *utf8string,
979                     gssize       len,            
980                     gsize       *bytes_read,    
981                     gsize       *bytes_written,
982                     GError     **error)
983 {
984   const gchar *charset;
985
986   if (g_get_charset (&charset))
987     return strdup_len (utf8string, len, bytes_read, bytes_written, error);
988   else
989     return g_convert (utf8string, len,
990                       charset, "UTF-8", bytes_read, bytes_written, error);
991 }
992
993 #ifndef G_PLATFORM_WIN32
994
995 /*
996  * get_filename_charset:
997  * @charset: return location for the name of the filename encoding 
998  *
999  * Determines the character set used for filenames by consulting the 
1000  * environment variables G_FILENAME_ENCODING and G_BROKEN_FILENAMES. 
1001  *
1002  * G_FILENAME_ENCODING may be set to a comma-separated list of character 
1003  * set names. The special token "@locale" is taken to mean the character set 
1004  * for the current locale. The first character set from the list is taken 
1005  * as the filename encoding. 
1006  * If G_FILENAME_ENCODING is not set, but G_BROKEN_FILENAMES is, the
1007  * character set of the current locale is taken as the filename encoding.
1008  *
1009  * The returned @charset belongs to GLib and must not be freed.
1010  * 
1011  * Return value: %TRUE if the charset used for filename is UTF-8.
1012  */
1013 static gboolean
1014 get_filename_charset (const gchar **charset)
1015 {
1016   static gboolean initialized = FALSE;
1017   static const gchar *filename_charset;
1018   static gboolean is_utf8;
1019   
1020   if (!initialized)
1021     {
1022       gchar *p, *q;
1023
1024       initialized = TRUE;
1025       
1026       p = getenv ("G_FILENAME_ENCODING");
1027       if (p != NULL) 
1028         {
1029           q = strchr (p, ',');
1030           if (!q) 
1031             q = p + strlen (p);
1032
1033           if (strncmp ("@locale", p, q - p) == 0)
1034             is_utf8 = g_get_charset (&filename_charset);
1035           else
1036             {
1037               filename_charset = g_strndup (p, q - p);
1038               is_utf8 = (strcmp (filename_charset, "UTF-8") == 0);
1039             }
1040         }
1041       else if (getenv ("G_BROKEN_FILENAMES") != NULL)
1042         is_utf8 = g_get_charset (&filename_charset);
1043       else 
1044         {
1045           filename_charset = "UTF-8";
1046           is_utf8 = TRUE;
1047         }
1048     }
1049
1050   *charset = filename_charset;
1051
1052   return is_utf8;
1053 }
1054 #else /* G_PLATFORM_WIN32 */
1055 #define get_filename_charset (charset) TRUE
1056 #endif /* G_PLATFORM_WIN32 */
1057
1058 /* This is called from g_thread_init(). It's used to
1059  * initialize some static data in a threadsafe way.
1060  */
1061 void 
1062 _g_convert_thread_init (void)
1063 {
1064   const gchar *dummy;
1065   (void) get_filename_charset (&dummy);
1066 }
1067
1068 /**
1069  * g_filename_to_utf8:
1070  * @opsysstring:   a string in the encoding for filenames
1071  * @len:           the length of the string, or -1 if the string is
1072  *                 nul-terminated.
1073  * @bytes_read:    location to store the number of bytes in the
1074  *                 input string that were successfully converted, or %NULL.
1075  *                 Even if the conversion was successful, this may be 
1076  *                 less than @len if there were partial characters
1077  *                 at the end of the input. If the error
1078  *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
1079  *                 stored will the byte offset after the last valid
1080  *                 input sequence.
1081  * @bytes_written: the number of bytes stored in the output buffer (not 
1082  *                 including the terminating nul).
1083  * @error:         location to store the error occuring, or %NULL to ignore
1084  *                 errors. Any of the errors in #GConvertError may occur.
1085  * 
1086  * Converts a string which is in the encoding used for filenames
1087  * into a UTF-8 string.
1088  * 
1089  * Return value: The converted string, or %NULL on an error.
1090  **/
1091 gchar*
1092 g_filename_to_utf8 (const gchar *opsysstring, 
1093                     gssize       len,           
1094                     gsize       *bytes_read,   
1095                     gsize       *bytes_written,
1096                     GError     **error)
1097 {
1098   const gchar *charset;
1099
1100   if (get_filename_charset (&charset))
1101     return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
1102   else
1103     return g_convert (opsysstring, len, 
1104                       "UTF-8", charset, bytes_read, bytes_written, error);
1105 }
1106
1107 /**
1108  * g_filename_from_utf8:
1109  * @utf8string:    a UTF-8 encoded string.
1110  * @len:           the length of the string, or -1 if the string is
1111  *                 nul-terminated.
1112  * @bytes_read:    location to store the number of bytes in the
1113  *                 input string that were successfully converted, or %NULL.
1114  *                 Even if the conversion was successful, this may be 
1115  *                 less than @len if there were partial characters
1116  *                 at the end of the input. If the error
1117  *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
1118  *                 stored will the byte offset after the last valid
1119  *                 input sequence.
1120  * @bytes_written: the number of bytes stored in the output buffer (not 
1121  *                 including the terminating nul).
1122  * @error:         location to store the error occuring, or %NULL to ignore
1123  *                 errors. Any of the errors in #GConvertError may occur.
1124  * 
1125  * Converts a string from UTF-8 to the encoding used for filenames.
1126  * 
1127  * Return value: The converted string, or %NULL on an error.
1128  **/
1129 gchar*
1130 g_filename_from_utf8 (const gchar *utf8string,
1131                       gssize       len,            
1132                       gsize       *bytes_read,    
1133                       gsize       *bytes_written,
1134                       GError     **error)
1135 {
1136   const gchar *charset;
1137
1138   if (get_filename_charset (&charset))
1139     return strdup_len (utf8string, len, bytes_read, bytes_written, error);
1140   else
1141     return g_convert (utf8string, len,
1142                       charset, "UTF-8", bytes_read, bytes_written, error);
1143 }
1144
1145 /* Test of haystack has the needle prefix, comparing case
1146  * insensitive. haystack may be UTF-8, but needle must
1147  * contain only ascii. */
1148 static gboolean
1149 has_case_prefix (const gchar *haystack, const gchar *needle)
1150 {
1151   const gchar *h, *n;
1152   
1153   /* Eat one character at a time. */
1154   h = haystack;
1155   n = needle;
1156
1157   while (*n && *h &&
1158          g_ascii_tolower (*n) == g_ascii_tolower (*h))
1159     {
1160       n++;
1161       h++;
1162     }
1163   
1164   return *n == '\0';
1165 }
1166
1167 typedef enum {
1168   UNSAFE_ALL        = 0x1,  /* Escape all unsafe characters   */
1169   UNSAFE_ALLOW_PLUS = 0x2,  /* Allows '+'  */
1170   UNSAFE_PATH       = 0x8,  /* Allows '/', '&', '=', ':', '@', '+', '$' and ',' */
1171   UNSAFE_HOST       = 0x10, /* Allows '/' and ':' and '@' */
1172   UNSAFE_SLASHES    = 0x20  /* Allows all characters except for '/' and '%' */
1173 } UnsafeCharacterSet;
1174
1175 static const guchar acceptable[96] = {
1176   /* A table of the ASCII chars from space (32) to DEL (127) */
1177   /*      !    "    #    $    %    &    '    (    )    *    +    ,    -    .    / */ 
1178   0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
1179   /* 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ? */
1180   0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
1181   /* @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O */
1182   0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
1183   /* P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _ */
1184   0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
1185   /* `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o */
1186   0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
1187   /* p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~  DEL */
1188   0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
1189 };
1190
1191 static const gchar hex[16] = "0123456789ABCDEF";
1192
1193 /* Note: This escape function works on file: URIs, but if you want to
1194  * escape something else, please read RFC-2396 */
1195 static gchar *
1196 g_escape_uri_string (const gchar *string, 
1197                      UnsafeCharacterSet mask)
1198 {
1199 #define ACCEPTABLE(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
1200
1201   const gchar *p;
1202   gchar *q;
1203   gchar *result;
1204   int c;
1205   gint unacceptable;
1206   UnsafeCharacterSet use_mask;
1207   
1208   g_return_val_if_fail (mask == UNSAFE_ALL
1209                         || mask == UNSAFE_ALLOW_PLUS
1210                         || mask == UNSAFE_PATH
1211                         || mask == UNSAFE_HOST
1212                         || mask == UNSAFE_SLASHES, NULL);
1213   
1214   unacceptable = 0;
1215   use_mask = mask;
1216   for (p = string; *p != '\0'; p++)
1217     {
1218       c = (guchar) *p;
1219       if (!ACCEPTABLE (c)) 
1220         unacceptable++;
1221     }
1222   
1223   result = g_malloc (p - string + unacceptable * 2 + 1);
1224   
1225   use_mask = mask;
1226   for (q = result, p = string; *p != '\0'; p++)
1227     {
1228       c = (guchar) *p;
1229       
1230       if (!ACCEPTABLE (c))
1231         {
1232           *q++ = '%'; /* means hex coming */
1233           *q++ = hex[c >> 4];
1234           *q++ = hex[c & 15];
1235         }
1236       else
1237         *q++ = *p;
1238     }
1239   
1240   *q = '\0';
1241   
1242   return result;
1243 }
1244
1245
1246 static gchar *
1247 g_escape_file_uri (const gchar *hostname,
1248                    const gchar *pathname)
1249 {
1250   char *escaped_hostname = NULL;
1251   char *escaped_path;
1252   char *res;
1253
1254 #ifdef G_OS_WIN32
1255   char *p, *backslash;
1256
1257   /* Turn backslashes into forward slashes. That's what Netscape
1258    * does, and they are actually more or less equivalent in Windows.
1259    */
1260   
1261   pathname = g_strdup (pathname);
1262   p = (char *) pathname;
1263   
1264   while ((backslash = strchr (p, '\\')) != NULL)
1265     {
1266       *backslash = '/';
1267       p = backslash + 1;
1268     }
1269 #endif
1270
1271   if (hostname && *hostname != '\0')
1272     {
1273       escaped_hostname = g_escape_uri_string (hostname, UNSAFE_HOST);
1274     }
1275
1276   escaped_path = g_escape_uri_string (pathname, UNSAFE_PATH);
1277
1278   res = g_strconcat ("file://",
1279                      (escaped_hostname) ? escaped_hostname : "",
1280                      (*escaped_path != '/') ? "/" : "",
1281                      escaped_path,
1282                      NULL);
1283
1284 #ifdef G_OS_WIN32
1285   g_free ((char *) pathname);
1286 #endif
1287
1288   g_free (escaped_hostname);
1289   g_free (escaped_path);
1290   
1291   return res;
1292 }
1293
1294 static int
1295 unescape_character (const char *scanner)
1296 {
1297   int first_digit;
1298   int second_digit;
1299
1300   first_digit = g_ascii_xdigit_value (scanner[0]);
1301   if (first_digit < 0) 
1302     return -1;
1303   
1304   second_digit = g_ascii_xdigit_value (scanner[1]);
1305   if (second_digit < 0) 
1306     return -1;
1307   
1308   return (first_digit << 4) | second_digit;
1309 }
1310
1311 static gchar *
1312 g_unescape_uri_string (const char *escaped,
1313                        int         len,
1314                        const char *illegal_escaped_characters,
1315                        gboolean    ascii_must_not_be_escaped)
1316 {
1317   const gchar *in, *in_end;
1318   gchar *out, *result;
1319   int c;
1320   
1321   if (escaped == NULL)
1322     return NULL;
1323
1324   if (len < 0)
1325     len = strlen (escaped);
1326
1327   result = g_malloc (len + 1);
1328   
1329   out = result;
1330   for (in = escaped, in_end = escaped + len; in < in_end; in++)
1331     {
1332       c = *in;
1333
1334       if (c == '%')
1335         {
1336           /* catch partial escape sequences past the end of the substring */
1337           if (in + 3 > in_end)
1338             break;
1339
1340           c = unescape_character (in + 1);
1341
1342           /* catch bad escape sequences and NUL characters */
1343           if (c <= 0)
1344             break;
1345
1346           /* catch escaped ASCII */
1347           if (ascii_must_not_be_escaped && c <= 0x7F)
1348             break;
1349
1350           /* catch other illegal escaped characters */
1351           if (strchr (illegal_escaped_characters, c) != NULL)
1352             break;
1353
1354           in += 2;
1355         }
1356
1357       *out++ = c;
1358     }
1359   
1360   g_assert (out - result <= len);
1361   *out = '\0';
1362
1363   if (in != in_end || !g_utf8_validate (result, -1, NULL))
1364     {
1365       g_free (result);
1366       return NULL;
1367     }
1368
1369   return result;
1370 }
1371
1372 static gboolean
1373 is_escalphanum (gunichar c)
1374 {
1375   return c > 0x7F || g_ascii_isalnum (c);
1376 }
1377
1378 static gboolean
1379 is_escalpha (gunichar c)
1380 {
1381   return c > 0x7F || g_ascii_isalpha (c);
1382 }
1383
1384 /* allows an empty string */
1385 static gboolean
1386 hostname_validate (const char *hostname)
1387 {
1388   const char *p;
1389   gunichar c, first_char, last_char;
1390
1391   p = hostname;
1392   if (*p == '\0')
1393     return TRUE;
1394   do
1395     {
1396       /* read in a label */
1397       c = g_utf8_get_char (p);
1398       p = g_utf8_next_char (p);
1399       if (!is_escalphanum (c))
1400         return FALSE;
1401       first_char = c;
1402       do
1403         {
1404           last_char = c;
1405           c = g_utf8_get_char (p);
1406           p = g_utf8_next_char (p);
1407         }
1408       while (is_escalphanum (c) || c == '-');
1409       if (last_char == '-')
1410         return FALSE;
1411       
1412       /* if that was the last label, check that it was a toplabel */
1413       if (c == '\0' || (c == '.' && *p == '\0'))
1414         return is_escalpha (first_char);
1415     }
1416   while (c == '.');
1417   return FALSE;
1418 }
1419
1420 /**
1421  * g_filename_from_uri:
1422  * @uri: a uri describing a filename (escaped, encoded in UTF-8).
1423  * @hostname: Location to store hostname for the URI, or %NULL.
1424  *            If there is no hostname in the URI, %NULL will be
1425  *            stored in this location.
1426  * @error: location to store the error occuring, or %NULL to ignore
1427  *         errors. Any of the errors in #GConvertError may occur.
1428  * 
1429  * Converts an escaped UTF-8 encoded URI to a local filename in the
1430  * encoding used for filenames. 
1431  * 
1432  * Return value: a newly-allocated string holding the resulting
1433  *               filename, or %NULL on an error.
1434  **/
1435 gchar *
1436 g_filename_from_uri (const gchar *uri,
1437                      gchar      **hostname,
1438                      GError     **error)
1439 {
1440   const char *path_part;
1441   const char *host_part;
1442   char *unescaped_hostname;
1443   char *result;
1444   char *filename;
1445   int offs;
1446 #ifdef G_OS_WIN32
1447   char *p, *slash;
1448 #endif
1449
1450   if (hostname)
1451     *hostname = NULL;
1452
1453   if (!has_case_prefix (uri, "file:/"))
1454     {
1455       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
1456                    _("The URI '%s' is not an absolute URI using the file scheme"),
1457                    uri);
1458       return NULL;
1459     }
1460   
1461   path_part = uri + strlen ("file:");
1462   
1463   if (strchr (path_part, '#') != NULL)
1464     {
1465       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
1466                    _("The local file URI '%s' may not include a '#'"),
1467                    uri);
1468       return NULL;
1469     }
1470         
1471   if (has_case_prefix (path_part, "///")) 
1472     path_part += 2;
1473   else if (has_case_prefix (path_part, "//"))
1474     {
1475       path_part += 2;
1476       host_part = path_part;
1477
1478       path_part = strchr (path_part, '/');
1479
1480       if (path_part == NULL)
1481         {
1482           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
1483                        _("The URI '%s' is invalid"),
1484                        uri);
1485           return NULL;
1486         }
1487
1488       unescaped_hostname = g_unescape_uri_string (host_part, path_part - host_part, "", TRUE);
1489
1490       if (unescaped_hostname == NULL ||
1491           !hostname_validate (unescaped_hostname))
1492         {
1493           g_free (unescaped_hostname);
1494           g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
1495                        _("The hostname of the URI '%s' is invalid"),
1496                        uri);
1497           return NULL;
1498         }
1499       
1500       if (hostname)
1501         *hostname = unescaped_hostname;
1502       else
1503         g_free (unescaped_hostname);
1504     }
1505
1506   filename = g_unescape_uri_string (path_part, -1, "/", FALSE);
1507
1508   if (filename == NULL)
1509     {
1510       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
1511                    _("The URI '%s' contains invalidly escaped characters"),
1512                    uri);
1513       return NULL;
1514     }
1515
1516   offs = 0;
1517 #ifdef G_OS_WIN32
1518   /* Drop localhost */
1519   if (hostname && *hostname != NULL &&
1520       g_ascii_strcasecmp (*hostname, "localhost") == 0)
1521     {
1522       g_free (*hostname);
1523       *hostname = NULL;
1524     }
1525
1526   /* Turn slashes into backslashes, because that's the canonical spelling */
1527   p = filename;
1528   while ((slash = strchr (p, '/')) != NULL)
1529     {
1530       *slash = '\\';
1531       p = slash + 1;
1532     }
1533
1534   /* Windows URIs with a drive letter can be like "file://host/c:/foo"
1535    * or "file://host/c|/foo" (some Netscape versions). In those cases, start
1536    * the filename from the drive letter.
1537    */
1538   if (g_ascii_isalpha (filename[1]))
1539     {
1540       if (filename[2] == ':')
1541         offs = 1;
1542       else if (filename[2] == '|')
1543         {
1544           filename[2] = ':';
1545           offs = 1;
1546         }
1547     }
1548 #endif
1549   
1550   result = g_filename_from_utf8 (filename + offs, -1, NULL, NULL, error);
1551   g_free (filename);
1552   
1553   return result;
1554 }
1555
1556 /**
1557  * g_filename_to_uri:
1558  * @filename: an absolute filename specified in the encoding
1559  *            used for filenames by the operating system.
1560  * @hostname: A UTF-8 encoded hostname, or %NULL for none.
1561  * @error: location to store the error occuring, or %NULL to ignore
1562  *         errors. Any of the errors in #GConvertError may occur.
1563  * 
1564  * Converts an absolute filename to an escaped UTF-8 encoded URI.
1565  * 
1566  * Return value: a newly-allocated string holding the resulting
1567  *               URI, or %NULL on an error.
1568  **/
1569 gchar *
1570 g_filename_to_uri   (const gchar *filename,
1571                      const gchar *hostname,
1572                      GError     **error)
1573 {
1574   char *escaped_uri;
1575   char *utf8_filename;
1576
1577   g_return_val_if_fail (filename != NULL, NULL);
1578
1579   if (!g_path_is_absolute (filename))
1580     {
1581       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH,
1582                    _("The pathname '%s' is not an absolute path"),
1583                    filename);
1584       return NULL;
1585     }
1586
1587   if (hostname &&
1588       !(g_utf8_validate (hostname, -1, NULL)
1589         && hostname_validate (hostname)))
1590     {
1591       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
1592                    _("Invalid hostname"));
1593       return NULL;
1594     }
1595   
1596   utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, error);
1597   if (utf8_filename == NULL)
1598     return NULL;
1599   
1600 #ifdef G_OS_WIN32
1601   /* Don't use localhost unnecessarily */
1602   if (hostname && g_ascii_strcasecmp (hostname, "localhost") == 0)
1603     hostname = NULL;
1604 #endif
1605
1606   escaped_uri = g_escape_file_uri (hostname,
1607                                    utf8_filename);
1608   g_free (utf8_filename);
1609   
1610   return escaped_uri;
1611 }
1612