/* * fm-mime-type.c * * Copyright 2009 PCMan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "fm-mime-type.h" #include #include #include #include #include #ifdef HAVE_MMAP #include #endif /* FIXME: how can we handle reload of xdg mime? */ static GHashTable *mime_hash = NULL; G_LOCK_DEFINE(mime_hash); static guint reload_callback_id = 0; static GList* reload_cb = NULL; //static VFSFileMonitor** mime_caches_monitor = NULL; typedef struct { GFreeFunc cb; gpointer user_data; }FmMimeReloadCbEnt; void fm_mime_type_init() { mime_hash = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, fm_mime_type_unref ); } void fm_mime_type_finalize() { g_hash_table_destroy( mime_hash ); } FmMimeType* fm_mime_type_get_for_file_name( const char* ufile_name ) { FmMimeType* mime_type; char * type; gboolean uncertain; type = g_content_type_guess( ufile_name, NULL, 0, &uncertain ); mime_type = fm_mime_type_get_for_type( type ); g_free(type); return mime_type; } FmMimeType* fm_mime_type_get_for_native_file( const char* file_path, const char* base_name, struct stat* pstat ) { FmMimeType* mime_type; struct stat st; if( !pstat ) { pstat = &st; if( stat( file_path, &st ) == -1 ) return NULL; } if( S_ISREG(pstat->st_mode) ) { gboolean uncertain; char* type = g_content_type_guess( base_name, NULL, 0, &uncertain ); if( uncertain ) { int fd, len; if( pstat->st_size == 0 ) /* empty file = text file with 0 characters in it. */ return fm_mime_type_get_for_type( "text/plain" ); fd = open(file_path, O_RDONLY); if( fd >= 0 ) { #ifdef HAVE_MMAP const char* buf; len = pstat->st_size > 4096 ? 4096 : pstat->st_size; buf = (const char*)mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); if(G_LIKELY(buf != MAP_FAILED)) { g_free(type); type = g_content_type_guess( NULL, buf, len, &uncertain ); munmap(buf, len); } #else char buf[4096]; len = read(fd, buf, 4096); g_free(type); type = g_content_type_guess( NULL, buf, len, &uncertain ); #endif close(fd); } } mime_type = fm_mime_type_get_for_type( type ); g_free(type); return mime_type; } if( S_ISDIR(pstat->st_mode) ) return fm_mime_type_get_for_type( "inode/directory" ); if (S_ISCHR(pstat->st_mode)) return fm_mime_type_get_for_type( "inode/chardevice" ); if (S_ISBLK(pstat->st_mode)) return fm_mime_type_get_for_type( "inode/blockdevice" ); if (S_ISFIFO(pstat->st_mode)) return fm_mime_type_get_for_type( "inode/fifo" ); if (S_ISLNK(pstat->st_mode)) return fm_mime_type_get_for_type( "inode/symlink" ); #ifdef S_ISSOCK if (S_ISSOCK(pstat->st_mode)) return fm_mime_type_get_for_type( "inode/socket" ); #endif /* impossible */ g_error( "Invalid stat mode: %s", base_name ); return NULL; } FmMimeType* fm_mime_type_get_for_type( const char* type ) { FmMimeType * mime_type; G_LOCK( mime_hash ); mime_type = g_hash_table_lookup( mime_hash, type ); if ( !mime_type ) { mime_type = fm_mime_type_new( type ); g_hash_table_insert( mime_hash, mime_type->type, mime_type ); } G_UNLOCK( mime_hash ); fm_mime_type_ref( mime_type ); return mime_type; } FmMimeType* fm_mime_type_new( const char* type_name ) { FmMimeType * mime_type = g_slice_new0( FmMimeType ); GIcon* gicon; mime_type->type = g_strdup( type_name ); mime_type->n_ref = 1; gicon = g_content_type_get_icon(mime_type->type); if( strcmp(mime_type->type, "inode/directory") == 0 ) g_themed_icon_prepend_name(G_THEMED_ICON(gicon), "folder"); else if( g_content_type_can_be_executable(mime_type->type) ) g_themed_icon_append_name(G_THEMED_ICON(gicon), "application-x-executable"); mime_type->icon = fm_icon_from_gicon(gicon); g_object_unref(gicon); #if 0 /* TODO: Special case desktop dir? That could be expensive with xdg dirs... */ if (strcmp (path, g_get_home_dir ()) == 0) type_icon = "user-home"; else type_icon = "text-x-generic"; #endif return mime_type; } FmMimeType* fm_mime_type_ref( FmMimeType* mime_type ) { g_atomic_int_inc(&mime_type->n_ref); return mime_type; } void fm_mime_type_unref( gpointer mime_type_ ) { FmMimeType* mime_type = (FmMimeType*)mime_type_; if ( g_atomic_int_dec_and_test(&mime_type->n_ref) ) { g_free( mime_type->type ); if ( mime_type->icon ) fm_icon_unref( mime_type->icon ); g_slice_free( FmMimeType, mime_type ); } } FmIcon* fm_mime_type_get_icon( FmMimeType* mime_type ) { return mime_type->icon; } const char* fm_mime_type_get_type( FmMimeType* mime_type ) { return mime_type->type; } /* Get human-readable description of mime type */ const char* fm_mime_type_get_desc( FmMimeType* mime_type ) { /* FIXME: is locking needed here or not? */ if ( G_UNLIKELY( ! mime_type->description ) ) { mime_type->description = g_content_type_get_description( mime_type->type ); /* FIXME: should handle this better */ if ( G_UNLIKELY( ! mime_type->description || ! *mime_type->description ) ) mime_type->description = g_content_type_get_description( mime_type->type ); } return mime_type->description; }