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

Revision 17128, 21.7 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17127, 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/*
4 * Copyright (C) 1998 Miguel de Icaza
5 * Copyright (C) 1997 Paolo Molaro
6 * Copyright (C) 2000, 2001 Eazel, Inc.
7 * All rights reserved.
8 *
9 * This file is part of the Gnome Library.
10 *
11 * The Gnome Library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public License as
13 * published by the Free Software Foundation; either version 2 of the
14 * License, or (at your option) any later version.
15 *
16 * The Gnome Library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
23 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
25 */
26
27#include <config.h>
28#include "gnome-vfs-mime.h"
29
30#include "gnome-vfs-mime-private.h"
31#include "gnome-vfs-mime-sniff-buffer-private.h"
32#include "gnome-vfs-module-shared.h"
33#include "gnome-vfs-ops.h"
34#include "gnome-vfs-result.h"
35#include "gnome-vfs-uri.h"
36#include <ctype.h>
37#include <dirent.h>
38#include <regex.h>
39#include <stdio.h>
40#include <string.h>
41#include <time.h>
42
43static gboolean module_inited = FALSE;
44
45static GHashTable *mime_extensions [2] = { NULL, NULL };
46static GList      *mime_regexs     [2] = { NULL, NULL };
47
48#define DEFAULT_DATE_TRACKER_INTERVAL   5       /* in milliseconds */
49
50typedef struct {
51        char *mime_type;
52        regex_t regex;
53} RegexMimePair;
54
55typedef struct {
56        char *dirname;
57        unsigned int valid : 1;
58        unsigned int system_dir : 1;
59} mime_dir_source_t;
60
61typedef struct {
62        char *file_path;
63        time_t mtime;
64} FileDateRecord;
65
66struct FileDateTracker {
67        time_t last_checked;
68        guint check_interval;
69        GHashTable *records;
70};
71
72/* These ones are used to automatically reload mime-types on demand */
73static mime_dir_source_t gnome_mime_dir, user_mime_dir;
74static FileDateTracker *mime_data_date_tracker;
75
76#ifdef G_THREADS_ENABLED
77
78/* We lock this mutex whenever we modify global state in this module.  */
79G_LOCK_DEFINE_STATIC (mime_mutex);
80
81#endif /* G_LOCK_DEFINE_STATIC */
82
83
84static char *
85get_priority (char *def, int *priority)
86{
87        *priority = 0;
88
89        if (*def == ',') {
90                def++;
91                if (*def == '1') {
92                        *priority = 0;
93                        def++;
94                } else if (*def == '2') {
95                        *priority = 1;
96                        def++;
97                }
98        }
99
100        while (*def && *def == ':')
101                def++;
102
103        return def;
104}
105
106static int
107list_find_type (gconstpointer value, gconstpointer type)
108{
109        return g_strcasecmp((const char *) value, (const char *) type);
110}
111
112static void
113add_to_key (char *mime_type, char *def)
114{
115        int priority = 1;
116        char *s, *p, *ext;
117        GList *list = NULL;
118
119        if (strncmp (def, "ext", 3) == 0){
120                char *tokp;
121
122                def += 3;
123                def = get_priority (def, &priority);
124                s = p = g_strdup (def);
125
126                while ((ext = strtok_r (s, " \t\n\r,", &tokp)) != NULL) {
127                        list = (GList *) g_hash_table_lookup (mime_extensions [priority], ext);
128                        if (!g_list_find_custom (list, mime_type, list_find_type)) {
129                                list = g_list_prepend (list, g_strdup (mime_type));
130                                g_hash_table_insert (mime_extensions [priority], g_strdup (ext), list);
131                        }
132                        s = NULL;
133                }
134                g_free (p);
135        }
136
137        if (strncmp (def, "regex", 5) == 0) {
138                RegexMimePair *mp;
139                def += 5;
140                def = get_priority (def, &priority);
141
142                while (*def != '\0' && isspace ((unsigned char)*def)) {
143                        def++;
144                }
145
146                if (*def == '\0') {
147                        return;
148                }
149
150                /* This was g_new instead of g_new0, but there seems
151                 * to be a bug in the Solaris? version of regcomp that
152                 * requires an initialized regex or it will crash.
153                 */
154                mp = g_new0 (RegexMimePair, 1);
155                if (regcomp (&mp->regex, def, REG_EXTENDED | REG_NOSUB)) {
156                        g_free (mp);
157                        return;
158                }
159                mp->mime_type = g_strdup (mime_type);
160
161                mime_regexs [priority] = g_list_prepend (mime_regexs [priority], mp);
162        }
163}
164
165static void
166mime_fill_from_file (const char *filename)
167{
168        FILE *file;
169        char buf [1024];
170        char *current_key;
171
172        g_assert (filename != NULL);
173
174        file = fopen (filename, "r");
175
176        if (file == NULL) {
177                return;
178        }
179       
180        current_key = NULL;
181        while (fgets (buf, sizeof (buf), file) != NULL) {
182                char *p;
183
184                if (buf [0] == '#') {
185                        continue;
186                }
187
188                /* Trim trailing spaces */
189                for (p = buf + strlen (buf) - 1; p >= buf; p--) {
190                        if (!isspace ((unsigned char)*p) && *p != '\n') {
191                                break;
192                        }
193                        *p = 0;
194                }
195
196                if (buf [0] == '\0') {
197                        continue;
198                }
199
200                if (buf [0] == '\t' || buf [0] == ' '){
201                        if (current_key){
202                                char *p = buf;
203
204                                while (*p && isspace ((unsigned char)*p))
205                                        p++;
206
207                                if (*p == 0)
208                                        continue;
209
210                                add_to_key (current_key, p);
211                        }
212                } else {
213                        g_free (current_key);
214
215                        current_key = g_strdup (buf);
216                        if (current_key [strlen (current_key)-1] == ':')
217                                current_key [strlen (current_key)-1] = 0;
218                }
219        }
220
221        g_free (current_key);
222
223        fclose (file);
224
225        gnome_vfs_file_date_tracker_start_tracking_file (mime_data_date_tracker, filename);
226}
227
228static void
229mime_load (mime_dir_source_t *source)
230{
231        DIR *dir;
232        struct dirent *dent;
233        const int extlen = sizeof (".mime") - 1;
234        char *filename;
235        struct stat s;
236
237        g_return_if_fail (source != NULL);
238        g_return_if_fail (source->dirname != NULL);
239
240        source->valid = (stat (source->dirname, &s) != -1);
241
242        dir = opendir (source->dirname);
243        if (dir == NULL) {
244                source->valid = FALSE;
245                return;
246        }
247
248        if (source->system_dir) {
249                filename = g_strconcat (source->dirname, "/gnome-vfs.mime", NULL);
250                mime_fill_from_file (filename);
251                g_free (filename);
252        }
253
254        while (TRUE) {
255                int len;
256               
257                dent = readdir (dir);
258                if (dent == NULL) {
259                        break;
260                }
261               
262                len = strlen (dent->d_name);
263
264                if (len <= extlen) {
265                        continue;
266                }
267               
268                if (strcmp (dent->d_name + len - extlen, ".mime") != 0) {
269                        continue;
270                }
271
272                if (source->system_dir && strcmp (dent->d_name, "gnome-vfs.mime") == 0) {
273                        continue;
274                }
275
276                if (source->system_dir && strcmp (dent->d_name, "gnome.mime") == 0) {
277                        /* Ignore the obsolete "official" one so it doesn't override
278                         * the new official one.
279                         */
280                        continue;
281                }
282
283                if (!source->system_dir && strcmp (dent->d_name, "user.mime") == 0) {
284                        continue;
285                }
286
287                filename = g_strconcat (source->dirname, "/", dent->d_name, NULL);
288
289                mime_fill_from_file (filename);
290                g_free (filename);
291        }
292        closedir (dir);
293
294        if (!source->system_dir) {
295                filename = g_strconcat (source->dirname, "/user.mime", NULL);
296                mime_fill_from_file (filename);
297                g_free (filename);
298        }
299
300        gnome_vfs_file_date_tracker_start_tracking_file (mime_data_date_tracker, source->dirname);
301}
302
303static gboolean
304remove_one_mime_hash_entry (gpointer key, gpointer value, gpointer user_data)
305{
306        g_free (key);
307        g_list_foreach (value, (GFunc) g_free, NULL);
308        g_list_free (value);
309
310        return TRUE;
311}
312
313static void
314mime_extensions_empty (void)
315{
316        GList *p;
317        int i;
318        for (i = 0; i < 2; i++) {
319                if (mime_extensions [i] != NULL) {
320                        g_hash_table_foreach_remove (mime_extensions [i],
321                                                     remove_one_mime_hash_entry, NULL);
322                }
323
324                for (p = mime_regexs [i]; p != NULL; p = p->next){
325                        RegexMimePair *mp = p->data;
326
327                        g_free (mp->mime_type);
328                        regfree (&mp->regex);
329                        g_free (mp);
330                }
331                g_list_free (mime_regexs [i]);
332                mime_regexs [i] = NULL;
333        }
334}
335
336static void
337maybe_reload (void)
338{
339        if (!gnome_vfs_file_date_tracker_date_has_changed (mime_data_date_tracker)) {
340                return;
341        }
342
343        mime_extensions_empty ();
344
345        mime_load (&gnome_mime_dir);
346        mime_load (&user_mime_dir);
347}
348
349static void
350mime_init (void)
351{
352        mime_extensions [0] = g_hash_table_new (g_str_hash, g_str_equal);
353        mime_extensions [1] = g_hash_table_new (g_str_hash, g_str_equal);
354
355        mime_data_date_tracker = gnome_vfs_file_date_tracker_new ();
356       
357        gnome_mime_dir.dirname = g_strconcat (GNOME_VFS_DATADIR, "/mime-info", NULL);
358        gnome_mime_dir.system_dir = TRUE;
359
360        user_mime_dir.dirname = g_strconcat (g_get_home_dir (), "/.gnome/mime-info", NULL);
361        user_mime_dir.system_dir = FALSE;
362
363        mime_load (&gnome_mime_dir);
364        mime_load (&user_mime_dir);
365
366        module_inited = TRUE;
367}
368
369void
370gnome_vfs_mime_shutdown (void)
371{
372        if (!module_inited)
373                return;
374
375        gnome_vfs_mime_info_shutdown ();
376        gnome_vfs_mime_clear_magic_table ();
377
378        mime_extensions_empty ();
379       
380        g_hash_table_destroy (mime_extensions[0]);
381        g_hash_table_destroy (mime_extensions[1]);
382
383        gnome_vfs_file_date_tracker_free (mime_data_date_tracker);
384       
385        g_free (gnome_mime_dir.dirname);
386        g_free (user_mime_dir.dirname);
387}
388
389/**
390 * gnome_vfs_mime_type_from_name_or_default:
391 * @filename: A filename (the file does not necesarily exist).
392 * @defaultv: A default value to be returned if no match is found
393 *
394 * This routine tries to determine the mime-type of the filename
395 * only by looking at the filename from the GNOME database of mime-types.
396 *
397 * Returns the mime-type of the @filename.  If no value could be
398 * determined, it will return @defaultv.
399 */
400const char *
401gnome_vfs_mime_type_from_name_or_default (const char *filename, const char *defaultv)
402{
403        const gchar *ext;
404        char *upext;
405        int priority;
406        const char *result = defaultv;
407
408        if (filename == NULL) {
409                return result;
410        }
411
412        G_LOCK (mime_mutex);
413
414        ext = strrchr (filename, '.');
415        if (ext != NULL) {
416                ++ext;
417        }
418       
419        if (!module_inited) {
420                mime_init ();
421        }
422
423        maybe_reload ();
424
425        for (priority = 1; priority >= 0; priority--){
426                GList *l;
427                GList *list = NULL ;
428               
429                if (ext != NULL) {
430                       
431                        list = g_hash_table_lookup (mime_extensions [priority], ext);
432                        if (list != NULL) {
433                                list = g_list_first( list );
434                                result = (const char *) list->data;
435                                break;
436                        }
437
438                        /* Search for UPPER case extension */
439                        upext = g_strdup (ext);
440                        g_strup (upext);
441                        list = g_hash_table_lookup (mime_extensions [priority], upext);
442                        if (list != NULL) {
443                                g_free (upext);
444                                list = g_list_first (list);
445                                result = (const char *) list->data;
446                                break;
447                        }
448
449                        /* Final check for lower case */
450                        g_strdown (upext);
451                        list = g_hash_table_lookup (mime_extensions [priority], upext);
452                        g_free (upext);
453                        if (list != NULL) {
454                                list = g_list_first (list);
455                                result = (const char *) list->data;
456                                break;
457                        }
458                }
459
460                for (l = mime_regexs [priority]; l; l = l->next){
461                        RegexMimePair *mp = l->data;
462
463                        if (regexec (&mp->regex, filename, 0, 0, 0) == 0) {
464                                result = mp->mime_type;
465                                G_UNLOCK (mime_mutex);
466                                return result;
467                        }
468                }
469        }
470
471        G_UNLOCK (mime_mutex);
472        return result;
473}
474
475/**
476 * gnome_vfs_mime_type_from_name:
477 * @filename: A filename (the file does not necessarily exist).
478 *
479 * Determined the mime type for @filename.
480 *
481 * Returns the mime-type for this filename.
482 */
483const char *
484gnome_vfs_mime_type_from_name (const gchar * filename)
485{
486        return gnome_vfs_mime_type_from_name_or_default (filename, GNOME_VFS_MIME_TYPE_UNKNOWN);
487}
488
489static const char *
490gnome_vfs_get_mime_type_from_uri_internal (GnomeVFSURI *uri)
491{
492        const char *base_name;
493
494        /* Return a mime type based on the file extension or NULL if no match. */
495        base_name = gnome_vfs_uri_get_basename (uri);
496        if (base_name == NULL) {
497                return NULL;
498        }
499
500        return gnome_vfs_mime_type_from_name_or_default (base_name, NULL);
501}
502
503const char *
504gnome_vfs_get_mime_type_internal (GnomeVFSMimeSniffBuffer *buffer, const char *file_name)
505{
506        const char *result;
507
508        result = NULL;
509       
510        if (buffer != NULL) {
511                result = gnome_vfs_mime_get_type_from_magic_table (buffer);
512               
513                if (result != NULL) {
514                        return result;
515                }
516               
517                /* Special handling of gzip files -- use the file name to make a more
518                 * accurate guess of the file type for formats such as gnumeric.gz and
519                 * pdf.gz. Without this, these would always get identified as gzip even though
520                 * their name would suggest otherwise.
521                 * FIXME bugzilla.eazel.com 6867:
522                 * Generalize this so that we can have different magic patters
523                 * other than gzip do this.
524                 */
525                if (gnome_vfs_sniff_buffer_looks_like_gzip (buffer, file_name)) {
526                        return "application/x-gzip";
527                }
528               
529                if (result == NULL) {
530                        if (gnome_vfs_sniff_buffer_looks_like_text (buffer)) {
531                                /* Text file -- treat extensions as a more accurate source
532                                 * of type information.
533                                 */
534                               
535                                if (file_name != NULL) {
536                                        result = gnome_vfs_mime_type_from_name_or_default (file_name, NULL);
537                                }
538       
539                                if (result != NULL) {
540                                        return result;
541                                }
542
543                                /* Didn't find an extension match, assume plain text. */
544                                return "text/plain";
545
546                        } else if (gnome_vfs_sniff_buffer_looks_like_mp3 (buffer)) {
547                                return "audio/x-mp3";
548                        }
549                }
550        }
551       
552        if (result == NULL && file_name != NULL) {
553                /* No type recognized -- fall back on extensions. */
554                result = gnome_vfs_mime_type_from_name_or_default (file_name, NULL);
555        }
556       
557        if (result == NULL) {
558                result = GNOME_VFS_MIME_TYPE_UNKNOWN;
559        }
560       
561        return result;
562}
563
564/**
565 * gnome_vfs_get_mime_type:
566 * @uri: a real file or a non-existent uri.
567 * @data_size: Size of the data.
568 *
569 * Tries to guess the mime type of the file represented by @uir.
570 * Favors using the file data to the @uri extension.
571 * Handles passing @uri of a non-existent file by falling back
572 * on returning a type based on the extension.
573 *
574 * Returns the mime-type for this uri.
575 * FIXME: This function will not necessarily return the same mime type as doing a
576 * get file info on the text uri.
577 *
578 */
579const char *
580gnome_vfs_get_mime_type (GnomeVFSURI *uri)
581{
582        const char *result;
583        GnomeVFSMimeSniffBuffer *buffer;
584        GnomeVFSHandle *handle;
585        GnomeVFSResult error;
586
587        /* Check for special stat-defined file types first. */
588        result = gnome_vfs_get_special_mime_type (uri);
589        if (result != NULL) {
590                return result;
591        }
592
593        error = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_READ);
594
595        if (error != GNOME_VFS_OK) {
596                /* file may not exist, return type based on name only */
597                return gnome_vfs_get_mime_type_from_uri_internal (uri);
598        }
599       
600        buffer = gnome_vfs_mime_sniff_buffer_new_from_handle (handle);
601
602        result = gnome_vfs_get_mime_type_internal (buffer, gnome_vfs_uri_get_basename (uri));
603
604        gnome_vfs_mime_sniff_buffer_free (buffer);
605        gnome_vfs_close (handle);
606
607        return result;
608}
609
610static GnomeVFSResult
611file_seek_binder (gpointer context, GnomeVFSSeekPosition whence,
612                  GnomeVFSFileOffset offset)
613{
614        FILE *file = (FILE *)context;
615        int result;
616        result = fseek (file, offset, whence);
617        if (result < 0) {
618                return gnome_vfs_result_from_errno ();
619        }
620        return GNOME_VFS_OK;
621}
622
623static GnomeVFSResult
624file_read_binder (gpointer context, gpointer buffer,
625                  GnomeVFSFileSize bytes, GnomeVFSFileSize *bytes_read)
626{
627        FILE *file = (FILE *)context;   
628        *bytes_read = fread (buffer, 1, bytes, file);
629        if (*bytes_read < 0) {
630                *bytes_read = 0;
631                return gnome_vfs_result_from_errno ();
632        }
633
634        return GNOME_VFS_OK;
635}
636
637/**
638 * gnome_vfs_get_file_mime_type:
639 * @path: a path of a file.
640 * @stat_info: optional stat buffer.
641 * @suffix_only: whether or not to do a magic-based lookup.
642 *
643 * Tries to guess the mime type of the file represented by @path.
644 * If @suffix_only is false, uses the mime-magic based lookup first.
645 * Handles passing @path of a non-existent file by falling back
646 * on returning a type based on the extension.
647 *
648 * Returns the mime-type for this path.
649 */
650const char *
651gnome_vfs_get_file_mime_type (const char *path, const struct stat *stat_info,
652        gboolean suffix_only)
653{
654        const char *result;
655        GnomeVFSMimeSniffBuffer *buffer;
656        struct stat tmp_stat_buffer;
657        FILE *file;
658
659        file = NULL;
660        result = NULL;
661
662        /* get the stat info if needed */
663        if (stat_info == NULL && stat (path, &tmp_stat_buffer) == 0) {
664                stat_info = &tmp_stat_buffer;
665        }
666
667        /* single out special file types */
668        if (stat_info && !S_ISREG(stat_info->st_mode)) {
669                if (S_ISDIR(stat_info->st_mode)) {
670                        return "x-directory/normal";
671                } else if (S_ISCHR(stat_info->st_mode)) {
672                        return "x-special/device-char";
673                } else if (S_ISBLK(stat_info->st_mode)) {
674                        return "x-special/device-block";
675                } else if (S_ISFIFO(stat_info->st_mode)) {
676                        return "x-special/fifo";
677                } else if (S_ISSOCK(stat_info->st_mode)) {
678                        return "x-special/socket";
679                } else {
680                        /* unknown entry type, return generic file type */
681                        return GNOME_VFS_MIME_TYPE_UNKNOWN;
682                }
683        }
684
685        if (!suffix_only) {
686                file = fopen(path, "r");
687        }
688
689        if (file != NULL) {
690                buffer = gnome_vfs_mime_sniff_buffer_new_generic
691                        (file_seek_binder, file_read_binder, file);
692
693                result = gnome_vfs_get_mime_type_internal (buffer, path);
694                gnome_vfs_mime_sniff_buffer_free (buffer);
695                fclose (file);
696        } else {
697                result = gnome_vfs_get_mime_type_internal (NULL, path);
698        }
699
700       
701        g_assert (result != NULL);
702        return result;
703}
704
705/**
706 * gnome_vfs_get_mime_type_from_uri:
707 * @uri: A file uri.
708 *
709 * Tries to guess the mime type of the file @uri by
710 * checking the file name extension. Works on non-existent
711 * files.
712 *
713 * Returns the mime-type for this filename.
714 */
715const char *
716gnome_vfs_get_mime_type_from_uri (GnomeVFSURI *uri)
717{
718        const char *result;
719
720        result = gnome_vfs_get_mime_type_from_uri_internal (uri);
721        if (result == NULL) {
722                /* no type, return generic file type */
723                result = GNOME_VFS_MIME_TYPE_UNKNOWN;
724        }
725
726        return result;
727}
728
729/**
730 * gnome_vfs_get_mime_type_from_file_data:
731 * @uri: A file uri.
732 *
733 * Tries to guess the mime type of the file @uri by
734 * checking the file data using the magic patterns. Does not handle text files properly
735 *
736 * Returns the mime-type for this filename.
737 */
738const char *
739gnome_vfs_get_mime_type_from_file_data (GnomeVFSURI *uri)
740{
741        const char *result;
742        GnomeVFSMimeSniffBuffer *buffer;
743        GnomeVFSHandle *handle;
744        GnomeVFSResult error;
745
746        error = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_READ);
747
748        if (error != GNOME_VFS_OK) {
749                return GNOME_VFS_MIME_TYPE_UNKNOWN;
750        }
751       
752        buffer = gnome_vfs_mime_sniff_buffer_new_from_handle (handle);
753        result = gnome_vfs_get_mime_type_internal (buffer, NULL);       
754        gnome_vfs_mime_sniff_buffer_free (buffer);
755        gnome_vfs_close (handle);
756
757        return result;
758}
759
760/**
761 * gnome_vfs_get_mime_type_for_data:
762 * @data: A pointer to data in memory.
763 * @data_size: Size of the data.
764 *
765 * Tries to guess the mime type of the data in @data
766 * using the magic patterns.
767 *
768 * Returns the mime-type for this filename.
769 */
770const char *
771gnome_vfs_get_mime_type_for_data (gconstpointer data, int data_size)
772{
773        const char *result;
774        GnomeVFSMimeSniffBuffer *buffer;
775
776        buffer = gnome_vfs_mime_sniff_buffer_new_from_existing_data
777                (data, data_size);
778
779        result = gnome_vfs_get_mime_type_internal (buffer, NULL);       
780
781        gnome_vfs_mime_sniff_buffer_free (buffer);
782
783        return result;
784}
785
786gboolean
787gnome_vfs_mime_type_is_supertype (const char *mime_type)
788{
789        int length;
790
791        if (mime_type == NULL) {
792                return FALSE;
793        }
794
795        length = strlen (mime_type);
796
797        return length > 2
798               && mime_type[length - 2] == '/'
799               && mime_type[length - 1] == '*';
800}
801
802static char *
803extract_prefix_add_suffix (const char *string, const char *separator, const char *suffix)
804{
805        const char *separator_position;
806        int prefix_length;
807        char *result;
808
809        separator_position = strstr (string, separator);
810        prefix_length = separator_position == NULL
811                ? strlen (string)
812                : separator_position - string;
813
814        result = g_malloc (prefix_length + strlen (suffix) + 1);
815       
816        strncpy (result, string, prefix_length);
817        result[prefix_length] = '\0';
818
819        strcat (result, suffix);
820
821        return result;
822}
823
824/* Returns the supertype for a mime type. Note that if called
825 * on a supertype it will return a copy of the supertype.
826 */
827char *
828gnome_vfs_get_supertype_from_mime_type (const char *mime_type)
829{
830        if (mime_type == NULL) {
831                return NULL;
832        }
833        return extract_prefix_add_suffix (mime_type, "/", "/*");
834}
835
836static void
837file_date_record_update_mtime (FileDateRecord *record)
838{
839        struct stat s;
840
841        if (stat (record->file_path, &s) != -1) {
842                record->mtime = s.st_mtime;
843        }       
844}
845
846static FileDateRecord *
847file_date_record_new (const char *file_path) {
848        FileDateRecord *record;
849
850        record = g_new0 (FileDateRecord, 1);
851        record->file_path = g_strdup (file_path);
852
853        file_date_record_update_mtime (record);
854
855        return record;
856}
857
858static void
859file_date_record_free (FileDateRecord *record)
860{
861        g_free (record->file_path);
862        g_free (record);
863}
864
865FileDateTracker *
866gnome_vfs_file_date_tracker_new (void)
867{
868        FileDateTracker *tracker;
869
870        tracker = g_new0 (FileDateTracker, 1);
871        tracker->check_interval = DEFAULT_DATE_TRACKER_INTERVAL;
872        tracker->records = g_hash_table_new (g_str_hash, g_str_equal);
873
874        return tracker;
875}
876
877static gboolean
878release_key_and_value (gpointer key, gpointer value, gpointer user_data)
879{
880        g_free (key);
881        file_date_record_free (value);
882
883        return TRUE;
884}
885
886void
887gnome_vfs_file_date_tracker_free (FileDateTracker *tracker)
888{
889        g_hash_table_foreach_remove (tracker->records, release_key_and_value, NULL);
890        g_hash_table_destroy (tracker->records);
891        g_free (tracker);
892}
893
894/*
895 * Record the current mod date for a specified file, so that we can check
896 * later whether it has changed.
897 */
898void
899gnome_vfs_file_date_tracker_start_tracking_file (FileDateTracker *tracker,
900                                                 const char *local_file_path)
901{
902        FileDateRecord *record;
903
904        record = g_hash_table_lookup (tracker->records, local_file_path);
905        if (record != NULL) {
906                file_date_record_update_mtime (record);
907        } else {
908                g_hash_table_insert (tracker->records,
909                                     g_strdup (local_file_path),
910                                     file_date_record_new (local_file_path));
911        }
912}
913
914static void
915check_and_update_one (gpointer key, gpointer value, gpointer user_data)
916{
917        FileDateRecord *record;
918        gboolean *return_has_changed;
919        struct stat s;
920
921        g_assert (key != NULL);
922        g_assert (value != NULL);
923        g_assert (user_data != NULL);
924
925        record = (FileDateRecord *)value;
926        return_has_changed = (gboolean *)user_data;
927
928        if (stat (record->file_path, &s) != -1) {
929                if (s.st_mtime != record->mtime) {
930                        record->mtime = s.st_mtime;
931                        *return_has_changed = TRUE;
932                }
933        }
934}
935
936gboolean
937gnome_vfs_file_date_tracker_date_has_changed (FileDateTracker *tracker)
938{
939        time_t now;
940        gboolean any_date_changed;
941
942        now = time (NULL);
943
944        /* Note that this might overflow once in a blue moon, but the
945         * only side-effect of that would be a slightly-early check
946         * for changes.
947         */
948        if (tracker->last_checked + tracker->check_interval >= now) {
949                return FALSE;
950        }
951
952        any_date_changed = FALSE;
953
954        g_hash_table_foreach (tracker->records, check_and_update_one, &any_date_changed);
955
956        tracker->last_checked = now;
957
958        return any_date_changed;
959}
Note: See TracBrowser for help on using the repository browser.