Bumps documentation to 93% symbol coverage, touching most
[platform/upstream/glib.git] / gio / gcontenttype.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include <config.h>
24 #include <sys/types.h>
25 #include <dirent.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include "gcontenttypeprivate.h"
29 #include "glibintl.h"
30
31 /* This really sucks. GTK-Doc isn't smart enough to realize there are two
32  * different versions of the functions within this file, based on the platform. 
33  * I can move the definitions out of this file, but that's ugly. 
34  */
35
36 /**
37  * SECTION:gcontenttype
38  * @short_description: platform specific content typing
39  *
40  * A content type is a platform specific string that defines the type
41  * of a file. On unix it is a mime type, on win32 it is an extension string
42  * like ".doc", ".txt" or a percieved string like "audio". Such strings
43  * can be looked up in the registry at HKEY_CLASSES_ROOT.
44  **/
45
46 #ifdef G_OS_WIN32
47
48 #include <windows.h>
49
50 static char *
51 get_registry_classes_key (const char *subdir,
52                           const wchar_t *key_name)
53 {
54   wchar_t *wc_key;
55   HKEY reg_key = NULL;
56   DWORD key_type;
57   DWORD nbytes;
58   char *value_utf8;
59
60   value_utf8 = NULL;
61   
62   nbytes = 0;
63   wc_key = g_utf8_to_utf16 (subdir, -1, NULL, NULL, NULL);
64   if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
65                      KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS &&
66       RegQueryValueExW (reg_key, key_name, 0,
67                         &key_type, NULL, &nbytes) == ERROR_SUCCESS &&
68       key_type == REG_SZ)
69     {
70       wchar_t *wc_temp = g_new (wchar_t, (nbytes+1)/2 + 1);
71       RegQueryValueExW (reg_key, key_name, 0,
72                         &key_type, (LPBYTE) wc_temp, &nbytes);
73       wc_temp[nbytes/2] = '\0';
74       value_utf8 = g_utf16_to_utf8 (wc_temp, -1, NULL, NULL, NULL);
75       g_free (wc_temp);
76     }
77   g_free (wc_key);
78   
79   if (reg_key != NULL)
80     RegCloseKey (reg_key);
81
82   return value_utf8;
83 }
84
85 /**
86  * g_content_type_equals:
87  * @type1: a content type string.
88  * @type2: a content type string.
89  *
90  * Compares two content types for equality.
91  *
92  * Returns: %TRUE if the two strings are identical or equivalent,
93  * %FALSE otherwise.
94  **/  
95 gboolean
96 g_content_type_equals (const char *type1,
97                        const char *type2)
98 {
99   char *progid1, *progid2;
100   gboolean res;
101   
102   g_return_val_if_fail (type1 != NULL, FALSE);
103   g_return_val_if_fail (type2 != NULL, FALSE);
104
105   if (g_ascii_strcasecmp (type1, type2) == 0)
106     return TRUE;
107
108   res = FALSE;
109   progid1 = get_registry_classes_key (type1, NULL);
110   progid2 = get_registry_classes_key (type2, NULL);
111   if (progid1 != NULL && progid2 != NULL &&
112       strcmp (progid1, progid2) == 0)
113     res = TRUE;
114   g_free (progid1);
115   g_free (progid2);
116   
117   return res;
118 }
119
120 /**
121  * g_content_type_is_a:
122  * @type: a content type string. 
123  * @supertype: a string.
124  *
125  * Determines if @type is a subset of @supertype.  
126  *
127  * Returns: %TRUE if @type is a kind of @supertype,
128  * %FALSE otherwise. 
129  **/  
130 gboolean
131 g_content_type_is_a (const char   *type,
132                      const char   *supertype)
133 {
134   gboolean res;
135   char *value_utf8;
136
137   g_return_val_if_fail (type != NULL, FALSE);
138   g_return_val_if_fail (supertype != NULL, FALSE);
139
140   if (g_content_type_equals (type, supertype))
141     return TRUE;
142
143   res = FALSE;
144   value_utf8 = get_registry_classes_key (type, L"PerceivedType");
145   if (value_utf8 && strcmp (value_utf8, supertype) == 0)
146     res = TRUE;
147   g_free (value_utf8);
148   
149   return res;
150 }
151
152 /**
153  * g_content_type_is_unknown:
154  * @type: a content type string. 
155  * 
156  * Checks if the content type is known by GIO.
157  * 
158  * Returns: %TRUE if the type is unknown.
159  **/  
160 gboolean
161 g_content_type_is_unknown (const char *type)
162 {
163   g_return_val_if_fail (type != NULL, FALSE);
164
165   return strcmp ("*", type) == 0;
166 }
167
168 /**
169  * g_content_type_get_description:
170  * @type: a content type string. 
171  * 
172  * Gets the human readable description of the content type.
173  * 
174  * Returns: a short description of the content type @type. 
175  **/  
176 char *
177 g_content_type_get_description (const char *type)
178 {
179   char *progid;
180   char *description;
181
182   g_return_val_if_fail (type != NULL, NULL);
183
184   progid = get_registry_classes_key (type, NULL);
185   if (progid)
186     {
187       description = get_registry_classes_key (progid, NULL);
188       g_free (progid);
189
190       if (description)
191         return description;
192     }
193
194   if (g_content_type_is_unknown (type))
195     return g_strdup (_("Unknown type"));
196   return g_strdup_printf (_("%s filetype"), type);
197 }
198
199 /**
200  * g_content_type_get_mime_type:
201  * @type: a content type string. 
202  * 
203  * Gets the mime-type for the content type.
204  * 
205  * Returns: the registered mime-type for the given @type.
206  **/  
207 char *
208 g_content_type_get_mime_type (const char   *type)
209 {
210   char *mime;
211
212   g_return_val_if_fail (type != NULL, NULL);
213
214   mime = get_registry_classes_key (type, L"Content Type");
215   if (mime)
216     return mime;
217   else if (g_content_type_is_unknown (type))
218     return g_strdup ("application/octet-stream");
219   else if (*type == '.')
220     return g_strdup_printf ("application/x-ext-%s", type+1);
221   /* TODO: Map "image" to "image/ *", etc? */
222
223   return g_strdup ("application/octet-stream");
224 }
225
226 /**
227  * g_content_type_get_icon:
228  * @type: a content type string.
229  * 
230  * Gets the icon for a content type.
231  * 
232  * Returns: #GIcon corresponding to the content type.
233  **/  
234 GIcon *
235 g_content_type_get_icon (const char   *type)
236 {
237   g_return_val_if_fail (type != NULL, NULL);
238
239   /* TODO: How do we represent icons???
240      In the registry they are the default value of
241      HKEY_CLASSES_ROOT\<progid>\DefaultIcon with typical values like:
242      <type>: <value>
243      REG_EXPAND_SZ: %SystemRoot%\System32\Wscript.exe,3
244      REG_SZ: shimgvw.dll,3
245   */
246   return NULL;
247 }
248
249 /**
250  * g_content_type_can_be_executable:
251  * @type: a content type string.
252  * 
253  * Checks if a content type can be executable. Note that for instance
254  * things like text files can be executables (i.e. scripts and batch files).
255  * 
256  * Returns: %TRUE if the file type corresponds to a type that
257  * can be executable, %FALSE otherwise. 
258  **/  
259 gboolean
260 g_content_type_can_be_executable (const char   *type)
261 {
262   g_return_val_if_fail (type != NULL, FALSE);
263
264   if (strcmp (type, ".exe") == 0 ||
265       strcmp (type, ".com") == 0 ||
266       strcmp (type, ".bat") == 0)
267     return TRUE;
268   return FALSE;
269 }
270
271 static gboolean
272 looks_like_text (const guchar *data, gsize data_size)
273 {
274   gsize i;
275   guchar c;
276   for (i = 0; i < data_size; i++)
277     {
278       c = data[i];
279       if (g_ascii_iscntrl (c) && !g_ascii_isspace (c))
280         return FALSE;
281     }
282   return TRUE;
283 }
284
285 /**
286  * g_content_type_guess:
287  * @filename: a string.
288  * @data: a stream of data.
289  * @data_size: the size of @data.
290  * @result_uncertain: a flag indicating the certainty of the 
291  * result.
292  * 
293  * Guesses the content type based on example data. If the function is 
294  * uncertain, @result_uncertain will be set to %TRUE.
295  * 
296  * Returns: a string indicating a guessed content type for the 
297  * given data. 
298  **/  
299 char *
300 g_content_type_guess (const char   *filename,
301                       const guchar *data,
302                       gsize         data_size,
303                       gboolean     *result_uncertain)
304 {
305   char *basename;
306   char *type;
307   char *dot;
308
309   type = NULL;
310
311   if (filename)
312     {
313       basename = g_path_get_basename (filename);
314       dot = strrchr (basename, '.');
315       if (dot)
316         type = g_strdup (dot);
317       g_free (basename);
318     }
319
320   if (type)
321     return type;
322
323   if (data && looks_like_text (data, data_size))
324     return g_strdup (".txt");
325
326   return g_strdup ("*");
327 }
328
329 /**
330  * g_content_types_get_registered:
331  * 
332  * Gets a list of strings containing the registered content types on 
333  * the system.
334  * 
335  * Returns: #GList of the registered content types.
336  **/  
337 GList *
338 g_content_types_get_registered (void)
339 {
340   DWORD index;
341   wchar_t keyname[256];
342   DWORD key_len;
343   char *key_utf8;
344   GList *types;
345
346   types = NULL;
347   index = 0;
348   key_len = 256;
349   while (RegEnumKeyExW(HKEY_CLASSES_ROOT,
350                        index,
351                        keyname,
352                        &key_len,
353                        NULL,
354                        NULL,
355                        NULL,
356                        NULL) == ERROR_SUCCESS)
357     {
358       key_utf8 = g_utf16_to_utf8 (keyname, -1, NULL, NULL, NULL);
359       if (key_utf8)
360         {
361           if (*key_utf8 == '.')
362             types = g_list_prepend (types, key_utf8);
363           else
364             g_free (key_utf8);
365         }
366       index++;
367       key_len = 256;
368     }
369   
370   return g_list_reverse (types);
371 }
372
373 #else /* !G_OS_WIN32 - Unix specific version */
374
375 #define XDG_PREFIX _gio_xdg
376 #include "xdgmime/xdgmime.h"
377
378 /* We lock this mutex whenever we modify global state in this module.  */
379 G_LOCK_DEFINE_STATIC (gio_xdgmime);
380
381 gsize
382 _g_unix_content_type_get_sniff_len (void)
383 {
384   gsize size;
385
386   G_LOCK (gio_xdgmime);
387   size = xdg_mime_get_max_buffer_extents ();
388   G_UNLOCK (gio_xdgmime);
389
390   return size;
391 }
392
393 char *
394 _g_unix_content_type_unalias (const char *type)
395 {
396   char *res;
397   
398   G_LOCK (gio_xdgmime);
399   res = g_strdup (xdg_mime_unalias_mime_type (type));
400   G_UNLOCK (gio_xdgmime);
401   
402   return res;
403 }
404
405 char **
406 _g_unix_content_type_get_parents (const char *type)
407 {
408   const char *umime;
409   const char **parents;
410   GPtrArray *array;
411   int i;
412
413   array = g_ptr_array_new ();
414   
415   G_LOCK (gio_xdgmime);
416   
417   umime = xdg_mime_unalias_mime_type (type);
418   g_ptr_array_add (array, g_strdup (umime));
419   
420   parents = xdg_mime_get_mime_parents (umime);
421   for (i = 0; parents && parents[i] != NULL; i++)
422     g_ptr_array_add (array, g_strdup (parents[i]));
423   
424   G_UNLOCK (gio_xdgmime);
425   
426   g_ptr_array_add (array, NULL);
427   
428   return (char **)g_ptr_array_free (array, FALSE);
429 }
430
431 gboolean
432 g_content_type_equals (const char   *type1,
433                        const char   *type2)
434 {
435   gboolean res;
436   
437   g_return_val_if_fail (type1 != NULL, FALSE);
438   g_return_val_if_fail (type2 != NULL, FALSE);
439   
440   G_LOCK (gio_xdgmime);
441   res = xdg_mime_mime_type_equal (type1, type2);
442   G_UNLOCK (gio_xdgmime);
443         
444   return res;
445 }
446
447 gboolean
448 g_content_type_is_a (const char   *type,
449                      const char   *supertype)
450 {
451   gboolean res;
452     
453   g_return_val_if_fail (type != NULL, FALSE);
454   g_return_val_if_fail (supertype != NULL, FALSE);
455   
456   G_LOCK (gio_xdgmime);
457   res = xdg_mime_mime_type_subclass (type, supertype);
458   G_UNLOCK (gio_xdgmime);
459         
460   return res;
461 }
462
463 gboolean
464 g_content_type_is_unknown (const char *type)
465 {
466   g_return_val_if_fail (type != NULL, FALSE);
467
468   return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0;
469 }
470
471
472 typedef enum {
473   MIME_TAG_TYPE_OTHER,
474   MIME_TAG_TYPE_COMMENT
475 } MimeTagType;
476
477 typedef struct {
478   int current_type;
479   int current_lang_level;
480   int comment_lang_level;
481   char *comment;
482 } MimeParser;
483
484
485 static int
486 language_level (const char *lang)
487 {
488   const char * const *lang_list;
489   int i;
490   
491   /* The returned list is sorted from most desirable to least
492      desirable and always contains the default locale "C". */
493   lang_list = g_get_language_names ();
494   
495   for (i = 0; lang_list[i]; i++)
496     if (strcmp (lang_list[i], lang) == 0)
497       return 1000-i;
498   
499   return 0;
500 }
501
502 static void
503 mime_info_start_element (GMarkupParseContext *context,
504                          const gchar         *element_name,
505                          const gchar        **attribute_names,
506                          const gchar        **attribute_values,
507                          gpointer             user_data,
508                          GError             **error)
509 {
510   int i;
511   const char *lang;
512   MimeParser *parser = user_data;
513   
514   if (strcmp (element_name, "comment") == 0)
515     {
516       lang = "C";
517       for (i = 0; attribute_names[i]; i++)
518         if (strcmp (attribute_names[i], "xml:lang") == 0)
519           {
520             lang = attribute_values[i];
521             break;
522           }
523       
524       parser->current_lang_level = language_level (lang);
525       parser->current_type = MIME_TAG_TYPE_COMMENT;
526     }
527   else
528     parser->current_type = MIME_TAG_TYPE_OTHER;
529   
530 }
531
532 static void
533 mime_info_end_element (GMarkupParseContext *context,
534                        const gchar         *element_name,
535                        gpointer             user_data,
536                        GError             **error)
537 {
538   MimeParser *parser = user_data;
539   
540   parser->current_type = MIME_TAG_TYPE_OTHER;
541 }
542
543 static void
544 mime_info_text (GMarkupParseContext *context,
545                 const gchar         *text,
546                 gsize                text_len,  
547                 gpointer             user_data,
548                 GError             **error)
549 {
550   MimeParser *parser = user_data;
551
552   if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
553       parser->current_lang_level > parser->comment_lang_level)
554     {
555       g_free (parser->comment);
556       parser->comment = g_strndup (text, text_len);
557       parser->comment_lang_level = parser->current_lang_level;
558     }
559 }
560
561 static char *
562 load_comment_for_mime_helper (const char *dir, const char *basename)
563 {
564   GMarkupParseContext *context;
565   char *filename, *data;
566   gsize len;
567   gboolean res;
568   MimeParser parse_data = {0};
569   GMarkupParser parser = {
570     mime_info_start_element,
571     mime_info_end_element,
572     mime_info_text
573   };
574
575   filename = g_build_filename (dir, "mime", basename, NULL);
576   
577   res = g_file_get_contents (filename,  &data,  &len,  NULL);
578   g_free (filename);
579   if (!res)
580     return NULL;
581   
582   context = g_markup_parse_context_new   (&parser, 0, &parse_data, NULL);
583   res = g_markup_parse_context_parse (context, data, len, NULL);
584   g_free (data);
585   g_markup_parse_context_free (context);
586   
587   if (!res)
588     return NULL;
589
590   return parse_data.comment;
591 }
592
593
594 static char *
595 load_comment_for_mime (const char *mimetype)
596 {
597   const char * const* dirs;
598   char *basename;
599   char *comment;
600   int i;
601
602   basename = g_strdup_printf ("%s.xml", mimetype);
603
604   comment = load_comment_for_mime_helper (g_get_user_data_dir (), basename);
605   if (comment)
606     {
607       g_free (basename);
608       return comment;
609     }
610   
611   dirs = g_get_system_data_dirs ();
612
613   for (i = 0; dirs[i] != NULL; i++)
614     {
615       comment = load_comment_for_mime_helper (dirs[i], basename);
616       if (comment)
617         {
618           g_free (basename);
619           return comment;
620         }
621     }
622   g_free (basename);
623   
624   return g_strdup_printf (_("%s type"), mimetype);
625 }
626
627 char *
628 g_content_type_get_description (const char *type)
629 {
630   static GHashTable *type_comment_cache = NULL;
631   char *comment;
632
633   g_return_val_if_fail (type != NULL, NULL);
634   
635   G_LOCK (gio_xdgmime);
636   if (type_comment_cache == NULL)
637     type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
638
639   comment = g_hash_table_lookup (type_comment_cache, type);
640   comment = g_strdup (comment);
641   G_UNLOCK (gio_xdgmime);
642   
643   if (comment != NULL)
644     return comment;
645
646   comment = load_comment_for_mime (type);
647   
648   G_LOCK (gio_xdgmime);
649   g_hash_table_insert (type_comment_cache,
650                        g_strdup (type),
651                        g_strdup (comment));
652   G_UNLOCK (gio_xdgmime);
653
654   return comment;
655 }
656
657 char *
658 g_content_type_get_mime_type (const char   *type)
659 {
660   g_return_val_if_fail (type != NULL, NULL);
661
662   return g_strdup (type);
663 }
664
665 GIcon *
666 g_content_type_get_icon (const char   *type)
667 {
668   g_return_val_if_fail (type != NULL, NULL);
669
670   /* TODO: Implement */
671   return NULL;
672 }
673
674 /**
675  * g_content_type_can_be_executable:
676  * @type: a content type string.
677  * 
678  * Returns: %TRUE if the file type corresponds to something that
679  * can be executable, %FALSE otherwise. Note that for instance
680  * things like textfiles can be executables (i.e. scripts)
681  **/  
682 gboolean
683 g_content_type_can_be_executable (const char   *type)
684 {
685   g_return_val_if_fail (type != NULL, FALSE);
686
687   if (g_content_type_is_a (type, "application/x-executable")  ||
688       g_content_type_is_a (type, "text/plain"))
689     return TRUE;
690
691   return FALSE;
692 }
693
694 static gboolean
695 looks_like_text (const guchar *data, gsize data_size)
696 {
697   gsize i;
698   for (i = 0; i < data_size; i++)
699     {
700       if g_ascii_iscntrl (data[i])
701         return FALSE;
702     }
703   return TRUE;
704 }
705
706 char *
707 g_content_type_guess (const char   *filename,
708                       const guchar *data,
709                       gsize         data_size,
710                       gboolean     *result_uncertain)
711 {
712   char *basename;
713   const char *name_mimetypes[10], *sniffed_mimetype;
714   char *mimetype;
715   int i;
716   int n_name_mimetypes;
717   int sniffed_prio;
718
719   sniffed_prio = 0;
720   n_name_mimetypes = 0;
721   sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
722
723   if (result_uncertain)
724     *result_uncertain = FALSE;
725   
726   G_LOCK (gio_xdgmime);
727   
728   if (filename)
729     {
730       basename = g_path_get_basename (filename);
731       n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
732       g_free (basename);
733     }
734
735   /* Got an extension match, and no conflicts. This is it. */
736   if (n_name_mimetypes == 1)
737     {
738       G_UNLOCK (gio_xdgmime);
739       return g_strdup (name_mimetypes[0]);
740     }
741   
742   if (data)
743     {
744       sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio);
745       if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
746           data &&
747           looks_like_text (data, data_size))
748         sniffed_mimetype = "text/plain";
749     }
750   
751   if (n_name_mimetypes == 0)
752     {
753       if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
754           result_uncertain)
755         *result_uncertain = TRUE;
756       
757       mimetype = g_strdup (sniffed_mimetype);
758     }
759   else
760     {
761       mimetype = NULL;
762       if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
763         {
764           if (sniffed_prio >= 80) /* High priority sniffing match, use that */
765             mimetype = g_strdup (sniffed_mimetype);
766           else
767             {
768               /* There are conflicts between the name matches and we have a sniffed
769                  type, use that as a tie breaker. */
770               
771               for (i = 0; i < n_name_mimetypes; i++)
772                 {
773                   if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype))
774                     {
775                       /* This nametype match is derived from (or the same as) the sniffed type).
776                          This is probably it. */
777                       mimetype = g_strdup (name_mimetypes[i]);
778                       break;
779                     }
780                 }
781             }
782         }
783       
784       if (mimetype == NULL)
785         {
786           /* Conflicts, and sniffed type was no help or not there. guess on the first one */
787           mimetype = g_strdup (name_mimetypes[0]);
788           if (result_uncertain)
789             *result_uncertain = TRUE;
790         }
791     }
792   
793   G_UNLOCK (gio_xdgmime);
794
795   return mimetype;
796 }
797
798 static gboolean
799 foreach_mimetype (gpointer  key,
800                   gpointer  value,
801                   gpointer  user_data)
802 {
803   GList **l = user_data;
804
805   *l = g_list_prepend (*l, (char *)key);
806   return TRUE;
807 }
808
809 static void
810 enumerate_mimetypes_subdir (const char *dir, const char *prefix, GHashTable *mimetypes)
811 {
812   DIR *d;
813   struct dirent *ent;
814   char *mimetype;
815
816   d = opendir (dir);
817   if (d)
818     {
819       while ((ent = readdir (d)) != NULL)
820         {
821           if (g_str_has_suffix (ent->d_name, ".xml"))
822             {
823               mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name);
824               g_hash_table_insert (mimetypes, mimetype, NULL);
825             }
826         }
827       closedir (d);
828     }
829 }
830
831 static void
832 enumerate_mimetypes_dir (const char *dir, GHashTable *mimetypes)
833 {
834   DIR *d;
835   struct dirent *ent;
836   char *mimedir;
837   char *name;
838
839   mimedir = g_build_filename (dir, "mime", NULL);
840   
841   d = opendir (mimedir);
842   if (d)
843     {
844       while ((ent = readdir (d)) != NULL)
845         {
846           if (strcmp (ent->d_name, "packages") != 0)
847             {
848               name = g_build_filename (mimedir, ent->d_name, NULL);
849               if (g_file_test (name, G_FILE_TEST_IS_DIR))
850                 enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
851               g_free (name);
852             }
853         }
854       closedir (d);
855     }
856   
857   g_free (mimedir);
858 }
859
860 GList *
861 g_content_types_get_registered (void)
862 {
863   const char * const* dirs;
864   GHashTable *mimetypes;
865   int i;
866   GList *l;
867
868   mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
869
870   enumerate_mimetypes_dir (g_get_user_data_dir (), mimetypes);
871   dirs = g_get_system_data_dirs ();
872
873   for (i = 0; dirs[i] != NULL; i++)
874     enumerate_mimetypes_dir (dirs[i], mimetypes);
875
876   l = NULL;
877   g_hash_table_foreach_steal (mimetypes, foreach_mimetype, &l);
878   g_hash_table_destroy (mimetypes);
879
880   return l;
881 }
882
883 #endif /* Unix version */