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