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