ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-mime-info.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* gnome-vfs-mime-info.c - GNOME mime-information implementation.
4
5    Copyright (C) 1998 Miguel de Icaza
6    Copyright (C) 2000, 2001 Eazel, Inc.
7    All rights reserved.
8
9    The Gnome Library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Library General Public License as
11    published by the Free Software Foundation; either version 2 of the
12    License, or (at your option) any later version.
13
14    The Gnome Library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Library General Public License for more details.
18
19    You should have received a copy of the GNU Library General Public
20    License along with the Gnome Library; see the file COPYING.LIB.  If not,
21    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22    Boston, MA 02111-1307, USA.
23
24    Authors:
25    Miguel De Icaza <miguel@helixcode.com>
26    Mathieu Lacage <mathieu@eazel.com>
27 */
28
29 #include <config.h>
30 #include "gnome-vfs-mime-info.h"
31
32 #include "gnome-vfs-mime-monitor.h"
33 #include "gnome-vfs-mime-private.h"
34 #include "gnome-vfs-mime.h"
35 #include "gnome-vfs-private-utils.h"
36 #include <dirent.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
41 #include <sys/types.h>
42 #include <time.h>
43 #include <unistd.h>
44
45 #ifdef NEED_GNOMESUPPORT_H
46 #include "gnomesupport.h"
47 #endif
48
49 #define FAST_FILE_EOF -1
50 #define FAST_FILE_BUFFER_SIZE (1024 * 16)
51
52 typedef struct {
53         guchar *ptr;
54         guchar *buffer;
55         int     length;
56         FILE   *fh;
57 } FastFile;
58
59 static void
60 fast_file_close (FastFile *ffh)
61 {
62         fclose (ffh->fh);
63         g_free (ffh->buffer);
64 }
65
66 static gboolean
67 fast_file_open (FastFile *ffh, const char *filename)
68 {
69         memset (ffh, 0, sizeof (FastFile));
70
71         ffh->fh = fopen (filename, "r");
72         if (ffh->fh == NULL) {
73                 return FALSE;
74         }
75
76         ffh->buffer = g_malloc (FAST_FILE_BUFFER_SIZE);
77         ffh->ptr = ffh->buffer;
78
79         ffh->length = fread (ffh->buffer, 1, FAST_FILE_BUFFER_SIZE, ffh->fh);
80
81         if (ffh->length < 0) {
82                 fast_file_close (ffh);
83                 return FALSE;
84         }
85
86         return TRUE;
87 }
88
89 static inline int
90 fast_file_getc (FastFile *ffh)
91 {
92         if (ffh->ptr < ffh->buffer + ffh->length)
93                 return *(ffh->ptr++);
94         else {
95                 ffh->length = fread (ffh->buffer, 1, FAST_FILE_BUFFER_SIZE, ffh->fh);
96
97                 if (!ffh->length)
98                         return FAST_FILE_EOF;
99                 else {
100                         ffh->ptr = ffh->buffer;
101                         return *(ffh->ptr++);
102                 }
103         }
104 }
105
106 typedef struct {
107         char       *mime_type;
108         GHashTable *keys;
109 } GnomeMimeContext;
110
111 /* Describes the directories we scan for information */
112 typedef struct {
113         char *dirname;
114         struct stat s;
115         unsigned int valid : 1;
116         unsigned int system_dir : 1;
117         unsigned int force_reload : 1;
118 } mime_dir_source_t;
119
120
121 #define DELETED_KEY "deleted"
122 #define DELETED_VALUE "moilegrandvizir"
123
124 /* These ones are used to automatically reload mime info on demand */
125 static mime_dir_source_t gnome_mime_dir, user_mime_dir;
126 static time_t last_checked;
127
128 /* To initialize the module automatically */
129 static gboolean gnome_vfs_mime_inited = FALSE;
130
131 /* you will write back or reload the file if and only if this var' value is 0 */
132 static int gnome_vfs_is_frozen = 0;
133
134 static GList *current_lang = NULL;
135 /* we want to replace the previous key if the current key has a higher
136    language level */
137 static int previous_key_lang_level = -1;
138
139 /*
140  * A hash table containing all of the Mime records for specific
141  * mime types (full description, like image/png)
142  * It also contains a the generic types like image/
143  * extracted from .keys files
144  */
145 static GHashTable *specific_types;
146 /* user specific data */
147 static GHashTable *specific_types_user;
148
149
150 /*
151  * A hash table containing all of the Mime records for all registered
152  * mime types
153  * extracted from .mime files
154  */
155 static GHashTable *registered_types;
156 /* user specific data */
157 static GHashTable *registered_types_user;
158
159
160
161 /* Prototypes */
162 static GnomeVFSResult write_back_mime_user_file (void);
163 static GnomeVFSResult write_back_keys_user_file (void);
164 static const char *   gnome_vfs_mime_get_registered_mime_type_key (const char *mime_type,
165                                                                    const char *key);
166
167 void
168 _gnome_vfs_mime_info_mark_gnome_mime_dir_dirty (void)
169 {
170         gnome_mime_dir.force_reload = TRUE;
171 }
172
173 void
174 _gnome_vfs_mime_info_mark_user_mime_dir_dirty (void)
175 {
176         user_mime_dir.force_reload = TRUE;
177 }
178
179 static gboolean
180 does_string_contain_caps (const char *string)
181 {
182         const char *temp_c;
183
184         temp_c = string;
185         while (*temp_c != '\0') {
186                 if (g_ascii_isupper (*temp_c)) {
187                         return TRUE;
188                 }
189                 temp_c++;
190         }
191
192         return FALSE;
193 }
194
195 static GnomeMimeContext *
196 context_new (GHashTable *hash_table, GString *str)
197 {
198         GnomeMimeContext *context;
199         char *mime_type;
200         char last_char;
201
202         mime_type = g_strdup (str->str);
203
204         last_char = mime_type[strlen (mime_type) - 1];
205         if (last_char == '*') {
206                 mime_type[strlen (mime_type) - 1] = '\0';
207         }
208
209         context = g_hash_table_lookup (hash_table, mime_type);
210
211         if (context != NULL) {
212                 g_free (mime_type);
213                 return context;
214         }
215
216 /*      fprintf (stderr, "New context: '%s'\n", mime_type); */
217
218         context = g_new (GnomeMimeContext, 1);
219         context->mime_type = mime_type;
220         context->keys = g_hash_table_new_full (
221                 g_str_hash, g_str_equal,
222                 (GDestroyNotify) g_free,
223                 (GDestroyNotify) g_free);
224
225         g_hash_table_insert (hash_table, context->mime_type, context);
226         return context;
227 }
228
229 static void
230 context_destroy (GnomeMimeContext *context)
231 {
232         g_hash_table_destroy (context->keys);
233         g_free (context->mime_type);
234         g_free (context);
235 }
236
237 /* this gives us a number of the language in the current language list,
238    the higher the number the "better" the translation */
239 static int
240 language_level (const char *langage)
241 {
242         int i;
243         GList *li;
244
245         if (langage == NULL)
246                 return 0;
247
248         for (i = 1, li = current_lang; li != NULL; i++, li = g_list_next (li)) {
249                 if (strcmp ((const char *) li->data, langage) == 0)
250                         return i;
251         }
252
253         return -1;
254 }
255
256
257 static void
258 context_add_key (GnomeMimeContext *context, char *key, char *lang, char *value)
259 {
260         int lang_level;
261
262         lang_level = language_level (lang);
263         /* wrong language completely */
264         if (lang_level < 0)
265                 return;
266
267         /* if a previous key in the hash had a better lang_level don't do anything */
268         if (lang_level > 0 &&
269             previous_key_lang_level > lang_level) {
270                 return;
271         }
272
273         /*      fprintf (stderr, "Add key: '%s' '%s' '%s' %d\n", key, lang, value, lang_level);*/
274
275         g_hash_table_replace (context->keys, g_strdup (key), g_strdup (value));
276
277         previous_key_lang_level = lang_level;
278 }
279
280 typedef enum {
281         STATE_NONE,
282         STATE_LANG,
283         STATE_LOOKING_FOR_KEY,
284         STATE_ON_MIME_TYPE,
285         STATE_ON_KEY,
286         STATE_ON_VALUE
287 } ParserState;
288
289 /* #define APPEND_CHAR(gstr,c) g_string_insert_c ((gstr), -1, (c)) */
290
291 #define APPEND_CHAR(gstr,c) \
292         G_STMT_START {                                     \
293                 if (gstr->len + 1 < gstr->allocated_len) { \
294                         gstr->str [gstr->len++] = c;       \
295                         gstr->str [gstr->len] = '\0';      \
296                 } else {                                   \
297                         g_string_insert_c (gstr, -1, c);   \
298                 }                                          \
299         } G_STMT_END
300
301 typedef enum {
302         FORMAT_MIME,
303         FORMAT_KEYS
304 } Format;
305
306 #define load_mime_type_info_from(a,b) load_type_info_from ((a), (b), FORMAT_MIME)
307 #define load_mime_list_info_from(a,b) load_type_info_from ((a), (b), FORMAT_KEYS)
308
309 static void
310 load_type_info_from (const char *filename,
311                      GHashTable *hash_table,
312                      Format      format)
313 {
314         GString *line;
315         int column, c;
316         ParserState state;
317         FastFile mime_file;
318         gboolean skip_line;
319         GnomeMimeContext *context;
320         int key, lang, last_str_end; /* offsets */
321
322         if (!fast_file_open (&mime_file, filename)) {
323                 return;
324         }
325
326         skip_line = FALSE;
327         column = -1;
328         context = NULL;
329         line = g_string_sized_new (120);
330         key = lang = last_str_end = 0;
331         state = STATE_NONE;
332
333         while ((c = fast_file_getc (&mime_file)) != EOF) {
334         handle_char:
335                 column++;
336                 if (c == '\r')
337                         continue;
338
339                 if (c == '#' && column == 0) {
340                         skip_line = TRUE;
341                         continue;
342                 }
343
344                 if (skip_line) {
345                         if (c == '\n') {
346                                 skip_line = FALSE;
347                                 column = -1;
348                                 g_string_truncate (line, 0);
349                                 key = lang = last_str_end = 0;
350                         }
351                         continue;
352                 }
353
354                 if (c == '\n') {
355                         skip_line = FALSE;
356                         column = -1;
357                         if (state == STATE_ON_MIME_TYPE) {
358                                 /* setup for a new key */
359                                 previous_key_lang_level = -1;
360                                 context = context_new (hash_table, line);
361
362                         } else if (state == STATE_ON_VALUE) {
363                                 APPEND_CHAR (line, '\0');
364                                 context_add_key (context,
365                                                  line->str + key,
366                                                  lang ? line->str + lang : NULL,
367                                                  line->str + last_str_end);
368                                 key = lang = 0;
369                         }
370                         g_string_truncate (line, 0);
371                         last_str_end = 0;
372                         state = STATE_LOOKING_FOR_KEY;
373                         continue;
374                 }
375
376                 switch (state) {
377                 case STATE_NONE:
378                         if (c == ' ' || c == '\t') {
379                                 break;
380                         } else if (c == ':') {
381                                 skip_line = TRUE;
382                                 break;
383                         } else {
384                                 state = STATE_ON_MIME_TYPE;
385                         }
386                         /* fall down */
387
388                 case STATE_ON_MIME_TYPE:
389                         if (c == ':') {
390                                 skip_line = TRUE;
391                                 /* setup for a new key */
392                                 previous_key_lang_level = -1;
393                                 context = context_new (hash_table, line);
394                                 state = STATE_LOOKING_FOR_KEY;
395                                 break;
396                         }
397                         APPEND_CHAR (line, c);
398                         break;
399
400                 case STATE_LOOKING_FOR_KEY:
401                         if (c == '\t' || c == ' ') {
402                                 break;
403                         }
404
405                         if (c == '[') {
406                                 state = STATE_LANG;
407                                 break;
408                         }
409
410                         if (column == 0) {
411                                 state = STATE_ON_MIME_TYPE;
412                                 APPEND_CHAR (line, c);
413                                 break;
414                         }
415                         state = STATE_ON_KEY;
416                         /* falldown */
417
418                 case STATE_ON_KEY:
419                         if (c == '\\') {
420                                 c = fast_file_getc (&mime_file);
421                                 if (c == EOF) {
422                                         break;
423                                 }
424                         }
425                         if (c == '=') {
426                                 key = last_str_end;
427                                 APPEND_CHAR (line, '\0');
428                                 last_str_end = line->len;
429                                 state = STATE_ON_VALUE;
430                                 break;
431                         }
432
433                         if (format == FORMAT_KEYS && c == ':') {
434                                 key = last_str_end;
435
436                                 APPEND_CHAR (line, '\0');
437                                 last_str_end = line->len;
438
439                                 /* Skip space after colon.  There should be one
440                                  * there.  That is how the file is defined. */
441                                 c = fast_file_getc (&mime_file);
442                                 if (c != ' ') {
443                                         if (c == EOF) {
444                                                 break;
445                                         } else {
446                                                 goto handle_char;
447                                         }
448                                 } else {
449                                         column++;
450                                 }
451
452                                 state = STATE_ON_VALUE;
453                                 break;
454                         }
455
456                         APPEND_CHAR (line, c);
457                         break;
458
459                 case STATE_ON_VALUE:
460                         APPEND_CHAR (line, c);
461                         break;
462
463                 case STATE_LANG:
464                         if (c == ']') {
465                                 state = STATE_ON_KEY;
466
467                                 lang = last_str_end;
468                                 APPEND_CHAR (line, '\0');
469                                 last_str_end = line->len;
470
471                                 if (!line->str [0] ||
472                                     language_level (line->str + lang) < 0) {
473                                         skip_line = TRUE;
474                                         key = lang = last_str_end = 0;
475                                         g_string_truncate (line, 0);
476                                         state = STATE_LOOKING_FOR_KEY;
477                                 }
478                         } else {
479                                 APPEND_CHAR (line, c);
480                         }
481                         break;
482                 }
483         }
484
485         /* This happens if the last line of the file doesn't end with '\n' */
486         if (context != NULL) {
487                 if (last_str_end && line->str [0]) {
488                         APPEND_CHAR (line, '\0');
489                         context_add_key (context,
490                                          line->str + key,
491                                          lang ? line->str + lang : NULL,
492                                          line->str + last_str_end);
493                 } else {
494                         if (g_hash_table_size (context->keys) < 1) {
495                                 g_hash_table_remove (hash_table, context->mime_type);
496                                 context_destroy (context);
497                         }
498                 }
499         }
500
501         g_string_free (line, TRUE);
502
503         previous_key_lang_level = -1;
504
505         fast_file_close (&mime_file);
506 }
507
508 static void
509 mime_info_load (mime_dir_source_t *source)
510 {
511         DIR *dir;
512         struct dirent *dent;
513         const int extlen = sizeof (".keys") - 1;
514         char *filename;
515
516         if (stat (source->dirname, &source->s) != -1)
517                 source->valid = TRUE;
518         else
519                 source->valid = FALSE;
520
521         dir = opendir (source->dirname);
522         if (!dir) {
523                 source->valid = FALSE;
524                 return;
525         }
526         if (source->system_dir) {
527                 filename = g_strconcat (source->dirname, "/gnome-vfs.keys", NULL);
528                 load_mime_type_info_from (filename, specific_types);
529                 g_free (filename);
530         }
531
532         while ((dent = readdir (dir)) != NULL) {
533
534                 int len = strlen (dent->d_name);
535
536                 if (len <= extlen)
537                         continue;
538                 if (strcmp (dent->d_name + len - extlen, ".keys"))
539                         continue;
540                 if (source->system_dir && !strcmp (dent->d_name, "gnome-vfs.keys"))
541                         continue;
542
543                 if (source->system_dir && !strcmp (dent->d_name, "gnome.keys")) {
544                         /* Ignore the obsolete "official" one so it doesn't override
545                          * the new official one.
546                          */
547                         continue;
548                 }
549
550                 if (!source->system_dir && !strcmp (dent->d_name, "user.keys"))
551                         continue;
552
553                 filename = g_strconcat (source->dirname, "/", dent->d_name, NULL);
554                 load_mime_type_info_from (filename, specific_types);
555                 g_free (filename);
556         }
557         if (!source->system_dir) {
558                 filename = g_strconcat (source->dirname, "/user.keys", NULL);
559                 load_mime_type_info_from (filename, specific_types_user);
560                 g_free (filename);
561         }
562         closedir (dir);
563 }
564
565 static void
566 mime_list_load (mime_dir_source_t *source)
567 {
568         DIR *dir;
569         struct dirent *dent;
570         const int extlen = sizeof (".mime") - 1;
571         char *filename;
572
573         if (stat (source->dirname, &source->s) != -1)
574                 source->valid = TRUE;
575         else
576                 source->valid = FALSE;
577
578         dir = opendir (source->dirname);
579         if (!dir) {
580                 source->valid = FALSE;
581                 return;
582         }
583         if (source->system_dir) {
584                 filename = g_strconcat (source->dirname, "/gnome-vfs.mime", NULL);
585                 load_mime_list_info_from (filename, registered_types);
586                 g_free (filename);
587         }
588
589         while ((dent = readdir (dir)) != NULL) {
590
591                 int len = strlen (dent->d_name);
592
593                 if (len <= extlen)
594                         continue;
595                 if (strcmp (dent->d_name + len - extlen, ".mime"))
596                         continue;
597                 if (source->system_dir && !strcmp (dent->d_name, "gnome-vfs.mime"))
598                         continue;
599
600                 if (source->system_dir && !strcmp (dent->d_name, "gnome.mime")) {
601                         /* Ignore the obsolete "official" one so it doesn't override
602                          * the new official one.
603                          */
604                         continue;
605                 }
606
607                 if (!source->system_dir && !strcmp (dent->d_name, "user.mime"))
608                         continue;
609
610                 filename = g_strconcat (source->dirname, "/", dent->d_name, NULL);
611                 load_mime_list_info_from (filename, registered_types);
612                 g_free (filename);
613         }
614         if (!source->system_dir) {
615                 filename = g_strconcat (source->dirname, "/user.mime", NULL);
616                 load_mime_list_info_from (filename, registered_types_user);
617                 g_free (filename);
618         }
619         closedir (dir);
620 }
621
622 static void
623 load_mime_type_info (void)
624 {
625         mime_info_load (&gnome_mime_dir);
626         mime_info_load (&user_mime_dir);
627         mime_list_load (&gnome_mime_dir);
628         mime_list_load (&user_mime_dir);
629 }
630
631 static void
632 gnome_vfs_mime_init (void)
633 {
634         /*
635          * The hash tables that store the mime keys.
636          */
637         specific_types = g_hash_table_new (g_str_hash, g_str_equal);
638         registered_types  = g_hash_table_new (g_str_hash, g_str_equal);
639
640         specific_types_user = g_hash_table_new (g_str_hash, g_str_equal);
641         registered_types_user  = g_hash_table_new (g_str_hash, g_str_equal);
642
643         current_lang = gnome_vfs_i18n_get_language_list ("LC_MESSAGES");
644
645         /*
646          * Setup the descriptors for the information loading
647          */
648         gnome_mime_dir.dirname = g_strdup (DATADIR "/mime-info");
649         gnome_mime_dir.system_dir = TRUE;
650
651         user_mime_dir.dirname  = g_strconcat
652                 (g_get_home_dir (), "/.gnome/mime-info", NULL);
653         user_mime_dir.system_dir = FALSE;
654
655         /*
656          * Load
657          */
658         load_mime_type_info ();
659
660         last_checked = time (NULL);
661         gnome_vfs_mime_inited = TRUE;
662 }
663
664 static gboolean
665 remove_keys (gpointer key, gpointer value, gpointer user_data)
666 {
667         GnomeMimeContext *context = value;
668
669         context_destroy (context);
670
671         return TRUE;
672 }
673
674 static void
675 reload_if_needed (void)
676 {
677         time_t now = time (NULL);
678         gboolean need_reload = FALSE;
679         struct stat s;
680
681         if (gnome_vfs_is_frozen > 0)
682                 return;
683
684         if (gnome_mime_dir.force_reload || user_mime_dir.force_reload)
685                 need_reload = TRUE;
686         else if (now > last_checked + 5) {
687                 if (stat (gnome_mime_dir.dirname, &s) != -1 &&
688                     s.st_mtime != gnome_mime_dir.s.st_mtime)
689                         need_reload = TRUE;
690                 else if (stat (user_mime_dir.dirname, &s) != -1 &&
691                          s.st_mtime != user_mime_dir.s.st_mtime)
692                         need_reload = TRUE;
693         }
694
695         last_checked = now;
696
697         if (need_reload) {
698                 gnome_vfs_mime_info_reload ();
699         }
700 }
701
702 static void
703 gnome_vfs_mime_info_clear (void)
704 {
705         if (specific_types != NULL) {
706                 g_hash_table_foreach_remove (specific_types, remove_keys, NULL);
707         }
708         if (registered_types != NULL) {
709                 g_hash_table_foreach_remove (registered_types, remove_keys, NULL);
710         }
711         if (specific_types_user != NULL) {
712                 g_hash_table_foreach_remove (specific_types_user, remove_keys, NULL);
713         }
714         if (registered_types_user != NULL) {
715                 g_hash_table_foreach_remove (registered_types_user, remove_keys, NULL);
716         }
717 }
718
719 /**
720  * _gnome_vfs_mime_info_shutdown:
721  * 
722  * Remove the MIME database from memory.
723  **/
724 void
725 _gnome_vfs_mime_info_shutdown (void)
726 {
727         gnome_vfs_mime_info_clear ();
728
729         if (specific_types != NULL) {
730                 g_hash_table_destroy (specific_types);
731                 specific_types = NULL;
732         }
733         if (registered_types != NULL) {
734                 g_hash_table_destroy (registered_types);
735                 registered_types = NULL;
736         }
737         if (specific_types_user != NULL) {
738                 g_hash_table_destroy (specific_types_user);
739                 specific_types_user = NULL;
740         }
741         if (registered_types_user != NULL) {
742                 g_hash_table_destroy (registered_types_user);
743                 registered_types_user = NULL;
744         }
745 }
746
747 /**
748  * gnome_vfs_mime_info_reload:
749  *
750  * Reload the MIME database from disk and notify any listeners
751  * holding active #GnomeVFSMIMEMonitor objects.
752  **/
753 void
754 gnome_vfs_mime_info_reload (void)
755 {
756         if (!gnome_vfs_mime_inited) {
757                 gnome_vfs_mime_init ();
758         }
759
760         /* 1. Clean */
761         gnome_vfs_mime_info_clear ();
762
763         /* 2. Reload */
764         load_mime_type_info ();
765
766         /* 3. clear our force flags */
767         gnome_mime_dir.force_reload = FALSE;
768         user_mime_dir.force_reload = FALSE;
769
770         /* 3. Tell anyone who cares */
771         _gnome_vfs_mime_monitor_emit_data_changed (gnome_vfs_mime_monitor_get ());
772 }
773
774
775 /**
776  * gnome_vfs_mime_freeze:
777  *
778  * Freezes the mime data so that you can do multiple
779  * updates to the dat in one batch without needing
780  * to back the files to disk or readind them
781  */
782 void
783 gnome_vfs_mime_freeze (void)
784 {
785         gnome_vfs_is_frozen++;
786 }
787
788
789
790 /**
791  * gnome_vfs_mime_thaw:
792  *
793  * UnFreezes the mime data so that you can do multiple
794  * updates to the dat in one batch without needing
795  * to back the files to disk or readind them
796  */
797 void
798 gnome_vfs_mime_thaw (void)
799 {
800         gnome_vfs_is_frozen--;
801
802         if (gnome_vfs_is_frozen == 0) {
803                 write_back_mime_user_file ();
804                 write_back_keys_user_file ();
805         }
806 }
807
808
809 static GnomeVFSResult
810 set_value_real (const char *mime_type, const char *key, const char *value,
811                 GHashTable *user_hash_table)
812 {
813         GnomeMimeContext *context;
814
815         if (mime_type == NULL
816             || key == NULL
817             || value == NULL) {
818                 return gnome_vfs_result_from_errno ();
819         }
820
821         g_return_val_if_fail (!does_string_contain_caps (mime_type),
822                               gnome_vfs_result_from_errno ());
823
824         if (!gnome_vfs_mime_inited) {
825                 gnome_vfs_mime_init ();
826         }
827
828         context = g_hash_table_lookup (user_hash_table, mime_type);
829         if (context == NULL) {
830                 GString *string;
831
832                 string = g_string_new (mime_type);
833
834                 /* create the mime type context */
835                 context = context_new (user_hash_table, string);
836                 /* add the info to the mime type context */
837                 g_hash_table_insert (context->keys, g_strdup (key), g_strdup (value));
838                 g_string_free (string, TRUE);
839         } else
840                 g_hash_table_replace (context->keys, g_strdup (key), g_strdup (value));
841
842         return GNOME_VFS_OK;
843 }
844
845 /**
846  * gnome_vfs_mime_set_value
847  * @mime_type: a mime type.
848  * @key: a key to store the value in.
849  * @value: the value to store in the key.
850  *
851  * This function is going to set the value
852  * associated to the key and it will save it
853  * to the user' file if necessary.
854  * You should not free the key/values passed to
855  * this function. They are used internally.
856  *
857  * Returns: GNOME_VFS_OK if the operation succeeded, otherwise an error code
858  *
859  */
860 GnomeVFSResult
861 gnome_vfs_mime_set_value (const char *mime_type, const char *key, const char *value)
862 {
863         GnomeVFSResult retval;
864
865         retval = set_value_real (mime_type, key, value, specific_types_user);
866
867         if (gnome_vfs_is_frozen == 0) {
868                 return write_back_keys_user_file ();
869         }
870
871         return retval;
872 }
873
874
875 static gboolean
876 is_mime_type_deleted (const char *mime_type)
877 {
878         const char *deleted_key;
879
880         deleted_key = gnome_vfs_mime_get_registered_mime_type_key (mime_type, DELETED_KEY);
881         return deleted_key != NULL && strcmp (deleted_key, DELETED_VALUE) == 0;
882 }
883
884 static const char *
885 get_value_from_hash_table (GHashTable *hash_table, const char *mime_type, const char *key)
886 {
887         GnomeMimeContext *context;
888         char *value;
889
890         value = NULL;
891         context = g_hash_table_lookup (hash_table, mime_type);
892         if (context != NULL) {
893                 value = g_hash_table_lookup (context->keys, key);
894         }
895         return value;
896 }
897
898 static const char *
899 get_value_real (const char *mime_type,
900                 const char *key,
901                 GHashTable *user_hash_table,
902                 GHashTable *system_hash_table)
903 {
904         const char *value;
905         char *generic_type, *p;
906
907         g_return_val_if_fail (key != NULL, NULL);
908         g_assert (user_hash_table != NULL);
909         g_assert (system_hash_table != NULL);
910
911         if (mime_type == NULL) {
912                 return NULL;
913         }
914
915         g_return_val_if_fail (!does_string_contain_caps (mime_type),
916                               NULL);
917
918         reload_if_needed ();
919
920         if (strcmp (key, DELETED_KEY) != 0 && is_mime_type_deleted (mime_type)) {
921                 return NULL;
922         }
923
924         value = get_value_from_hash_table (user_hash_table, mime_type, key);
925         if (value != NULL) {
926                 return value;
927         }
928
929         value = get_value_from_hash_table (system_hash_table, mime_type, key);
930         if (value != NULL) {
931                 return value;
932         }
933
934         generic_type = g_strdup (mime_type);
935         p = strchr (generic_type, '/');
936         if (p != NULL)
937                 *(p+1) = '\0';
938
939         value = get_value_from_hash_table (user_hash_table, generic_type, key);
940         if (value != NULL) {
941                 g_free (generic_type);
942                 return value;
943         }
944
945         value = get_value_from_hash_table (system_hash_table, generic_type, key);
946         g_free (generic_type);
947         if (value != NULL) {
948                 return value;
949         }
950
951         return NULL;
952 }
953
954
955 /**
956  * gnome_vfs_mime_get_value:
957  * @mime_type: a mime type.
958  * @key: A key to lookup for the given mime-type
959  *
960  * This function retrieves the value associated with @key in
961  * the given GnomeMimeContext.  The string is private, you
962  * should not free the result.
963  *
964  * Returns: GNOME_VFS_OK if the operation succeeded, otherwise an error code
965  */
966 const char *
967 gnome_vfs_mime_get_value (const char *mime_type, const char *key)
968 {
969         if (!gnome_vfs_mime_inited)
970                 gnome_vfs_mime_init ();
971
972         return get_value_real (mime_type, key, specific_types_user, specific_types);
973 }
974
975 /**
976  * gnome_vfs_mime_type_is_known:
977  * @mime_type: a mime type.
978  *
979  * This function returns TRUE if @mime_type is in the MIME database at all.
980  *
981  * Returns: TRUE if anything is known about @mime_type, otherwise FALSE
982  */
983 gboolean
984 gnome_vfs_mime_type_is_known (const char *mime_type)
985 {
986         if (mime_type == NULL) {
987                 return FALSE;
988         }
989
990         g_return_val_if_fail (!does_string_contain_caps (mime_type),
991                               FALSE);
992
993         if (!gnome_vfs_mime_inited)
994                 gnome_vfs_mime_init ();
995
996         reload_if_needed ();
997
998         if (is_mime_type_deleted (mime_type)) {
999                 return FALSE;
1000         }
1001         
1002         if (g_hash_table_lookup (specific_types, mime_type)) {
1003                 return TRUE;
1004         }
1005
1006         if (g_hash_table_lookup (specific_types_user, mime_type)) {
1007                 return TRUE;
1008         }
1009
1010         if (g_hash_table_lookup (registered_types, mime_type)) {
1011                 return TRUE;
1012         }
1013
1014         if (g_hash_table_lookup (registered_types_user, mime_type)) {
1015                 return TRUE;
1016         }
1017
1018         return FALSE;
1019 }
1020
1021
1022 /**
1023  * gnome_vfs_mime_keys_list_free:
1024  * @mime_type_list: A mime type list to free.
1025  *
1026  * Frees the mime type list.
1027  */
1028 void
1029 gnome_vfs_mime_keys_list_free (GList *mime_type_list)
1030 {
1031         /* we do not need to free the data in the list since
1032            the data was stolen from the internal hash table
1033            This function is there so that people do not need
1034            to know this particuliar implementation detail.
1035         */
1036
1037         g_list_free (mime_type_list);
1038 }
1039
1040 static void
1041 assemble_list (gpointer key, gpointer value, gpointer user_data)
1042 {
1043         GList **listp = user_data;
1044
1045         (*listp) = g_list_prepend ((*listp), key);
1046 }
1047
1048 /**
1049  * gnome_vfs_mime_get_key_list:
1050  * @mime_type: the MIME type to lookup
1051  *
1052  * Gets a list of all keys associated with @mime_type.
1053  *
1054  * Return value: a GList of const char * representing keys associated
1055  * with @mime_type
1056  **/
1057 GList *
1058 gnome_vfs_mime_get_key_list (const char *mime_type)
1059 {
1060         char *p, *generic_type;
1061         GnomeMimeContext *context;
1062         GList *list = NULL, *l;
1063
1064         if (mime_type == NULL) {
1065                 return NULL;
1066         }
1067         g_return_val_if_fail (!does_string_contain_caps (mime_type),
1068                               NULL);
1069
1070         if (!gnome_vfs_mime_inited)
1071                 gnome_vfs_mime_init ();
1072
1073         reload_if_needed ();
1074
1075         generic_type = g_strdup (mime_type);
1076         p = strchr (generic_type, '/');
1077         if (p != NULL)
1078                 *(p+1) = 0;
1079
1080         context = g_hash_table_lookup (specific_types_user, generic_type);
1081         if (context != NULL) {
1082                 g_hash_table_foreach (
1083                         context->keys, assemble_list, &list);
1084         }
1085
1086         context = g_hash_table_lookup (specific_types, generic_type);
1087         if (context != NULL) {
1088                 g_hash_table_foreach (
1089                         context->keys, assemble_list, &list);
1090         }
1091
1092         g_free (generic_type);
1093         for (l = list; l;) {
1094                 if (l->next) {
1095                         void *this = l->data;
1096                         GList *m;
1097
1098                         for (m = l->next; m; m = m->next) {
1099                                 if (strcmp ((char*) this, (char*) m->data) != 0)
1100                                         continue;
1101                                 list = g_list_remove (list, m->data);
1102                                 break;
1103                         }
1104                 }
1105                 l = l->next;
1106         }
1107
1108         return list;
1109 }
1110
1111 gint
1112 str_cmp_callback  (gconstpointer a,
1113                    gconstpointer b);
1114 gint
1115 str_cmp_callback  (gconstpointer a,
1116                    gconstpointer b)
1117 {
1118         return (strcmp ((char *)a, (char *)b));
1119 }
1120
1121 /**
1122  * gnome_vfs_mime_set_extensions_list:
1123  * @mime_type: the mime type.
1124  * @extensions_list: a whitespace-separated list of the
1125  *                   extensions to set for this mime type.
1126  *
1127  * Sets the extensions for a given mime type. Overrides
1128  * the previously set extensions.
1129  *
1130  * Return value: GNOME_VFS_OK if the operation succeeded, otherwise an error code.
1131  */
1132 GnomeVFSResult
1133 gnome_vfs_mime_set_extensions_list (const char *mime_type,
1134                                     const char *extensions_list)
1135 {
1136         if (is_mime_type_deleted (mime_type)) {
1137                 gnome_vfs_mime_set_registered_type_key (mime_type, DELETED_KEY, "");
1138         }
1139
1140         return gnome_vfs_mime_set_registered_type_key (mime_type, "ext", extensions_list);
1141 }
1142
1143
1144 /**
1145  * gnome_vfs_mime_get_extensions_list:
1146  * @mime_type: type to get the extensions of
1147  *
1148  * Get the file extensions associated with mime type @mime_type.
1149  *
1150  * Return value: a GList of char *s
1151  **/
1152 GList *
1153 gnome_vfs_mime_get_extensions_list (const char *mime_type)
1154 {
1155         GList *list;
1156         const char *extensions_system, *extensions_user;
1157         char *extensions;
1158         gchar **elements;
1159         int index;
1160         GnomeMimeContext *context;
1161
1162         if (mime_type == NULL) {
1163                 return NULL;
1164         }
1165         g_return_val_if_fail (!does_string_contain_caps (mime_type),
1166                               NULL);
1167
1168
1169         if (!gnome_vfs_mime_inited) {
1170                 gnome_vfs_mime_init ();
1171         }
1172
1173         reload_if_needed ();
1174
1175         extensions_system = NULL;
1176         extensions_user = NULL;
1177
1178         context = g_hash_table_lookup (registered_types_user, mime_type);
1179         if (context != NULL) {
1180                 extensions_user = g_hash_table_lookup (context->keys, "ext");
1181         }
1182
1183         context = g_hash_table_lookup (registered_types, mime_type);
1184         if (context != NULL) {
1185                 extensions_system = g_hash_table_lookup (context->keys, "ext");
1186         }
1187
1188
1189         extensions = NULL;
1190         if (extensions_user != NULL) {
1191                 extensions = g_strdup (extensions_user);
1192         } else if (extensions_system != NULL) {
1193                 extensions = g_strdup (extensions_system);
1194         }
1195
1196         /* build a GList from the string */
1197         list = NULL;
1198         if (extensions != NULL) {
1199                 /* Parse the extensions and add to list */
1200                 elements = g_strsplit (extensions, " ", 0);
1201                 if (elements != NULL) {
1202                         index = 0;
1203
1204                         while (elements[index] != NULL) {
1205                                 if (strcmp (elements[index], "") != 0) {
1206                                         list = g_list_append (list, g_strdup (elements[index]));
1207                                 }
1208                                 index++;
1209                         }
1210                         g_strfreev (elements);
1211                 }
1212         }
1213
1214         g_free (extensions);
1215
1216         return list;
1217 }
1218
1219
1220 /**
1221  * gnome_vfs_mime_get_extensions_string:
1222  * @mime_type: the mime type
1223  *
1224  * Retrieves the extensions associated with @mime_type as a single
1225  * space seperated string.
1226  *
1227  * Return value: a string containing space seperated extensions for @mime_type
1228  */
1229 char *
1230 gnome_vfs_mime_get_extensions_string (const char *mime_type)
1231 {
1232         GList *extensions_list, *temp_list;
1233         char *extensions;
1234
1235         if (mime_type == NULL) {
1236                 return NULL;
1237         }
1238         g_return_val_if_fail (!does_string_contain_caps (mime_type),
1239                               NULL);
1240
1241
1242         /* it might seem overkill to use gnome_vfs_mime_get_extensions_list
1243            here but it has the advantage that this function returns
1244            a list of unique extensions */
1245
1246         extensions_list = gnome_vfs_mime_get_extensions_list (mime_type);
1247         if (extensions_list == NULL) {
1248                 return NULL;
1249         }
1250         extensions = NULL;
1251
1252         for (temp_list = extensions_list; temp_list != NULL; temp_list = temp_list->next) {
1253                 char *temp_string;
1254                 temp_string = g_strconcat (temp_list->data, " ", extensions, NULL);
1255                 g_free (extensions);
1256                 extensions = temp_string;
1257         }
1258
1259         extensions[strlen (extensions) - 1] = '\0';
1260         return extensions;
1261 }
1262
1263 /**
1264  * gnome_vfs_mime_get_extensions_pretty_string:
1265  * @mime_type: the mime type
1266  *
1267  * Returns the supported extensions for @mime_type as a comma-seperated list.
1268  *
1269  * Return value: a string containing comma seperated extensions for this mime-type
1270  **/
1271 char *
1272 gnome_vfs_mime_get_extensions_pretty_string (const char *mime_type)
1273 {
1274         GList *extensions, *element;
1275         char *ext_str, *tmp_str;
1276
1277         if (mime_type == NULL) {
1278                 return NULL;
1279         }
1280
1281         ext_str = NULL;
1282         tmp_str = NULL;
1283
1284         if (!gnome_vfs_mime_inited) {
1285                 gnome_vfs_mime_init ();
1286         }
1287
1288         reload_if_needed ();
1289
1290         extensions = gnome_vfs_mime_get_extensions_list (mime_type);
1291         if (extensions == NULL) {
1292                 return NULL;
1293         }
1294
1295         for (element = extensions; element != NULL; element = element->next) {
1296                 if (ext_str != NULL) {
1297                         tmp_str = ext_str;
1298
1299                         if (element->next == NULL) {
1300                                 ext_str = g_strconcat (tmp_str, ".", (char *)element->data, NULL);
1301                         } else {
1302                                 ext_str = g_strconcat (tmp_str, ".", (char *)element->data, ", ", NULL);
1303                         }
1304                         g_free (tmp_str);
1305                 } else {
1306                         if (g_list_length (extensions) == 1) {
1307                                 ext_str = g_strconcat (".", (char *)element->data, NULL);
1308                         } else {
1309                                 ext_str = g_strconcat (".", (char *)element->data, ", ", NULL);
1310                         }
1311                 }
1312         }
1313         gnome_vfs_mime_extensions_list_free (extensions);
1314
1315         return ext_str;
1316 }
1317
1318
1319 /**
1320  * gnome_vfs_mime_extensions_list_free:
1321  * @list: the extensions list
1322  *
1323  * Call this function on the list returned by gnome_vfs_mime_extensions
1324  * to free the list and all of its elements.
1325  **/
1326 void
1327 gnome_vfs_mime_extensions_list_free (GList *list)
1328 {
1329         if (list == NULL) {
1330                 return;
1331         }
1332         g_list_foreach (list, (GFunc) g_free, NULL);
1333         g_list_free (list);
1334 }
1335
1336
1337
1338 static gint
1339 mime_list_sort (gconstpointer a, gconstpointer b)
1340 {
1341   return (strcmp ((const char *) a, (const char *) b));
1342 }
1343
1344 /**
1345  *  get_key_name
1346  *
1347  *  Hash table function that adds the name of the mime type
1348  *  to the supplied GList.
1349  *
1350  */
1351 static void
1352 get_key_name (gpointer key, gpointer value, gpointer user_data)
1353 {
1354         GnomeMimeContext *context;
1355         char *name;
1356         GList **list = user_data;
1357         GList *duplicate;
1358
1359         if (value == NULL || key == NULL) {
1360                 return;
1361         }
1362
1363         context = (GnomeMimeContext *) value;
1364
1365         if (context->mime_type[0] == '#') {
1366                 return;
1367         }
1368
1369         if (is_mime_type_deleted (context->mime_type)) {
1370                 return;
1371         }
1372
1373         /* Get name from key and exit if key is NULL or string is empty */
1374         name = (char *)key;
1375         if (name == NULL || strlen (name) == 0) {
1376                 return;
1377         }
1378
1379         duplicate = NULL;
1380         duplicate = g_list_find_custom ((*list), context->mime_type, (GCompareFunc)strcmp);
1381         if (duplicate == NULL) {
1382                 (*list) = g_list_insert_sorted ((*list), g_strdup(context->mime_type), mime_list_sort);
1383         }
1384
1385 }
1386
1387 /**
1388  * gnome_vfs_mime_reset
1389  *
1390  * resets the user's mime database to the system defaults.
1391  */
1392 void
1393 gnome_vfs_mime_reset (void)
1394 {
1395         char *filename;
1396
1397         filename = g_strconcat (user_mime_dir.dirname, "/user.keys", NULL);
1398         unlink (filename);
1399         g_free (filename);
1400
1401         filename = g_strconcat (user_mime_dir.dirname, "/user.mime", NULL);
1402         unlink (filename);
1403         g_free (filename);
1404 }
1405
1406
1407 /**
1408  * gnome_vfs_mime_registered_mime_type_delete:
1409  * @mime_type: string representing the existing type to delete
1410  *
1411  * Delete a mime type for the user which runs this command.
1412  * You can undo this only by calling gnome_vfs_mime_reset
1413  */
1414
1415 void
1416 gnome_vfs_mime_registered_mime_type_delete (const char *mime_type)
1417 {
1418         gnome_vfs_mime_set_registered_type_key (mime_type,
1419                                                 DELETED_KEY,
1420                                                 DELETED_VALUE);
1421
1422 }
1423
1424 /*
1425  * gnome_vfs_get_registered_mime_types
1426  *
1427  *  Return the list containing the name of all
1428  *  registrered mime types.
1429  *  This function is costly in terms of speed.
1430  */
1431 GList *
1432 gnome_vfs_get_registered_mime_types (void)
1433 {
1434         GList *type_list = NULL;
1435
1436         if (!gnome_vfs_mime_inited) {
1437                 gnome_vfs_mime_init ();
1438         }
1439
1440         reload_if_needed ();
1441
1442         /* Extract mime type names */
1443         g_hash_table_foreach (registered_types_user, get_key_name, &type_list);
1444         g_hash_table_foreach (registered_types, get_key_name, &type_list);
1445
1446         return type_list;
1447 }
1448
1449 /**
1450  * gnome_vfs_mime_registered_mime_type_list_free:
1451  * @list: the extensions list
1452  *
1453  * Call this function on the list returned by gnome_vfs_get_registered_mime_types
1454  * to free the list and all of its elements.
1455  */
1456 void
1457 gnome_vfs_mime_registered_mime_type_list_free (GList *list)
1458 {
1459         if (list == NULL) {
1460                 return;
1461         }
1462
1463         g_list_foreach (list, (GFunc) g_free, NULL);
1464         g_list_free (list);
1465 }
1466
1467 /**
1468  * gnome_vfs_mime_set_registered_type_key:
1469  * @mime_type:  Mime type to set key for
1470  * @key:        The key to set
1471  * @data:       The data to set for the key
1472  *
1473  * This function sets the key data for the registered mime
1474  * type's hash table.
1475  *
1476  * Returns: GNOME_VFS_OK if the operation succeeded, otherwise an error code
1477  */
1478 GnomeVFSResult
1479 gnome_vfs_mime_set_registered_type_key (const char *mime_type, const char *key, const char *value)
1480 {
1481         GnomeVFSResult result;
1482
1483         result = set_value_real (mime_type, key, value, registered_types_user);
1484
1485         if (gnome_vfs_is_frozen == 0) {
1486                 result = write_back_mime_user_file ();
1487         }
1488
1489         return result;
1490 }
1491
1492 /**
1493  * gnome_vfs_mime_get_registered_mime_type_key
1494  * @mime_type: a mime type.
1495  * @key: A key to lookup for the given mime-type
1496  *
1497  * This function retrieves the value associated with @key in
1498  * the given GnomeMimeContext.  The string is private, you
1499  * should not free the result.
1500  */
1501 static const char *
1502 gnome_vfs_mime_get_registered_mime_type_key (const char *mime_type, const char *key)
1503 {
1504         if (!gnome_vfs_mime_inited)
1505                 gnome_vfs_mime_init ();
1506
1507         return get_value_real (mime_type, key,
1508                                registered_types_user,
1509                                registered_types);
1510 }
1511
1512
1513 static gboolean
1514 ensure_user_directory_exist (void)
1515 {
1516         DIR *dir;
1517         gboolean retval;
1518
1519         if (stat (user_mime_dir.dirname, &user_mime_dir.s) != -1)
1520                 user_mime_dir.valid = TRUE;
1521         else
1522                 user_mime_dir.valid = FALSE;
1523
1524         dir = NULL;
1525         dir = opendir (user_mime_dir.dirname);
1526         if (dir == NULL) {
1527                 int result;
1528
1529                 result = mkdir (user_mime_dir.dirname, S_IRWXU );
1530                 if (result != 0) {
1531                         user_mime_dir.valid = FALSE;
1532                         return FALSE;
1533                 }
1534                 dir = opendir (user_mime_dir.dirname);
1535                 if (dir == NULL) {
1536                         user_mime_dir.valid = FALSE;
1537                 }
1538         }
1539
1540         retval = (dir != NULL);
1541         if (retval) 
1542                 closedir (dir);
1543         return retval;
1544 }
1545
1546
1547 static void
1548 write_back_mime_user_file_context_callback (char *key_data,
1549                                             char *value_data,
1550                                             FILE *file) 
1551 {
1552         fprintf (file, "\t%s: %s\n", key_data, value_data);
1553 }
1554 static void
1555 write_back_mime_user_file_callback (char                *mime_type,
1556                                     GnomeMimeContext    *context,
1557                                     FILE                *file)
1558 {
1559         fprintf (file, "%s\n", mime_type);
1560         g_hash_table_foreach (context->keys,
1561                 (GHFunc) write_back_mime_user_file_context_callback,
1562                 file);
1563         fprintf (file, "\n");
1564 }
1565
1566 static GnomeVFSResult
1567 write_back_mime_user_file (void)
1568 {
1569         FILE *file;
1570         char *filename;
1571
1572         if (!ensure_user_directory_exist ()) {
1573                 return gnome_vfs_result_from_errno ();
1574         }
1575
1576         if (!user_mime_dir.system_dir) {
1577                 filename = g_strconcat (user_mime_dir.dirname, "/user.mime", NULL);
1578
1579                 remove (filename);
1580                 file = fopen (filename, "w");
1581                 if (file == NULL)
1582                         return gnome_vfs_result_from_errno ();
1583
1584                 fprintf (file,
1585                          "# This file was autogenerated by gnome-vfs-mime-info.\n"
1586                          "# Do not edit by hand.\n");
1587
1588                 g_hash_table_foreach (registered_types_user,
1589                         (GHFunc) write_back_mime_user_file_callback,
1590                         file);
1591
1592                 /* Cleanup file */
1593                 fclose (file);
1594                 g_free (filename);
1595         }
1596
1597         return GNOME_VFS_OK;
1598 }
1599
1600 static void
1601 write_back_keys_user_file_context_callback (char *key_data,
1602                                             char *value_data,
1603                                             FILE *file)
1604 {
1605         fprintf (file, "\t%s=%s\n", key_data, value_data);
1606 }
1607 static void
1608 write_back_keys_user_file_callback (char             *mime_type,
1609                                     GnomeMimeContext *context,
1610                                     FILE             *file)
1611
1612 {
1613         fprintf (file, "%s\n", mime_type);
1614         g_hash_table_foreach (context->keys,
1615                 (GHFunc) write_back_keys_user_file_context_callback,
1616                 file);
1617         fprintf (file, "\n");
1618 }
1619
1620 static GnomeVFSResult
1621 write_back_keys_user_file (void)
1622 {
1623         FILE *file;
1624         char *filename;
1625
1626         if (!ensure_user_directory_exist ()) {
1627                 return gnome_vfs_result_from_errno ();
1628         }
1629
1630         if (!user_mime_dir.system_dir) {
1631                 filename = g_strconcat (user_mime_dir.dirname, "/user.keys", NULL);
1632
1633                 remove (filename);
1634                 file = fopen (filename, "w");
1635                 if (file == NULL) {
1636                         return gnome_vfs_result_from_errno ();
1637                 }
1638
1639
1640                 fprintf (file, "# this file was autogenerated by gnome-vfs-mime-info.\n"
1641                          "# DO NOT EDIT BY HAND\n");
1642
1643                 g_hash_table_foreach (specific_types_user,
1644                         (GHFunc) write_back_keys_user_file_callback,
1645                         file);
1646
1647                 /* Cleanup file */
1648                 fclose (file);
1649                 g_free (filename);
1650         }
1651
1652         return GNOME_VFS_OK;
1653 }