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