source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-mime-info.c @ 18126

Revision 18126, 43.7 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18125, which included commits to RCS files with non-trunk default branches.
Line 
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 "libcharset/libcharset.h"
33
34#include "gnome-vfs-mime-monitor.h"
35#include "gnome-vfs-mime-private.h"
36#include "gnome-vfs-mime.h"
37#include "gnome-vfs-private.h"
38#include <ctype.h>
39#include <dirent.h>
40#include <gtk/gtkmain.h>
41#include <stdio.h>
42#include <string.h>
43#include <sys/stat.h>
44#include <sys/time.h>
45#include <sys/types.h>
46#include <time.h>
47#include <unistd.h>
48
49#include <iconv.h>
50#include <errno.h>
51#include <stdlib.h>
52
53
54#if defined(USE_LIBICONV) && !defined (_LIBICONV_H)
55#error libiconv in use but included iconv.h not from libiconv
56#endif
57#if !defined(USE_LIBICONV) && defined (_LIBICONV_H)
58#error libiconv not in use but included iconv.h is from libiconv
59#endif
60
61#ifdef NEED_GNOMESUPPORT_H
62#include "gnomesupport.h"
63#endif
64
65
66#if !defined getc_unlocked && !defined HAVE_GETC_UNLOCKED
67# define getc_unlocked(fp) getc (fp)
68#endif
69
70
71/* The only goal of this function is to make sure no comment line is ever returned
72   to the 2 parsers below.
73   It is evil because I could not figure out what was wrong with those parsers.
74   They should ignore all comments but they do not ignore them. They insert them
75   in the hash table. This function makes sure this never ever happens.
76   -- Mathieu - who takes all responsbility for this complete evilness.
77*/
78static int
79hack_getc (FILE *stream)
80{
81        static int previous_char = '\n';
82        int current_char;
83
84        current_char = getc_unlocked (stream);
85
86        if (current_char == '#' &&
87            previous_char == '\n') {
88                while (getc_unlocked (stream) != '\n') {}
89                return hack_getc (stream);
90        } else {
91                return current_char;
92        }
93}
94
95typedef struct {
96        char       *mime_type;
97        GHashTable *keys;
98} GnomeMimeContext;
99
100/* Describes the directories we scan for information */
101typedef struct {
102        char *dirname;
103        struct stat s;
104        unsigned int valid : 1;
105        unsigned int system_dir : 1;
106} mime_dir_source_t;
107
108
109#define DELETED_KEY "deleted"
110#define DELETED_VALUE "moilegrandvizir"
111
112/* These ones are used to automatically reload mime info on demand */
113static mime_dir_source_t gnome_mime_dir, user_mime_dir;
114static time_t last_checked;
115
116/* To initialize the module automatically */
117static gboolean gnome_vfs_mime_inited = FALSE;
118
119/* you will write back the file if and only if this var' value is 0 */
120static int should_write_file_back = 0;
121
122static GList *current_lang = NULL;
123/* we want to replace the previous key if the current key has a higher
124   language level */
125static char *previous_key = NULL;
126static int previous_key_lang_level = -1;
127
128
129
130
131/*
132 * A hash table containing all of the Mime records for specific
133 * mime types (full description, like image/png)
134 * It also contains a the generic types like image/
135 * extracted from .keys files
136 */
137static GHashTable *specific_types;
138/* user specific data */
139static GHashTable *specific_types_user;
140
141
142/*
143 * A hash table containing all of the Mime records for all registered
144 * mime types
145 * extracted from .mime files
146 */
147static GHashTable *registered_types;
148/* user specific data */
149static GHashTable *registered_types_user;
150
151
152
153/* Prototypes */
154static void           reload_if_needed (void);
155static GnomeVFSResult write_back_mime_user_file (void);
156static GnomeVFSResult write_back_keys_user_file (void);
157static const char *   gnome_vfs_mime_get_registered_mime_type_key (const char *mime_type,
158                                                                   const char *key);
159
160
161static gboolean
162does_string_contain_caps (const char *string)
163{
164        const char *temp_c;
165
166        temp_c = string;
167        while (*temp_c != '\0') {
168                if (isupper ((guchar) *temp_c)) {
169                        return TRUE;
170                }
171                temp_c++;
172        }
173
174        return FALSE;
175}
176
177
178
179static GnomeMimeContext *
180context_new (GHashTable *hash_table, GString *str)
181{
182        GnomeMimeContext *context;
183        char *mime_type;
184        char last_char;
185
186        mime_type = g_strdup (str->str);
187
188        last_char = mime_type[strlen (mime_type) - 1];
189        if (last_char == '*') {
190                mime_type[strlen (mime_type) - 1] = '\0';
191        }
192
193        context = g_hash_table_lookup (hash_table, mime_type);
194
195        if (context != NULL) {
196                g_free (mime_type);
197                return context;
198        }
199       
200        context = g_new (GnomeMimeContext, 1);
201        context->mime_type = mime_type;
202        context->keys = g_hash_table_new (g_str_hash, g_str_equal);
203
204        g_hash_table_insert (hash_table, context->mime_type, context);
205        return context;
206}
207
208static gboolean
209release_key_and_value (gpointer key, gpointer value, gpointer user_data)
210{
211        g_free (key);
212        g_free (value);
213
214        return TRUE;
215}
216
217static void
218context_destroy (GnomeMimeContext *context)
219{
220        /*
221         * Destroy it
222         */
223        g_hash_table_foreach_remove (context->keys, release_key_and_value, NULL);
224        g_hash_table_destroy (context->keys);
225        g_free (context->mime_type);
226        g_free (context);
227}
228
229static void
230context_destroy_and_unlink (GnomeMimeContext *context)
231{
232        /*
233         * Remove the context from our hash tables, we dont know
234         * where it is: so just remove it from both (it can
235         * only be in one).
236         */
237        g_hash_table_remove (specific_types,        context->mime_type);
238        g_hash_table_remove (registered_types,      context->mime_type);
239        g_hash_table_remove (specific_types_user,   context->mime_type);
240        g_hash_table_remove (registered_types_user, context->mime_type);
241
242        context_destroy (context);
243}
244
245/* this gives us a number of the language in the current language list,
246   the higher the number the "better" the translation */
247static int
248language_level (const char *langage)
249{
250        int i;
251        GList *li;
252
253        if (langage == NULL)
254                return 0;
255
256        for (i = 1, li = current_lang; li != NULL; i++, li = g_list_next (li)) {
257                if (strcmp ((const char *) li->data, langage) == 0)
258                        return i;
259        }
260
261        return -1;
262}
263
264/* this function adapted from glib2's g_convert_with_iconv */
265static gchar*
266convert_with_iconv (const gchar *str,
267                    gssize       len,
268                    iconv_t      converter,
269                    gsize       *bytes_read,
270                    gsize       *bytes_written)
271{
272        gchar *dest;
273        gchar *outp;
274        const gchar *p;
275        gsize inbytes_remaining;
276        gsize outbytes_remaining;
277        gsize err;
278        gsize outbuf_size;
279        gboolean have_error = FALSE;
280 
281        g_return_val_if_fail (str != NULL, NULL);
282        g_return_val_if_fail (converter != (iconv_t) -1, NULL);
283     
284        if (len < 0)
285                len = strlen (str);
286
287        p = str;
288        inbytes_remaining = len;
289        outbuf_size = len + 1; /* + 1 for nul in case len == 1 */
290 
291        outbytes_remaining = outbuf_size - 1; /* -1 for nul */
292        outp = dest = g_malloc (outbuf_size);
293
294 again:
295 
296        err = iconv (converter, (char **)&p, &inbytes_remaining, &outp, &outbytes_remaining);
297
298        if (err == (size_t) -1) {
299                switch (errno) {
300                case EINVAL:
301                        /* Incomplete text, do not report an error */
302                        break;
303                case E2BIG: {
304                        size_t used = outp - dest;
305                       
306                        outbuf_size *= 2;
307                        dest = g_realloc (dest, outbuf_size);
308                       
309                        outp = dest + used;
310                        outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
311                       
312                        goto again;
313                }
314                case EILSEQ:
315                        g_warning (_("Invalid byte sequence in conversion input"));
316                        have_error = TRUE;
317                        break;
318                default:
319                        g_warning (_("Error during conversion: %s"),
320                                   strerror (errno));
321                        have_error = TRUE;
322                        break;
323                }
324        }
325       
326        *outp = '\0';
327       
328        if (bytes_read) {
329                *bytes_read = p - str;
330        } else {
331                if ((p - str) != len) {
332                        if (!have_error) {
333                                g_warning (_("Partial character sequence at end of input"));
334                                have_error = TRUE;
335                        }
336                }
337        }
338       
339        if (bytes_written)
340                *bytes_written = outp - dest;   /* Doesn't include '\0' */
341       
342        if (have_error) {
343                g_free (dest);
344                return NULL;
345        } else {
346                return dest;
347        }
348}
349
350static gboolean
351get_charset (const char **a)
352{
353  const char *charset = getenv("CHARSET");
354
355  if (charset && *charset)
356    {
357      *a = charset;
358
359      if (charset && strstr (charset, "UTF-8"))
360        return TRUE;
361      else
362        return FALSE;
363    }
364
365  charset = _gnome_vfs_locale_charset ();
366
367  if (charset && *charset)
368    {
369      *a = charset;
370     
371      if (charset && strstr (charset, "UTF-8"))
372        return TRUE;
373      else
374        return FALSE;
375    }
376
377  /* Assume this for compatibility at present.  */
378  *a = "US-ASCII";
379 
380  return FALSE;
381}
382
383static gchar *
384locale_from_utf8 (const gchar *utf8string)
385{
386        gssize len;
387        gsize bytes_read;
388        gsize bytes_written;
389
390        iconv_t converter;
391        char *converted;
392        const char *charset;
393
394        static gboolean already_warned = FALSE;
395
396        if (utf8string == NULL) {
397                return NULL;
398        }
399       
400        len = strlen (utf8string);
401
402        if (get_charset (&charset)) {
403                return strdup (utf8string);
404        } else {
405                converter = iconv_open (charset, "UTF-8");
406                if (converter == (iconv_t)-1) {
407                        if (already_warned != TRUE) {
408                                already_warned = TRUE;
409                                g_warning ("Unable to convert MIME info from UTF-8 to the current locale %s. MIME info will probably display wrong.", charset);
410                        }
411                        return g_strdup (utf8string);
412                }
413                converted = convert_with_iconv (utf8string,
414                                                len,
415                                                converter,
416                                                &bytes_read,
417                                                &bytes_written);
418                iconv_close (converter);
419
420                if (converted == NULL) {
421                        g_warning ("Unable to convert %s from UTF-8 to %s, this string will probably display wrong.", utf8string, charset);
422                        return g_strdup (utf8string);
423                }
424        }
425
426        return converted;
427}
428
429static void
430context_add_key (GnomeMimeContext *context, char *key, char *lang, char *value)
431{
432        char *v;
433        char *orig_key;
434        int lang_level;
435        char *converted_value;
436
437        lang_level = language_level(lang);
438        /* wrong language completely */
439        if (lang_level < 0)
440                return;
441
442        /* if we have some language defined and
443           if there was a previous_key */
444        if (lang_level > 0 && previous_key) {
445                /* if our new key has a better lang_level then remove the
446                   previous key */
447                if (previous_key_lang_level <= lang_level) {
448                        if (g_hash_table_lookup_extended (context->keys,
449                                                          previous_key,
450                                                          (gpointer *)&orig_key,
451                                                          (gpointer *)&v)) {
452                                g_hash_table_remove (context->keys, orig_key);
453                                g_free(orig_key);
454                                g_free(v);
455                        }
456                /* else, our language level really sucks and the previous
457                   translation was of better language quality so just
458                   ignore us */
459                } else {
460                        return;
461                }
462        }
463
464        if (lang != NULL) {
465                converted_value = locale_from_utf8 (value);
466        } else {
467                converted_value = g_strdup (value);
468        }
469
470        if (g_hash_table_lookup_extended (context->keys, key,
471                                          (gpointer *)&orig_key,
472                                          (gpointer *)&v)) {
473                /* if we found it in the database already, just replace it here */
474                g_free (v);
475                g_hash_table_insert (context->keys, orig_key,
476                                     converted_value);
477        } else {
478                g_hash_table_insert (context->keys, g_strdup(key),
479                                     converted_value);
480        }
481        /* set this as the previous key */
482        g_free(previous_key);
483        previous_key = g_strdup(key);
484        previous_key_lang_level = lang_level;
485}
486
487typedef enum {
488        STATE_NONE,
489        STATE_LANG,
490        STATE_LOOKING_FOR_KEY,
491        STATE_ON_MIME_TYPE,
492        STATE_ON_KEY,
493        STATE_ON_VALUE
494} ParserState;
495
496static void
497load_mime_type_info_from (const char *filename, GHashTable *hash_table)
498{
499        FILE *mime_file;
500        gboolean in_comment, context_used;
501        GString *line;
502        int column, c;
503        ParserState state;
504        GnomeMimeContext *context;
505        char *key;
506        char *lang;
507       
508        mime_file = fopen (filename, "r");
509        if (mime_file == NULL)
510                return;
511
512        in_comment = FALSE;
513        context_used = FALSE;
514        column = -1;
515        context = NULL;
516        key = NULL;
517        lang = NULL;
518        line = g_string_sized_new (120);
519        state = STATE_NONE;
520       
521        while ((c = hack_getc (mime_file)) != EOF){
522                column++;
523                if (c == '\r')
524                        continue;
525
526                if (c == '#' && column == 0){           
527                        in_comment = TRUE;
528                        continue;
529                }
530               
531                if (c == '\n'){
532                        in_comment = FALSE;
533                        column = -1;
534                        if (state == STATE_ON_MIME_TYPE){
535
536                                /* set previous key to nothing
537                                   for this mime type */
538                                g_free(previous_key);
539                                previous_key = NULL;
540                                previous_key_lang_level = -1;
541
542                                context = context_new (hash_table, line);
543                                context_used = FALSE;
544                                g_string_assign (line, "");
545                                state = STATE_LOOKING_FOR_KEY;
546                                continue;
547                        }
548                        if (state == STATE_ON_VALUE){
549                                context_used = TRUE;
550                                context_add_key (context, key, lang, line->str);
551                                g_string_assign (line, "");
552                                g_free (key);
553                                key = NULL;
554                                g_free (lang);
555                                lang = NULL;
556                                state = STATE_LOOKING_FOR_KEY;
557                                continue;
558                        }
559                        continue;
560                }
561
562                if (in_comment)
563                        continue;
564
565                switch (state){
566                case STATE_NONE:
567                        if (c != ' ' && c != '\t')
568                                state = STATE_ON_MIME_TYPE;
569                        else
570                                break;
571                        /* fall down */
572                       
573                case STATE_ON_MIME_TYPE:
574                        if (c == ':'){
575                                in_comment = TRUE;
576                                break;
577                        }
578                        g_string_append_c (line, c);
579                        break;
580
581                case STATE_LOOKING_FOR_KEY:
582                        if (c == '\t' || c == ' ')
583                                break;
584
585                        if (c == '['){
586                                state = STATE_LANG;
587                                break;
588                        }
589
590                        if (column == 0){
591                                state = STATE_ON_MIME_TYPE;
592                                g_string_append_c (line, c);
593                                break;
594                        }
595                        state = STATE_ON_KEY;
596                        /* falldown */
597
598                case STATE_ON_KEY:
599                        if (c == '\\'){
600                                c = hack_getc (mime_file);
601                                if (c == EOF)
602                                        break;
603                        }
604                        if (c == '='){
605                                key = g_strdup (line->str);
606                                g_string_assign (line, "");
607                                state = STATE_ON_VALUE;
608                                break;
609                        }
610                        g_string_append_c (line, c);
611                        break;
612
613                case STATE_ON_VALUE:
614                        g_string_append_c (line, c);
615                        break;
616                       
617                case STATE_LANG:
618                        if (c == ']'){
619                                state = STATE_ON_KEY;     
620                                if (line->str [0]){
621                                        g_free(lang);
622                                        lang = g_strdup(line->str);
623                                } else {
624                                        in_comment = TRUE;
625                                        state = STATE_LOOKING_FOR_KEY;
626                                }
627                                g_string_assign (line, "");
628                                break;
629                        }
630                        g_string_append_c (line, c);
631                        break;
632                }
633        }
634
635        if (context != NULL) {
636                if (key && line->str [0])
637                        context_add_key (context, key, lang, line->str);
638                else
639                        if (!context_used)
640                                context_destroy_and_unlink (context);
641        }
642
643        g_string_free (line, TRUE);
644        g_free (key);
645        g_free (lang);
646
647        /* free the previous_key stuff */
648        g_free(previous_key);
649        previous_key = NULL;
650        previous_key_lang_level = -1;
651
652        fclose (mime_file);
653}
654
655/*
656 *  load_mime_list_info_from
657 *
658 *  Why this special function when a similar one is already in the code?
659 *  Because we need to handle the case where ':' is used to delimit
660 *  the start of a key in a .mime file instead of '=' as is used in
661 *  the .key file.  Why is this done?  Why are there two mime database
662 *  files with differing standards?  We may never know. 
663 *  Until we have a better solution, this will suffice.
664 * 
665 *  Both ':' and '=' are used to delimit the start of a key.
666 */
667 
668static void
669load_mime_list_info_from (const char *filename, GHashTable *hash_table)
670{
671        FILE *mime_file;
672        gboolean in_comment, context_used;
673        GString *line;
674        int column, c;
675        ParserState state;
676        GnomeMimeContext *context;
677        char *key;
678        char *lang;
679       
680        mime_file = fopen (filename, "r");
681        if (mime_file == NULL)
682                return;
683
684        in_comment = FALSE;
685        context_used = FALSE;
686        column = -1;
687        context = NULL;
688        key = NULL;
689        lang = NULL;
690        line = g_string_sized_new (120);
691        state = STATE_NONE;
692       
693        while ((c = hack_getc (mime_file)) != EOF){
694                column++;
695                if (c == '\r')
696                        continue;
697
698                if (c == '#' && column == 0){
699                        in_comment = TRUE;
700                        continue;
701                }
702               
703                if (c == '\n'){
704                        in_comment = FALSE;
705                        column = 0;
706                        if (state == STATE_ON_MIME_TYPE){
707                                /* set previous key to nothing
708                                   for this mime type */
709                                g_free(previous_key);
710                                previous_key = NULL;
711                                previous_key_lang_level = -1;
712
713                                context = context_new (hash_table, line);
714                                context_used = FALSE;
715                                g_string_assign (line, "");
716                                state = STATE_LOOKING_FOR_KEY;
717                                continue;
718                        }
719                        if (state == STATE_ON_VALUE){
720                                context_used = TRUE;
721                                context_add_key (context, key, lang, line->str);
722                                g_string_assign (line, "");
723                                g_free (key);
724                                key = NULL;
725                                g_free (lang);
726                                lang = NULL;
727                                state = STATE_LOOKING_FOR_KEY;
728                                continue;
729                        }
730                        continue;
731                }
732
733                if (in_comment) {
734                        continue;
735                }
736
737                switch (state){
738                case STATE_NONE:
739                        if (c != ' ' && c != '\t')
740                                state = STATE_ON_MIME_TYPE;
741                        else
742                                break;
743                        /* fall down */
744                       
745                case STATE_ON_MIME_TYPE:
746                        if (c == ':'){
747                                in_comment = TRUE;
748                                break;
749                        }
750                        g_string_append_c (line, c);
751                        break;
752
753                case STATE_LOOKING_FOR_KEY:
754                        if (c == '\t' || c == ' ')
755                                break;
756
757                        if (c == '['){
758                                state = STATE_LANG;
759                                break;
760                        }
761
762                        if (column == 1){
763                                state = STATE_ON_MIME_TYPE;
764                                g_string_append_c (line, c);
765                                break;
766                        }
767                        state = STATE_ON_KEY;
768                        /* falldown */
769
770                case STATE_ON_KEY:
771                        if (c == '\\'){
772                                c = hack_getc (mime_file);
773                                if (c == EOF)
774                                        break;
775                        }                       
776                        if (c == '=') {
777                                key = g_strdup (line->str);
778                                g_string_assign (line, "");
779                                state = STATE_ON_VALUE;
780                                break;
781                        }
782
783                        if (c == ':') {
784                                key = g_strdup (line->str);                             
785                                g_string_assign (line, "");
786
787                                /* Skip space after colon.  There should be one
788                                 * there.  That is how the file is defined. */
789                                c = hack_getc (mime_file);
790                                if (c != ' ') {
791                                        /* Revert seek */
792                                        ungetc (c, mime_file);
793                                } else {
794                                        column++;
795                                }
796                               
797                                state = STATE_ON_VALUE;
798                                break;
799                        }
800
801                        g_string_append_c (line, c);
802                        break;
803
804                case STATE_ON_VALUE:
805                        g_string_append_c (line, c);
806                        break;
807                       
808                case STATE_LANG:
809                        if (c == ']') {
810                                state = STATE_ON_KEY;     
811                                if (line->str [0]){
812                                        g_free(lang);
813                                        lang = g_strdup(line->str);
814                                } else {
815                                        in_comment = TRUE;
816                                        state = STATE_LOOKING_FOR_KEY;
817                                }
818                                g_string_assign (line, "");
819                                break;
820                        }
821                        g_string_append_c (line, c);
822                        break;
823                }
824        }
825
826        if (context != NULL) {
827                if (key && line->str [0])
828                        context_add_key (context, key, lang, line->str);
829                else
830                        if (!context_used)
831                                context_destroy_and_unlink (context);
832        }
833
834        g_string_free (line, TRUE);
835        g_free (key);
836        g_free (lang);
837
838        /* free the previous_key stuff */
839        g_free(previous_key);
840        previous_key = NULL;
841        previous_key_lang_level = -1;
842
843        fclose (mime_file);
844}
845
846static void
847mime_info_load (mime_dir_source_t *source)
848{
849        DIR *dir;
850        struct dirent *dent;
851        const int extlen = sizeof (".keys") - 1;
852        char *filename;
853       
854        if (stat (source->dirname, &source->s) != -1)
855                source->valid = TRUE;
856        else
857                source->valid = FALSE;
858       
859        dir = opendir (source->dirname);
860        if (!dir){
861                source->valid = FALSE;
862                return;
863        }
864        if (source->system_dir){
865                filename = g_strconcat (source->dirname, "/gnome-vfs.keys", NULL);
866                load_mime_type_info_from (filename, specific_types);
867                g_free (filename);
868        }
869
870        while ((dent = readdir (dir)) != NULL){
871               
872                int len = strlen (dent->d_name);
873
874                if (len <= extlen)
875                        continue;
876                if (strcmp (dent->d_name + len - extlen, ".keys"))
877                        continue;
878                if (source->system_dir && !strcmp (dent->d_name, "gnome-vfs.keys"))
879                        continue;
880
881                if (source->system_dir && !strcmp (dent->d_name, "gnome.keys")) {
882                        /* Ignore the obsolete "official" one so it doesn't override
883                         * the new official one.
884                         */
885                        continue;
886                }
887                       
888                if (!source->system_dir && !strcmp (dent->d_name, "user.keys"))
889                        continue;
890
891                filename = g_strconcat (source->dirname, "/", dent->d_name, NULL);
892                load_mime_type_info_from (filename, specific_types);
893                g_free (filename);
894        }
895        if (!source->system_dir) {
896                filename = g_strconcat (source->dirname, "/user.keys", NULL);
897                load_mime_type_info_from (filename, specific_types_user);
898                g_free (filename);
899        }
900        closedir (dir);
901}
902
903static void
904mime_list_load (mime_dir_source_t *source)
905{
906        DIR *dir;
907        struct dirent *dent;
908        const int extlen = sizeof (".mime") - 1;
909        char *filename;
910       
911        if (stat (source->dirname, &source->s) != -1)
912                source->valid = TRUE;
913        else
914                source->valid = FALSE;
915       
916        dir = opendir (source->dirname);
917        if (!dir){
918                source->valid = FALSE;
919                return;
920        }
921        if (source->system_dir){
922                filename = g_strconcat (source->dirname, "/gnome-vfs.mime", NULL);
923                load_mime_list_info_from (filename, registered_types);
924                g_free (filename);
925        }
926
927        while ((dent = readdir (dir)) != NULL){
928               
929                int len = strlen (dent->d_name);
930
931                if (len <= extlen)
932                        continue;
933                if (strcmp (dent->d_name + len - extlen, ".mime"))
934                        continue;
935                if (source->system_dir && !strcmp (dent->d_name, "gnome-vfs.mime"))
936                        continue;
937
938                if (source->system_dir && !strcmp (dent->d_name, "gnome.mime")) {
939                        /* Ignore the obsolete "official" one so it doesn't override
940                         * the new official one.
941                         */
942                        continue;
943                }
944                       
945                if (!source->system_dir && !strcmp (dent->d_name, "user.mime"))
946                        continue;
947
948                filename = g_strconcat (source->dirname, "/", dent->d_name, NULL);
949                load_mime_list_info_from (filename, registered_types);
950                g_free (filename);
951        }
952        if (!source->system_dir) {
953                filename = g_strconcat (source->dirname, "/user.mime", NULL);
954                load_mime_list_info_from (filename, registered_types_user);
955                g_free (filename);
956        }
957        closedir (dir);
958}
959
960static void
961load_mime_type_info (void)
962{
963        mime_info_load (&gnome_mime_dir);
964        mime_info_load (&user_mime_dir);
965        mime_list_load (&gnome_mime_dir);
966        mime_list_load (&user_mime_dir);
967}
968
969static void
970gnome_vfs_mime_init (void)
971{
972        /*
973         * The hash tables that store the mime keys.
974         */
975        specific_types = g_hash_table_new (g_str_hash, g_str_equal);
976        registered_types  = g_hash_table_new (g_str_hash, g_str_equal);
977
978        specific_types_user = g_hash_table_new (g_str_hash, g_str_equal);
979        registered_types_user  = g_hash_table_new (g_str_hash, g_str_equal);
980       
981        current_lang = gnome_vfs_i18n_get_language_list ("LC_MESSAGES");
982
983        /*
984         * Setup the descriptors for the information loading
985         */
986
987        gnome_mime_dir.dirname = g_strconcat (GNOME_VFS_DATADIR, "/mime-info", NULL);
988        gnome_mime_dir.system_dir = TRUE;
989       
990        user_mime_dir.dirname  = g_strconcat (g_get_home_dir(), "/.gnome/mime-info", NULL);
991        user_mime_dir.system_dir = FALSE;
992
993        /*
994         * Load
995         */
996        load_mime_type_info ();
997
998        last_checked = time (NULL);
999        gnome_vfs_mime_inited = TRUE;
1000}
1001
1002static gboolean
1003remove_keys (gpointer key, gpointer value, gpointer user_data)
1004{
1005        GnomeMimeContext *context = value;
1006
1007        context_destroy (context);
1008       
1009        return TRUE;
1010}
1011
1012static void
1013reload_if_needed (void)
1014{
1015        time_t now = time (NULL);
1016        gboolean need_reload = FALSE;
1017        struct stat s;
1018       
1019        if (last_checked + 5 >= now)
1020                return;
1021
1022        if (stat (gnome_mime_dir.dirname, &s) != -1)
1023                if (s.st_mtime != gnome_mime_dir.s.st_mtime)
1024                        need_reload = TRUE;
1025
1026        if (stat (user_mime_dir.dirname, &s) != -1)
1027                if (s.st_mtime != user_mime_dir.s.st_mtime)
1028                        need_reload = TRUE;
1029
1030        last_checked = now;
1031       
1032        if (need_reload) {
1033                gnome_vfs_mime_info_reload ();
1034        }
1035}
1036
1037static void
1038gnome_vfs_mime_info_clear (void)
1039{
1040        if (specific_types != NULL) {
1041                g_hash_table_foreach_remove (specific_types, remove_keys, NULL);
1042        }
1043        if (registered_types != NULL) {
1044                g_hash_table_foreach_remove (registered_types, remove_keys, NULL);
1045        }
1046        if (specific_types_user != NULL) {
1047                g_hash_table_foreach_remove (specific_types_user, remove_keys, NULL);
1048        }
1049        if (registered_types_user != NULL) {
1050                g_hash_table_foreach_remove (registered_types_user, remove_keys, NULL);
1051        }
1052}
1053
1054void
1055gnome_vfs_mime_info_shutdown (void)
1056{
1057        gnome_vfs_mime_info_clear ();
1058
1059        if (specific_types != NULL) {
1060                g_hash_table_destroy (specific_types);
1061                specific_types = NULL;
1062        }
1063        if (registered_types != NULL) {
1064                g_hash_table_destroy (registered_types);
1065                registered_types = NULL;
1066        }
1067        if (specific_types_user != NULL) {
1068                g_hash_table_destroy (specific_types_user);
1069                specific_types_user = NULL;
1070        }
1071        if (registered_types_user != NULL) {
1072                g_hash_table_destroy (registered_types_user);
1073                registered_types_user = NULL;
1074        }
1075}
1076
1077void
1078gnome_vfs_mime_info_reload (void)
1079{
1080        if (!gnome_vfs_mime_inited) {
1081                gnome_vfs_mime_init ();
1082        }
1083
1084        /* 1. Clean */
1085        gnome_vfs_mime_info_clear ();
1086       
1087        /* 2. Reload */
1088        load_mime_type_info ();
1089
1090        /* 3. Tell anyone who cares */
1091        /* FIXME bugzilla.eazel.com 5459:
1092         * This is called only when some client asks for data, so changes made
1093         * to the MIME data via (e.g.) the File Types and Programs capplet
1094         * won't be reflected in clients (e.g. Nautilus) until the next time
1095         * some client asks for MIME data. One way to fix this is to implement
1096         * the gconf solution mentioned in bug 5460. Another possibility is to
1097         * use file-node monitoring to notice when the contents of the data
1098         * directory have changed, but file-node monitoring is only a pipe
1099         * dream at the moment.
1100         */
1101        gnome_vfs_mime_monitor_emit_data_changed (gnome_vfs_mime_monitor_get ());
1102}
1103
1104
1105/**
1106 * gnome_vfs_mime_freeze
1107 *
1108 * Freezes the mime data so that you can do multiple
1109 * updates to the dat in one batch without needing
1110 * to back the files to disk or readind them
1111 */
1112void
1113gnome_vfs_mime_freeze (void)
1114{
1115        should_write_file_back++;
1116}
1117
1118
1119
1120/**
1121 * gnome_vfs_mime_thaw
1122 *
1123 * UnFreezes the mime data so that you can do multiple
1124 * updates to the dat in one batch without needing
1125 * to back the files to disk or readind them
1126 */
1127void
1128gnome_vfs_mime_thaw (void)
1129{
1130        should_write_file_back--;
1131
1132        if (should_write_file_back == 0) {
1133                write_back_mime_user_file ();
1134                write_back_keys_user_file ();           
1135        }
1136}
1137
1138
1139static GnomeVFSResult
1140set_value_real (const char *mime_type, const char *key, const char *value,
1141                GHashTable *user_hash_table)
1142{
1143        GnomeMimeContext *context;
1144
1145        if (mime_type == NULL
1146            || key == NULL
1147            || value == NULL) {
1148                return gnome_vfs_result_from_errno ();
1149        }
1150
1151        g_return_val_if_fail (!does_string_contain_caps (mime_type),
1152                              gnome_vfs_result_from_errno ());
1153       
1154        if (!gnome_vfs_mime_inited) {
1155                gnome_vfs_mime_init ();
1156        }
1157
1158        context = g_hash_table_lookup (user_hash_table, mime_type);
1159        if (context != NULL) {
1160                gpointer orig_key, orig_value;
1161                if (g_hash_table_lookup_extended (context->keys, key, &orig_key, &orig_value)) {
1162                        g_hash_table_insert (context->keys, orig_key, g_strdup (value));
1163                        g_free (orig_value);
1164                } else {
1165                        g_hash_table_insert (context->keys, g_strdup (key), g_strdup (value));
1166                }
1167        } else {
1168                GString *string;
1169
1170                string = g_string_new (mime_type);
1171
1172                /* create the mime type context */
1173                context = context_new (user_hash_table, string);
1174                /* add the info to the mime type context */
1175                g_hash_table_insert (context->keys, g_strdup (key), g_strdup (value));
1176        }
1177       
1178        return GNOME_VFS_OK;
1179}
1180
1181/**
1182 * gnome_vfs_mime_set_value:
1183 * @mime_type: a mime type.
1184 * @key: a key to store the value in.
1185 * @value: the value to store in the key.
1186 *
1187 * This function is going to set the value
1188 * associated to the key and it will save it
1189 * to the user' file if necessary.
1190 * You should not free the key/values passed to
1191 * this function. They are used internally.
1192 */
1193GnomeVFSResult
1194gnome_vfs_mime_set_value (const char *mime_type, const char *key, const char *value)
1195{
1196        GnomeVFSResult retval;
1197       
1198        retval = set_value_real (mime_type, key, value, specific_types_user);
1199       
1200        if (should_write_file_back == 0) {
1201                return write_back_keys_user_file ();
1202        }
1203
1204        return retval;
1205}
1206
1207
1208static gboolean
1209is_mime_type_deleted (const char *mime_type)
1210{
1211        const char *deleted_key;
1212
1213        deleted_key = gnome_vfs_mime_get_registered_mime_type_key (mime_type, DELETED_KEY);
1214        return deleted_key != NULL && strcmp (deleted_key, DELETED_VALUE) == 0;
1215}
1216
1217static const char *
1218get_value_from_hash_table (GHashTable *hash_table, const char *mime_type, const char *key)
1219{
1220        GnomeMimeContext *context;
1221        char *value;
1222
1223        value = NULL;
1224        context = g_hash_table_lookup (hash_table, mime_type);
1225        if (context != NULL) {
1226                value = g_hash_table_lookup (context->keys, key);
1227        }
1228        return value;
1229}
1230
1231static const char *
1232get_value_real (const char *mime_type,
1233                const char *key,
1234                GHashTable *user_hash_table,
1235                GHashTable *system_hash_table)
1236{
1237        const char *value;
1238        char *generic_type, *p;
1239       
1240        g_return_val_if_fail (key != NULL, NULL);
1241        g_assert (user_hash_table != NULL);
1242        g_assert (system_hash_table != NULL);
1243
1244        if (mime_type == NULL) {
1245                return NULL;
1246        }
1247       
1248        g_return_val_if_fail (!does_string_contain_caps (mime_type),
1249                              NULL);
1250
1251        reload_if_needed ();
1252
1253        if (strcmp (key, DELETED_KEY) != 0 && is_mime_type_deleted (mime_type)) {
1254                return NULL;
1255        }
1256
1257        value = get_value_from_hash_table (user_hash_table, mime_type, key);
1258        if (value != NULL) {
1259                return value;
1260        }
1261       
1262        value = get_value_from_hash_table (system_hash_table, mime_type, key);
1263        if (value != NULL) {
1264                return value;
1265        }
1266       
1267        generic_type = g_strdup (mime_type);
1268        p = strchr (generic_type, '/');
1269        if (p != NULL)
1270                *(p+1) = '\0';
1271       
1272        value = get_value_from_hash_table (user_hash_table, generic_type, key);
1273        if (value != NULL) {
1274                g_free (generic_type);
1275                return value;
1276        }
1277       
1278        value = get_value_from_hash_table (system_hash_table, generic_type, key);
1279        g_free (generic_type);
1280        if (value != NULL) {
1281                return value;
1282        }
1283
1284        return NULL;
1285}
1286
1287
1288/**
1289 * gnome_vfs_mime_get_value:
1290 * @mime_type: a mime type.
1291 * @key: A key to lookup for the given mime-type
1292 *
1293 * This function retrieves the value associated with @key in
1294 * the given GnomeMimeContext.  The string is private, you
1295 * should not free the result.
1296 */
1297const char *
1298gnome_vfs_mime_get_value (const char *mime_type, const char *key)
1299{
1300        if (!gnome_vfs_mime_inited)
1301                gnome_vfs_mime_init ();
1302
1303        return get_value_real (mime_type, key, specific_types_user, specific_types);
1304}
1305
1306/**
1307 * gnome_vfs_mime_type_is_known:
1308 * @mime_type: a mime type.
1309 *
1310 * This function returns TRUE if @mime_type is in the MIME database at all.
1311 */
1312gboolean
1313gnome_vfs_mime_type_is_known (const char *mime_type)
1314{       
1315        if (mime_type == NULL) {
1316                return FALSE;
1317        }
1318
1319        g_return_val_if_fail (!does_string_contain_caps (mime_type),
1320                              FALSE);
1321
1322        if (!gnome_vfs_mime_inited)
1323                gnome_vfs_mime_init ();
1324
1325        reload_if_needed ();
1326
1327        if (g_hash_table_lookup (specific_types, mime_type)) {
1328                return TRUE;
1329        }
1330       
1331        if (g_hash_table_lookup (specific_types_user, mime_type)) {
1332                return TRUE;
1333        }
1334       
1335        if (g_hash_table_lookup (registered_types, mime_type)) {
1336                return TRUE;
1337        }
1338       
1339        if (g_hash_table_lookup (registered_types_user, mime_type)) {
1340                return TRUE;
1341        }
1342
1343        return FALSE;
1344}
1345
1346
1347/**
1348 * gnome_vfs_mime_keys_list_free:
1349 * @mime_type_list: A mime type list to free.
1350 *
1351 * Frees the mime type list.
1352 */
1353void
1354gnome_vfs_mime_keys_list_free (GList *mime_type_list)
1355{
1356        /* we do not need to free the data in the list since
1357           the data was stolen from the internal hash table
1358           This function is there so that people do not need
1359           to know this particuliar implementation detail.
1360        */
1361
1362        g_list_free (mime_type_list);
1363}
1364
1365static void
1366assemble_list (gpointer key, gpointer value, gpointer user_data)
1367{
1368        GList **listp = user_data;
1369
1370        (*listp) = g_list_prepend ((*listp), key);
1371}
1372
1373/**
1374 * gnome_vfs_mime_get_key_list:
1375 * @mime_type: the mime type to lookup.
1376 *
1377 * Returns a GList that contains private strings with all of the keys
1378 * associated with the @mime_type. 
1379 */
1380GList *
1381gnome_vfs_mime_get_key_list (const char *mime_type)
1382{
1383        char *p, *generic_type;
1384        GnomeMimeContext *context;
1385        GList *list = NULL, *l;
1386       
1387        if (mime_type == NULL) {
1388                return NULL;
1389        }
1390        g_return_val_if_fail (!does_string_contain_caps (mime_type),
1391                              NULL);
1392
1393        if (!gnome_vfs_mime_inited)
1394                gnome_vfs_mime_init ();
1395
1396        reload_if_needed ();
1397       
1398        generic_type = g_strdup (mime_type);
1399        p = strchr (generic_type, '/');
1400        if (p != NULL)
1401                *(p+1) = 0;
1402       
1403        context = g_hash_table_lookup (specific_types_user, generic_type);
1404        if (context != NULL) {
1405                g_hash_table_foreach (
1406                        context->keys, assemble_list, &list);
1407        }
1408       
1409        context = g_hash_table_lookup (specific_types, generic_type);
1410        if (context != NULL) {
1411                g_hash_table_foreach (
1412                        context->keys, assemble_list, &list);
1413        }
1414
1415        g_free (generic_type);
1416        for (l = list; l;){
1417                if (l->next){
1418                        void *this = l->data;
1419                        GList *m;
1420
1421                        for (m = l->next; m; m = m->next){
1422                                if (strcmp ((char*) this, (char*) m->data) != 0)
1423                                        continue;
1424                                list = g_list_remove (list, m->data);
1425                                break;
1426                        }
1427                }
1428                l = l->next;
1429        }
1430
1431        return list;
1432}
1433
1434gint
1435str_cmp_callback  (gconstpointer a,
1436                   gconstpointer b);
1437gint
1438str_cmp_callback  (gconstpointer a,
1439                   gconstpointer b)
1440{
1441        return (strcmp ((char *)a, (char *)b));
1442}
1443
1444/**
1445 * gnome_vfs_mime_set_extensions_list:
1446 * @mime_type: the mime type.
1447 * @extensions_list: a whitespace-separated list of the
1448 *                   extensions to set for this mime type.
1449 *
1450 * Sets the extensions for a given mime type. Overrides
1451 * the previously set extensions.
1452 *
1453 */
1454GnomeVFSResult
1455gnome_vfs_mime_set_extensions_list (const char *mime_type,
1456                                    const char *extensions_list)
1457{
1458        return gnome_vfs_mime_set_registered_type_key (mime_type, "ext", extensions_list);
1459}
1460
1461
1462/**
1463 * gnome_vfs_mime_get_extensions_list:
1464 * @mime_type: the mime type
1465 *
1466 * Returns a list of extensions for this mime-type
1467 */
1468GList *
1469gnome_vfs_mime_get_extensions_list (const char *mime_type)
1470{
1471        GList *list;
1472        const char *extensions_system, *extensions_user;
1473        char *extensions;
1474        gchar **elements;
1475        int index;
1476        GnomeMimeContext *context;
1477       
1478        if (mime_type == NULL) {
1479                return NULL;
1480        }
1481        g_return_val_if_fail (!does_string_contain_caps (mime_type),
1482                              NULL);
1483
1484
1485        if (!gnome_vfs_mime_inited) {
1486                gnome_vfs_mime_init ();
1487        }
1488
1489        reload_if_needed ();
1490
1491        extensions_system = NULL;
1492        extensions_user = NULL;
1493
1494        context = g_hash_table_lookup (registered_types_user, mime_type);
1495        if (context != NULL) {
1496                extensions_user = g_hash_table_lookup (context->keys, "ext");
1497        }
1498
1499        context = g_hash_table_lookup (registered_types, mime_type);
1500        if (context != NULL) {
1501                extensions_system = g_hash_table_lookup (context->keys, "ext");
1502        }
1503
1504
1505        extensions = NULL;
1506        if (extensions_user != NULL) {
1507                extensions = g_strdup (extensions_user);
1508        } else if (extensions_system != NULL) {
1509                extensions = g_strdup (extensions_system);
1510        }
1511
1512        /* build a GList from the string */
1513        list = NULL;
1514        if (extensions != NULL) {
1515                /* Parse the extensions and add to list */
1516                elements = g_strsplit (extensions, " ", 0);     
1517                if (elements != NULL) {
1518                        index = 0;
1519                       
1520                        while (elements[index] != NULL) {
1521                                if (strcmp (elements[index], "") != 0) {
1522                                        list = g_list_append (list, g_strdup (elements[index]));
1523                                }
1524                                index++;
1525                        }                       
1526                        g_strfreev (elements);
1527                }               
1528        }
1529
1530        g_free (extensions);
1531
1532        return list;
1533}
1534
1535
1536/**
1537 * gnome_vfs_mime_extensions_string:
1538 * @mime_type: the mime type
1539 *
1540 * Returns a string containing extensions for this mime-type
1541 */
1542char *
1543gnome_vfs_mime_get_extensions_string (const char *mime_type)
1544{
1545        GList *extensions_list, *temp_list;
1546        char *extensions;
1547       
1548        if (mime_type == NULL) {
1549                return NULL;
1550        }
1551        g_return_val_if_fail (!does_string_contain_caps (mime_type),
1552                              NULL);
1553
1554
1555        /* it might seem overkill to use gnome_vfs_mime_get_extensions_list
1556           here but it has the advantage that this function returns
1557           a list of unique extensions */
1558
1559        extensions_list = gnome_vfs_mime_get_extensions_list (mime_type);
1560        if (extensions_list == NULL) {
1561                return NULL;
1562        }
1563        extensions = NULL;
1564
1565        for (temp_list = extensions_list; temp_list != NULL; temp_list = temp_list->next) {
1566                char *temp_string;
1567                temp_string = g_strconcat (temp_list->data, " ", extensions, NULL);
1568                g_free (extensions);
1569                extensions = temp_string;
1570        }
1571
1572        extensions[strlen (extensions) - 1] = '\0';
1573        return extensions;
1574}
1575       
1576/**
1577 * gnome_vfs_mime_get_extensions_pretty_string:
1578 * @mime_type: the mime type
1579 *
1580 * Returns a string containing comma seperated extensions for this mime-type
1581 */
1582char *
1583gnome_vfs_mime_get_extensions_pretty_string (const char *mime_type)
1584{
1585        GList *extensions, *element;
1586        char *ext_str, *tmp_str;
1587       
1588        if (mime_type == NULL) {
1589                return NULL;
1590        }
1591
1592        ext_str = NULL;
1593        tmp_str = NULL;
1594
1595        if (!gnome_vfs_mime_inited) {
1596                gnome_vfs_mime_init ();
1597        }
1598
1599        reload_if_needed ();
1600       
1601        extensions = gnome_vfs_mime_get_extensions_list (mime_type);
1602        if (extensions == NULL) {
1603                return NULL;
1604        }
1605
1606        for (element = extensions; element != NULL; element = element->next) {
1607                if (ext_str != NULL) {
1608                        tmp_str = ext_str;
1609                       
1610                        if (element->next == NULL) {
1611                                ext_str = g_strconcat (tmp_str, ".", (char *)element->data, NULL);
1612                        } else {
1613                                ext_str = g_strconcat (tmp_str, ".", (char *)element->data, ", ", NULL);
1614                        }
1615                        g_free (tmp_str);
1616                } else {
1617                        if (g_list_length (extensions) == 1) {
1618                                ext_str = g_strconcat (".", (char *)element->data, NULL);
1619                        } else {
1620                                ext_str = g_strconcat (".", (char *)element->data, ", ", NULL);
1621                        }
1622                }
1623        }
1624        gnome_vfs_mime_extensions_list_free (extensions);
1625
1626        return ext_str;
1627}
1628
1629
1630/**
1631 * gnome_vfs_mime_extension_list_free:
1632 * @list: the extensions list
1633 *
1634 * Call this function on the list returned by gnome_vfs_mime_extensions
1635 * to free the list and all of its elements.
1636 */
1637void
1638gnome_vfs_mime_extensions_list_free (GList *list)
1639{
1640        if (list == NULL) {
1641                return;
1642        }
1643        g_list_foreach (list, (GFunc) g_free, NULL);
1644        g_list_free (list);
1645}
1646
1647
1648
1649static gint
1650mime_list_sort (gconstpointer a, gconstpointer b)
1651{
1652  return (strcmp ((const char *) a, (const char *) b));
1653}
1654
1655/**
1656 *  get_key_name
1657 *
1658 *  Hash table function that adds the name of the mime type
1659 *  to the supplied GList.
1660 * 
1661 */
1662static void
1663get_key_name (gpointer key, gpointer value, gpointer user_data)
1664{
1665        GnomeMimeContext *context;
1666        char *name;
1667        GList **list = user_data;
1668        GList *duplicate;
1669       
1670        if (value == NULL || key == NULL) {
1671                return;
1672        }
1673       
1674        context = (GnomeMimeContext *) value;
1675
1676        if (context->mime_type[0] == '#') {
1677                return;
1678        }
1679
1680        if (is_mime_type_deleted (context->mime_type)) {
1681                return;
1682        }
1683
1684        /* Get name from key and exit if key is NULL or string is empty */     
1685        name = (char *)key;
1686        if (name == NULL || strlen (name) == 0) {
1687                return;
1688        }
1689
1690        duplicate = NULL;
1691        duplicate = g_list_find_custom ((*list), context->mime_type, (GCompareFunc)strcmp);
1692        if (duplicate == NULL) {
1693                (*list) = g_list_insert_sorted ((*list), g_strdup(context->mime_type), mime_list_sort);         
1694        }
1695
1696}
1697
1698/**
1699 * gnome_vfs_mime_reset
1700 *
1701 * resets the user's mime database to the system defaults.
1702 */
1703void
1704gnome_vfs_mime_reset (void)
1705{
1706        char *filename;
1707
1708        filename = g_strconcat (user_mime_dir.dirname, "/user.keys", NULL);
1709        unlink (filename);
1710        g_free (filename);
1711       
1712        filename = g_strconcat (user_mime_dir.dirname, "/user.mime", NULL);
1713        unlink (filename);
1714        g_free (filename);
1715}
1716
1717
1718/**
1719 * gnome_vfs_mime_registered_mime_type_delete
1720 *
1721 * Delete a mime type for the user which runs this command.
1722 * You can undo this only by calling gnome_vfs_mime_reset
1723 */
1724
1725void
1726gnome_vfs_mime_registered_mime_type_delete (const char *mime_type)
1727{
1728        gnome_vfs_mime_set_registered_type_key (mime_type,
1729                                                DELETED_KEY,
1730                                                DELETED_VALUE);
1731
1732}
1733
1734/*
1735 * gnome_vfs_get_registered_mime_types
1736 *
1737 *  Return the list containing the name of all
1738 *  registrered mime types.
1739 *  This function is costly in terms of speed.
1740 */
1741GList *
1742gnome_vfs_get_registered_mime_types (void)
1743{
1744        GList *type_list = NULL;
1745       
1746        if (!gnome_vfs_mime_inited) {
1747                gnome_vfs_mime_init ();
1748        }
1749
1750        reload_if_needed ();
1751
1752        /* Extract mime type names */
1753        g_hash_table_foreach (registered_types_user, get_key_name, &type_list);
1754        g_hash_table_foreach (registered_types, get_key_name, &type_list);
1755
1756        return type_list;
1757}
1758
1759/**
1760 * gnome_vfs_mime_registered_mime_type_list_free:
1761 * @list: the extensions list
1762 *
1763 * Call this function on the list returned by gnome_vfs_get_registered_mime_types
1764 * to free the list and all of its elements.
1765 */
1766void
1767gnome_vfs_mime_registered_mime_type_list_free (GList *list)
1768{
1769        if (list == NULL) {
1770                return;
1771        }
1772
1773        g_list_foreach (list, (GFunc) g_free, NULL);
1774        g_list_free (list);
1775}
1776
1777/**
1778 * gnome_vfs_mime_set_registered_type_key:
1779 * @mime_type:  Mime type to set key for
1780 * @key:        The key to set
1781 * @data:       The data to set for the key
1782 *
1783 * This function sets the key data for the registered mime
1784 * type's hash table.
1785 */
1786GnomeVFSResult
1787gnome_vfs_mime_set_registered_type_key (const char *mime_type, const char *key, const char *value)
1788{
1789        GnomeVFSResult result;
1790
1791        result = set_value_real (mime_type, key, value, registered_types_user);
1792
1793        if (should_write_file_back == 0) {
1794                result = write_back_mime_user_file ();
1795        }
1796
1797        return result;
1798}
1799
1800/**
1801 * gnome_vfs_mime_get_registered_mime_type_key
1802 * @mime_type: a mime type.
1803 * @key: A key to lookup for the given mime-type
1804 *
1805 * This function retrieves the value associated with @key in
1806 * the given GnomeMimeContext.  The string is private, you
1807 * should not free the result.
1808 */
1809static const char *
1810gnome_vfs_mime_get_registered_mime_type_key (const char *mime_type, const char *key)
1811{
1812        if (!gnome_vfs_mime_inited)
1813                gnome_vfs_mime_init ();
1814
1815        return get_value_real (mime_type, key,
1816                               registered_types_user,
1817                               registered_types);
1818}
1819
1820
1821static DIR *
1822ensure_user_directory_exist (void)
1823{
1824        DIR *dir;
1825       
1826        if (stat (user_mime_dir.dirname, &user_mime_dir.s) != -1)
1827                user_mime_dir.valid = TRUE;
1828        else
1829                user_mime_dir.valid = FALSE;
1830
1831        dir = NULL;
1832        dir = opendir (user_mime_dir.dirname);
1833        if (dir == NULL){
1834                int result;
1835
1836                result = mkdir (user_mime_dir.dirname, S_IRWXU );
1837                if (result != 0) {
1838                        user_mime_dir.valid = FALSE;
1839                        return NULL;
1840                }
1841                dir = opendir (user_mime_dir.dirname);
1842                if (dir == NULL){
1843                        user_mime_dir.valid = FALSE;
1844                }
1845        }
1846
1847        return dir;
1848}
1849
1850
1851void  write_back_mime_user_file_context_callback (gpointer key,
1852                                                  gpointer value,
1853                                                  gpointer user_data);
1854void  write_back_mime_user_file_context_callback (gpointer key,
1855                                                  gpointer value,
1856                                                  gpointer user_data)
1857{
1858        char *key_data, *value_data;
1859        FILE *file;
1860
1861        key_data = (char *) key;
1862        value_data = (char *) value;
1863        file = (FILE *) user_data;
1864
1865        fprintf (file, "\t%s: %s\n", key_data, value_data);
1866}
1867
1868void  write_back_mime_user_file_callback (gpointer key,
1869                                          gpointer value,
1870                                          gpointer user_data);
1871void  write_back_mime_user_file_callback (gpointer key,
1872                                          gpointer value,
1873                                          gpointer user_data)
1874{
1875        GnomeMimeContext *context;
1876        char *mime_type;
1877        FILE *file;
1878
1879        context = (GnomeMimeContext *) value;
1880        mime_type = (char *)key;
1881        file = (FILE *) user_data;
1882
1883        /* print the mime type */
1884        fprintf (file, "%s\n", mime_type);
1885
1886        /* print the mime type keys */
1887        g_hash_table_foreach (context->keys,
1888                              write_back_mime_user_file_context_callback,
1889                              file);
1890       
1891        /* add a blank kine between mime types */
1892        fprintf (file, "\n");
1893
1894}
1895
1896static GnomeVFSResult
1897write_back_mime_user_file (void)
1898{
1899        DIR *dir;
1900        FILE *file;
1901        char *filename;
1902       
1903        dir = ensure_user_directory_exist ();
1904        if (dir == NULL) {
1905                return gnome_vfs_result_from_errno ();
1906        }
1907
1908       
1909        if (!user_mime_dir.system_dir){
1910                filename = g_strconcat (user_mime_dir.dirname, "/user.mime", NULL);
1911
1912                remove (filename);
1913                file = fopen (filename, "w");
1914                if (file == NULL) {
1915                        return gnome_vfs_result_from_errno ();
1916                }
1917
1918                fprintf (file,
1919                         "# This file was autogenerated by gnome-vfs-mime-info.\n"
1920                         "# Do not edit by hand.\n");
1921
1922                g_hash_table_foreach (registered_types_user,
1923                                      write_back_mime_user_file_callback,
1924                                      file);
1925               
1926                /* Cleanup file */
1927                fclose (file);
1928                g_free (filename);
1929        }
1930
1931        return GNOME_VFS_OK;
1932}
1933
1934void  write_back_keys_user_file_context_callback (gpointer key,
1935                                                  gpointer value,
1936                                                  gpointer user_data);
1937void  write_back_keys_user_file_context_callback (gpointer key,
1938                                                  gpointer value,
1939                                                  gpointer user_data)
1940{
1941        char *key_data, *value_data;
1942        FILE *file;
1943
1944        key_data = (char *) key;
1945        value_data = (char *) value;
1946        file = (FILE *) user_data;
1947
1948        fprintf (file, "\t%s=%s\n", key_data, value_data);
1949
1950}
1951
1952void  write_back_keys_user_file_callback (gpointer key,
1953                                          gpointer value,
1954                                          gpointer user_data);
1955void  write_back_keys_user_file_callback (gpointer key,
1956                                          gpointer value,
1957                                          gpointer user_data)
1958{
1959        GnomeMimeContext *context;
1960        char *mime_type;
1961        FILE *file;
1962
1963        context = (GnomeMimeContext *) value;
1964        mime_type = (char *)key;
1965        file = (FILE *) user_data;
1966
1967        /* print the mime type */
1968        fprintf (file, "%s\n", mime_type);
1969
1970        /* print the mime type keys */
1971        g_hash_table_foreach (context->keys,
1972                              write_back_keys_user_file_context_callback,
1973                              file);
1974       
1975        /* add a blank kine between mime types */
1976        fprintf (file, "\n");
1977
1978}
1979
1980static GnomeVFSResult
1981write_back_keys_user_file (void)
1982{
1983        DIR *dir;
1984        FILE *file;
1985        char *filename;
1986       
1987        dir = ensure_user_directory_exist ();
1988        if (dir == NULL) {
1989                return gnome_vfs_result_from_errno ();
1990        }
1991
1992        if (!user_mime_dir.system_dir){
1993                filename = g_strconcat (user_mime_dir.dirname, "/user.keys", NULL);
1994
1995                remove (filename);
1996                file = fopen (filename, "w");
1997                if (file == NULL) {
1998                        return gnome_vfs_result_from_errno ();
1999                }
2000
2001
2002                fprintf (file, "# this file was autogenerated by gnome-vfs-mime-info.\n"
2003                         "# DO NOT EDIT BY HAND\n");
2004
2005                g_hash_table_foreach (specific_types_user,
2006                                      write_back_keys_user_file_callback,
2007                                      file);
2008                               
2009                /* Cleanup file */
2010                fclose (file);
2011                g_free (filename);
2012        }
2013
2014        return GNOME_VFS_OK;   
2015}
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
Note: See TracBrowser for help on using the repository browser.