~ubuntu-branches/ubuntu/trusty/pcmanfm/trusty-proposed

« back to all changes in this revision

Viewing changes to src/mime-type/mime-type.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Lee
  • Date: 2008-09-26 10:19:20 UTC
  • mfrom: (4.1.5 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080926101920-cfldybkmwgwrtv9u
Tags: 0.5-3
* Correct spellings,  03_correct_spelling.dpatch (Closes:498794) 
* Code in some files are taken from other projects, added these
  informations into copyright file. (Closes:499678)
* Applied 04_defaut_terminal.dpatch to support x-terminal-emulator
  alternative. (Closes:497494) 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      mime-type.c
 
3
 *
 
4
 *      Copyright 2007 Houng Jen Yee (PCMan) <pcman.tw@gmail.com>
 
5
 *
 
6
 *      This program is free software; you can redistribute it and/or modify
 
7
 *      it under the terms of the GNU General Public License as published by
 
8
 *      the Free Software Foundation; either version 2 of the License, or
 
9
 *      (at your option) any later version.
 
10
 *
 
11
 *      This program is distributed in the hope that it will be useful,
 
12
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *      GNU General Public License for more details.
 
15
 *
 
16
 *      You should have received a copy of the GNU General Public License
 
17
 *      along with this program; if not, write to the Free Software
 
18
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
19
 *      MA 02110-1301, USA.
 
20
 */
 
21
 
 
22
/* Currently this library is NOT MT-safe */
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
#include <config.h>
 
26
#endif
 
27
 
 
28
#include "mime-type.h"
 
29
#include "mime-cache.h"
 
30
 
 
31
#include <string.h>
 
32
 
 
33
#include <fcntl.h>
 
34
#include <unistd.h>
 
35
#include <sys/stat.h>
 
36
#include <sys/types.h>
 
37
 
 
38
#include "glib-mem.h"
 
39
 
 
40
/*
 
41
 * FIXME:
 
42
 * Currently, mmap cannot be used because of the limitation of mmap.
 
43
 * When a file is mapped for mime-type sniffing (checking file magic),
 
44
 * they could be deleted during the check and hence result in Bus error.
 
45
 * (Refer to the man page of mmap for detail)
 
46
 * So here I undef HAVE_MMAP to disable the implementation using mmap.
 
47
 */
 
48
#undef HAVE_MMAP
 
49
 
 
50
#ifdef HAVE_MMAP
 
51
#include <sys/mman.h>
 
52
#endif
 
53
 
 
54
/* max extent used to checking text files */
 
55
#define TEXT_MAX_EXTENT 512
 
56
 
 
57
const char xdg_mime_type_unknown[] = "application/octet-stream";
 
58
const char xdg_mime_type_directory[] = "inode/directory";
 
59
const char xdg_mime_type_executable[] = "application/x-executable";
 
60
const char xdg_mime_type_plain_text[] = "text/plain";
 
61
 
 
62
static MimeCache** caches = NULL;
 
63
static guint n_caches = 0;
 
64
guint32 mime_cache_max_extent = 0;
 
65
 
 
66
/* allocated buffer used for mime magic checking to
 
67
     prevent frequent memory allocation */
 
68
static char* mime_magic_buf = NULL;
 
69
/* for MT safety, the buffer should be locked */
 
70
G_LOCK_DEFINE_STATIC(mime_magic_buf);
 
71
 
 
72
/* load all mime.cache files on the system,
 
73
 * including /usr/share/mime/mime.cache,
 
74
 * /usr/local/share/mime/mime.cache,
 
75
 * and $HOME/.local/share/mime/mime.cache. */
 
76
static void mime_cache_load_all();
 
77
 
 
78
/* free all mime.cache files on the system */
 
79
static void mime_cache_free_all();
 
80
 
 
81
static gboolean mime_type_is_data_plain_text( const char* data, int len );
 
82
 
 
83
/*
 
84
 * Get mime-type of the specified file (quick, but less accurate):
 
85
 * Mime-type of the file is determined by cheking the filename only.
 
86
 * If statbuf != NULL, it will be used to determine if the file is a directory.
 
87
*/
 
88
const char* mime_type_get_by_filename( const char* filename, struct stat* statbuf )
 
89
{
 
90
    const char* type = NULL, *suffix_pos = NULL, *prev_suffix_pos = (const char*)-1;
 
91
    int i;
 
92
    MimeCache* cache;
 
93
 
 
94
    if( G_UNLIKELY( statbuf && S_ISDIR( statbuf->st_mode ) ) )
 
95
        return XDG_MIME_TYPE_DIRECTORY;
 
96
 
 
97
    for( i = 0; ! type && i < n_caches; ++i )
 
98
    {
 
99
        cache = caches[i];
 
100
        type = mime_cache_lookup_literal( cache, filename );
 
101
        if( G_LIKELY( ! type ) )
 
102
        {
 
103
            const char* _type = mime_cache_lookup_suffix( cache, filename, &suffix_pos );
 
104
            if( _type && suffix_pos < prev_suffix_pos )
 
105
            {
 
106
                type = _type;
 
107
                prev_suffix_pos = suffix_pos;
 
108
            }
 
109
        }
 
110
    }
 
111
 
 
112
    if( G_UNLIKELY( ! type ) )  /* glob matching */
 
113
    {
 
114
        int max_glob_len = 0, glob_len = 0;
 
115
        for( i = 0; ! type && i < n_caches; ++i )
 
116
        {
 
117
            cache = caches[i];
 
118
            const char* matched_type;
 
119
            matched_type = mime_cache_lookup_glob( cache, filename, &glob_len );
 
120
            /* according to the mime.cache 1.0 spec, we should use the longest glob matched. */
 
121
            if( matched_type && glob_len > max_glob_len )
 
122
            {
 
123
                type = matched_type;
 
124
                max_glob_len = glob_len;
 
125
            }
 
126
        }
 
127
    }
 
128
 
 
129
 
 
130
    return type && *type ? type : XDG_MIME_TYPE_UNKNOWN;
 
131
}
 
132
 
 
133
/*
 
134
 * Get mime-type info of the specified file (slow, but more accurate):
 
135
 * To determine the mime-type of the file, mime_type_get_by_filename() is
 
136
 * tried first.  If the mime-type couldn't be determined, the content of
 
137
 * the file will be checked, which is much more time-consuming.
 
138
 * If statbuf is not NULL, it will be used to determine if the file is a directory,
 
139
 * or if the file is an executable file; otherwise, the function will call stat()
 
140
 * to gather this info itself. So if you already have stat info of the file,
 
141
 * pass it to the function to prevent checking the file stat again.
 
142
 * If you have basename of the file, pass it to the function can improve the
 
143
 * efifciency, too. Otherwise, the function will try to get the basename of
 
144
 * the specified file again.
 
145
*/
 
146
const char* mime_type_get_by_file( const char* filepath, struct stat* statbuf, const char* basename )
 
147
{
 
148
    const char* type;
 
149
    struct stat _statbuf;
 
150
 
 
151
    if( statbuf == NULL || G_UNLIKELY( S_ISLNK(statbuf->st_mode) ) )
 
152
    {
 
153
        statbuf = &_statbuf;
 
154
        if( stat ( filepath, statbuf ) == -1 )
 
155
            return XDG_MIME_TYPE_UNKNOWN;
 
156
    }
 
157
 
 
158
    if( S_ISDIR( statbuf->st_mode ) )
 
159
        return XDG_MIME_TYPE_DIRECTORY;
 
160
 
 
161
    if( basename == NULL )
 
162
    {
 
163
        basename = g_utf8_strrchr( filepath, -1, '/' );
 
164
        if( G_LIKELY( basename ) )
 
165
            ++basename;
 
166
        else
 
167
            basename = filepath;
 
168
    }
 
169
 
 
170
    if( G_LIKELY(basename) )
 
171
    {
 
172
        type = mime_type_get_by_filename( basename, statbuf );
 
173
        if( G_LIKELY( strcmp( type, XDG_MIME_TYPE_UNKNOWN ) ) )
 
174
            return type;
 
175
        type = NULL;
 
176
    }
 
177
 
 
178
    if( G_LIKELY(statbuf->st_size > 0) )
 
179
    {
 
180
        int fd = -1;
 
181
        char* data;
 
182
 
 
183
        /* Open the file and map it into memory */
 
184
        fd = open ( filepath, O_RDONLY, 0 );
 
185
        if ( fd != -1 )
 
186
        {
 
187
            int len = mime_cache_max_extent < statbuf->st_size ?  mime_cache_max_extent : statbuf->st_size;
 
188
#ifdef HAVE_MMAP
 
189
            data = (char*) mmap( NULL, len, PROT_READ, MAP_SHARED, fd, 0 );
 
190
#else
 
191
            /*
 
192
             * FIXME: Can g_alloca() be used here? It's very fast, but is it safe?
 
193
             * Actually, we can allocate a block of memory with the size of mime_cache_max_extent,
 
194
             * then we don't need to  do dynamic allocation/free every time, but multi-threading
 
195
             * will be a nightmare, so...
 
196
             */
 
197
            /* try to lock the common buffer */
 
198
            if( G_LIKELY( G_TRYLOCK( mime_magic_buf ) ) )
 
199
                data = mime_magic_buf;
 
200
            else /* the buffer is in use, allocate new one */
 
201
                data = g_malloc( len );
 
202
 
 
203
            len = read( fd, data, len );
 
204
 
 
205
            if( G_UNLIKELY( len == -1 ) )
 
206
            {
 
207
                if( G_LIKELY( data == mime_magic_buf ) )
 
208
                    G_UNLOCK( mime_magic_buf );
 
209
                else
 
210
                    g_free( data );
 
211
                data = (void*)-1;
 
212
            }
 
213
#endif
 
214
            if( data != (void*)-1 )
 
215
            {
 
216
                int i;
 
217
                for( i = 0; ! type && i < n_caches; ++i )
 
218
                    type = mime_cache_lookup_magic( caches[i], data, len );
 
219
 
 
220
                /* Check for executable file */
 
221
                if( ! type && g_file_test( filepath, G_FILE_TEST_IS_EXECUTABLE ) )
 
222
                    type = XDG_MIME_TYPE_EXECUTABLE;
 
223
 
 
224
                /* fallback: check for plain text */
 
225
                if( ! type )
 
226
                {
 
227
                    if( mime_type_is_data_plain_text( data, len > TEXT_MAX_EXTENT ? TEXT_MAX_EXTENT : len ) )
 
228
                        type = XDG_MIME_TYPE_PLAIN_TEXT;
 
229
                }
 
230
 
 
231
#ifdef HAVE_MMAP
 
232
                munmap ( (char*)data, len );
 
233
#else
 
234
                if( G_LIKELY( data == mime_magic_buf ) )
 
235
                    G_UNLOCK( mime_magic_buf );  /* unlock the common buffer */
 
236
                else /* we use our own buffer */
 
237
                    g_free( data );
 
238
#endif
 
239
            }
 
240
            close( fd );
 
241
        }
 
242
    }
 
243
    else
 
244
    {
 
245
        /* empty file can be viewed as text file */
 
246
        type = XDG_MIME_TYPE_PLAIN_TEXT;
 
247
    }
 
248
    return type && *type ? type : XDG_MIME_TYPE_UNKNOWN;
 
249
}
 
250
 
 
251
/* Get the name of mime-type */
 
252
/*const char* mime_type_get_type( MimeInfo* info )
 
253
{
 
254
    return info->type_name;
 
255
}
 
256
*/
 
257
 
 
258
static char* parse_xml_desc( const char* buf, size_t len, const char* locale )
 
259
{
 
260
    const char *buf_end = buf + len;
 
261
    const char *comment = NULL, *comment_end, *eng_comment;
 
262
    size_t eng_comment_len = 0, comment_len = 0;
 
263
    char target[64];
 
264
    static const char end_comment_tag[]="</comment>";
 
265
 
 
266
    eng_comment = g_strstr_len( buf, len, "<comment>" );    /* default English comment */
 
267
    if( G_UNLIKELY( ! eng_comment ) ) /* This xml file is invalid */
 
268
        return NULL;
 
269
    len -= 9;
 
270
    eng_comment += 9;
 
271
    comment_end = g_strstr_len( eng_comment, len, end_comment_tag ); /* find </comment> */
 
272
    if( G_UNLIKELY( ! comment_end ) )
 
273
        return NULL;
 
274
    eng_comment_len = comment_end - eng_comment;
 
275
 
 
276
    if( G_LIKELY( locale ) )
 
277
    {
 
278
        int target_len = g_snprintf( target, 64, "<comment xml:lang=\"%s\">", locale);
 
279
        buf = comment_end + 10;
 
280
        len = (buf_end - buf);
 
281
        if( G_LIKELY( ( comment = g_strstr_len( buf, len, target ) ) ) )
 
282
        {
 
283
            len -= target_len;
 
284
            comment += target_len;
 
285
            comment_end = g_strstr_len( comment, len, end_comment_tag );    /* find </comment> */
 
286
            if( G_LIKELY( comment_end ) )
 
287
                comment_len = (comment_end - comment);
 
288
            else
 
289
                comment = NULL;
 
290
        }
 
291
    }
 
292
    if( G_LIKELY( comment ) )
 
293
        return g_strndup( comment, comment_len );
 
294
    return g_strndup( eng_comment, eng_comment_len );
 
295
}
 
296
 
 
297
static char* _mime_type_get_desc( const char* type, const char* data_dir, const char* locale )
 
298
{
 
299
    int fd;
 
300
    struct stat statbuf;
 
301
    char *buffer, *_locale, *desc;
 
302
    char file_path[ 256 ];
 
303
 
 
304
    /* FIXME: This path shouldn't be hard-coded. */
 
305
    g_snprintf( file_path, 256, "%s/mime/%s.xml", data_dir, type );
 
306
 
 
307
    fd = open ( file_path, O_RDONLY, 0 );
 
308
    if ( G_UNLIKELY( fd == -1 ) )
 
309
        return NULL;
 
310
    if( G_UNLIKELY( fstat ( fd, &statbuf ) == -1 ) )
 
311
    {
 
312
        close( fd );
 
313
        return NULL;
 
314
    }
 
315
#ifdef HAVE_MMAP
 
316
    buffer = (char*)mmap( NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0 );
 
317
#else
 
318
    buffer = (char*)g_malloc( statbuf.st_size );
 
319
    if( read( fd, buffer, statbuf.st_size ) == -1 )
 
320
    {
 
321
        g_free( buffer );
 
322
        buffer = (char*)-1;
 
323
    }
 
324
#endif
 
325
    close( fd );
 
326
    if ( G_UNLIKELY( buffer == (void*)-1 ) )
 
327
        return NULL;
 
328
 
 
329
    _locale = NULL;
 
330
    if( ! locale )
 
331
    {
 
332
        const char* const * langs = g_get_language_names();
 
333
        char* dot = strchr( langs[0], '.' );
 
334
        if( dot )
 
335
            locale = _locale = g_strndup( langs[0], (size_t)(dot - langs[0]) );
 
336
        else
 
337
            locale = langs[0];
 
338
    }
 
339
    desc = parse_xml_desc( buffer, statbuf.st_size, locale );
 
340
    g_free( _locale );
 
341
 
 
342
#ifdef HAVE_MMAP
 
343
    munmap ( buffer, statbuf.st_size );
 
344
#else
 
345
    g_free( buffer );
 
346
#endif
 
347
    return desc;
 
348
}
 
349
 
 
350
/*
 
351
 * Get human-readable description of the mime-type
 
352
 * If locale is NULL, current locale will be used.
 
353
 * The returned string should be freed when no longer used.
 
354
*/
 
355
char* mime_type_get_desc( const char* type, const char* locale )
 
356
{
 
357
    char* desc;
 
358
    const gchar* const * dir;
 
359
 
 
360
    dir = g_get_system_data_dirs();
 
361
    for( ; *dir; ++dir )
 
362
    {
 
363
        desc = _mime_type_get_desc( type, *dir, locale );
 
364
        if( G_LIKELY(desc) )
 
365
            return desc;
 
366
    }
 
367
 
 
368
    /*
 
369
     * FIXME: According to specs on freedesktop.org, user_data_dir has
 
370
     * higher priority than system_data_dirs, but in most cases, there was
 
371
     * no file, or very few files in user_data_dir, so checking it first will
 
372
     * result in many unnecessary open() system calls, yealding bad performance.
 
373
     * Since the spec really sucks, we don't follow it here.
 
374
     */
 
375
    desc = _mime_type_get_desc( type, g_get_user_data_dir(), locale );
 
376
    return desc;
 
377
}
 
378
 
 
379
void mime_type_finalize()
 
380
{
 
381
/*
 
382
    if( G_LIKELY( table ) )
 
383
    {
 
384
        g_hash_table_destroy( table );
 
385
        table = NULL;
 
386
    }
 
387
*/
 
388
    mime_cache_free_all();
 
389
}
 
390
 
 
391
#if 0
 
392
void test_parents(const char* type)
 
393
{
 
394
    int i;
 
395
    const char** parents = NULL;
 
396
    const char** p;
 
397
 
 
398
    for( i = 0; i < n_caches; ++i )
 
399
    {
 
400
        parents = mime_cache_lookup_parents( caches[i], type );
 
401
        if( parents )
 
402
            break;
 
403
    }
 
404
    if( parents )
 
405
        for( p = parents; *p; ++p )
 
406
        {
 
407
            g_debug( "%s is parent of %s", *p, type );
 
408
        }
 
409
    else
 
410
        g_debug( "no parent found" );
 
411
}
 
412
 
 
413
void test_alias( const char* type )
 
414
{
 
415
    int i;
 
416
    const char* alias = NULL;
 
417
    for( i = 0; i < n_caches; ++i )
 
418
    {
 
419
        alias = mime_cache_lookup_alias( caches[i], type );
 
420
        if( alias )
 
421
            break;
 
422
    }
 
423
    g_debug("test:\nalias of %s is %s", type, alias );
 
424
 
 
425
}
 
426
#endif
 
427
 
 
428
void mime_type_init()
 
429
{
 
430
    mime_cache_load_all();
 
431
//    table = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, (GDestroyNotify)mime_type_unref );
 
432
}
 
433
 
 
434
/* load all mime.cache files on the system,
 
435
 * including /usr/share/mime/mime.cache,
 
436
 * /usr/local/share/mime/mime.cache,
 
437
 * and $HOME/.local/share/mime/mime.cache. */
 
438
void mime_cache_load_all()
 
439
{
 
440
    const char* const * dirs;
 
441
    int i;
 
442
    const char filename[] = "/mime/mime.cache";
 
443
    char* path;
 
444
 
 
445
    dirs = g_get_system_data_dirs();
 
446
    n_caches = g_strv_length( (char**)dirs ) + 1;
 
447
    caches = (MimeCache**)g_slice_alloc( n_caches * sizeof(MimeCache*) );
 
448
 
 
449
    path = g_build_filename( g_get_user_data_dir(), filename, NULL );
 
450
    caches[0] = mime_cache_new( path );
 
451
    g_free( path );
 
452
    if( caches[0]->magic_max_extent > mime_cache_max_extent )
 
453
        mime_cache_max_extent = caches[0]->magic_max_extent;
 
454
 
 
455
    for( i = 1; i < n_caches; ++i )
 
456
    {
 
457
        path = g_build_filename( dirs[i - 1], filename, NULL );
 
458
        caches[ i ] = mime_cache_new( path );
 
459
        g_free( path );
 
460
        if( caches[i]->magic_max_extent > mime_cache_max_extent )
 
461
            mime_cache_max_extent = caches[i]->magic_max_extent;
 
462
    }
 
463
    mime_magic_buf = g_malloc( mime_cache_max_extent );
 
464
    return ;
 
465
}
 
466
 
 
467
/* free all mime.cache files on the system */
 
468
void mime_cache_free_all()
 
469
{
 
470
    mime_cache_foreach( (GFunc)mime_cache_free, NULL );
 
471
    g_slice_free1( n_caches * sizeof(MimeCache*), caches );
 
472
    n_caches = 0;
 
473
    caches = NULL;
 
474
    mime_cache_max_extent = 0;
 
475
 
 
476
    g_free( mime_magic_buf );
 
477
    mime_magic_buf = NULL;
 
478
}
 
479
 
 
480
/* Iterate through all mime caches */
 
481
void mime_cache_foreach( GFunc func, gpointer user_data )
 
482
{
 
483
    int i;
 
484
    for( i = 0; i < n_caches; ++i )
 
485
        func( caches[i], user_data );
 
486
}
 
487
 
 
488
gboolean mime_cache_reload( MimeCache* cache )
 
489
{
 
490
    int i;
 
491
    gboolean ret = mime_cache_load( cache, cache->file_path );
 
492
    /* recalculate max magic extent */
 
493
    for( i = 0; i < n_caches; ++i )
 
494
    {
 
495
        if( caches[i]->magic_max_extent > mime_cache_max_extent )
 
496
            mime_cache_max_extent = caches[i]->magic_max_extent;
 
497
    }
 
498
 
 
499
    G_LOCK( mime_magic_buf );
 
500
 
 
501
    mime_magic_buf = g_realloc( mime_magic_buf, mime_cache_max_extent );
 
502
 
 
503
    G_UNLOCK( mime_magic_buf );
 
504
 
 
505
    return ret;
 
506
}
 
507
 
 
508
gboolean mime_type_is_data_plain_text( const char* data, int len )
 
509
{
 
510
    int i;
 
511
    if ( G_LIKELY( len >= 0 && data ) )
 
512
    {
 
513
        for ( i = 0; i < len; ++i )
 
514
        {
 
515
            if ( data[ i ] == '\0' )
 
516
                return FALSE;
 
517
        }
 
518
        return TRUE;
 
519
    }
 
520
    return FALSE;
 
521
}
 
522
 
 
523
gboolean mime_type_is_text_file( const char *file_path, const char* mime_type )
 
524
{
 
525
    int file;
 
526
    int rlen;
 
527
    gboolean ret = FALSE;
 
528
 
 
529
    if( mime_type )
 
530
    {
 
531
        if( mime_type_is_subclass( mime_type, XDG_MIME_TYPE_PLAIN_TEXT ) )
 
532
            return TRUE;
 
533
        if( ! g_str_has_prefix( mime_type, "text/" ) && ! g_str_has_prefix( mime_type, "application/" ) )
 
534
            return FALSE;
 
535
    }
 
536
 
 
537
    if( !file_path )
 
538
        return FALSE;
 
539
 
 
540
    file = open ( file_path, O_RDONLY );
 
541
    if ( file != -1 )
 
542
    {
 
543
        struct stat statbuf;
 
544
        if( fstat( file, &statbuf ) != -1 )
 
545
        {
 
546
            if( S_ISREG( statbuf.st_mode ) )
 
547
            {
 
548
#ifdef HAVE_MMAP
 
549
                char* data;
 
550
                rlen = statbuf.st_size < TEXT_MAX_EXTENT ? statbuf.st_size : TEXT_MAX_EXTENT;
 
551
                data = (char*) mmap( NULL, rlen, PROT_READ, MAP_SHARED, file, 0 );
 
552
                ret = mime_type_is_data_plain_text( data, rlen );
 
553
                munmap ( (char*)data, rlen );
 
554
#else
 
555
                unsigned char data[ TEXT_MAX_EXTENT ];
 
556
                rlen = read ( file, data, sizeof( data ) );
 
557
                ret = mime_type_is_data_plain_text( (char*) data, rlen );
 
558
#endif
 
559
            }
 
560
        }
 
561
        close ( file );
 
562
    }
 
563
    return ret;
 
564
}
 
565
 
 
566
gboolean mime_type_is_executable_file( const char *file_path, const char* mime_type )
 
567
{
 
568
    if ( !mime_type )
 
569
    {
 
570
        mime_type = mime_type_get_by_file( file_path, NULL, NULL );
 
571
    }
 
572
 
 
573
    /*
 
574
    * Only executable types can be executale.
 
575
    * Since some common types, such as application/x-shellscript,
 
576
    * are not in mime database, we have to add them ourselves.
 
577
    */
 
578
    if ( mime_type != XDG_MIME_TYPE_UNKNOWN &&
 
579
            (mime_type_is_subclass( mime_type, XDG_MIME_TYPE_EXECUTABLE ) ||
 
580
            mime_type_is_subclass( mime_type, "application/x-shellscript" ) ) )
 
581
    {
 
582
        if ( file_path )
 
583
        {
 
584
            if ( ! g_file_test( file_path, G_FILE_TEST_IS_EXECUTABLE ) )
 
585
                return FALSE;
 
586
        }
 
587
        return TRUE;
 
588
    }
 
589
    return FALSE;
 
590
}
 
591
 
 
592
/* Check if the specified mime_type is the subclass of the specified parent type */
 
593
gboolean mime_type_is_subclass( const char* type, const char* parent )
 
594
{
 
595
    int i;
 
596
    const char** parents = NULL;
 
597
    const char** p;
 
598
 
 
599
    /* special case, the type specified is identical to the parent type. */
 
600
    if( G_UNLIKELY( 0 == strcmp(type, parent) ) )
 
601
        return TRUE;
 
602
 
 
603
    for( i = 0; i < n_caches; ++i )
 
604
    {
 
605
        parents = mime_cache_lookup_parents( caches[i], type );
 
606
        if( parents )
 
607
        {
 
608
            for( p = parents; *p; ++p )
 
609
            {
 
610
                if( 0 == strcmp( parent, *p ) )
 
611
                    return TRUE;
 
612
            }
 
613
        }
 
614
    }
 
615
    return FALSE;
 
616
}
 
617
 
 
618
/*
 
619
 * Get all parent type of this mime_type
 
620
 * The returned string array should be freed with g_strfreev().
 
621
 */
 
622
char** mime_type_get_parents( const char* type )
 
623
{
 
624
    int i;
 
625
    const char** parents = NULL;
 
626
    const char** p;
 
627
    GArray* ret = g_array_sized_new( TRUE, TRUE, sizeof(char*), 5 );
 
628
 
 
629
    for( i = 0; i < n_caches; ++i )
 
630
    {
 
631
        parents = mime_cache_lookup_parents( caches[i], type );
 
632
        if( parents )
 
633
        {
 
634
            for( p = parents; *p; ++p )
 
635
            {
 
636
                char* parent = g_strdup( *p );
 
637
                g_array_append_val( ret, parent );
 
638
            }
 
639
        }
 
640
    }
 
641
    return (char**)g_array_free( ret, (0 == ret->len) );
 
642
}
 
643
 
 
644
/*
 
645
 * Get all alias types of this mime_type
 
646
 * The returned string array should be freed with g_strfreev().
 
647
 */
 
648
char** mime_type_get_alias( const char* type )
 
649
{
 
650
    int i;
 
651
    const char** alias = NULL;
 
652
    const char** p;
 
653
    GArray* ret = g_array_sized_new( TRUE, TRUE, sizeof(char*), 5 );
 
654
 
 
655
    for( i = 0; i < n_caches; ++i )
 
656
    {
 
657
        alias = (const char **) mime_cache_lookup_alias( caches[i], type );
 
658
        if( alias )
 
659
        {
 
660
            for( p = alias; *p; ++p )
 
661
            {
 
662
                char* type = g_strdup( *p );
 
663
                g_array_append_val( ret, type );
 
664
            }
 
665
        }
 
666
    }
 
667
    return (char**)g_array_free( ret, (0 == ret->len) );
 
668
}
 
669
 
 
670
/*
 
671
 * Get mime caches
 
672
 */
 
673
MimeCache** mime_type_get_caches( int* n )
 
674
{
 
675
    *n = n_caches;
 
676
    return caches;
 
677
}