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