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