efl/src/lib/efreet/efreet_mime.c

1613 lines
42 KiB
C
Raw Normal View History

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
2012-11-23 22:43:00 -08:00
#include "efreet_alloca.h"
2009-08-28 22:29:05 -07:00
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fnmatch.h>
#ifdef _WIN32
# include <winsock2.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
2009-04-20 23:16:19 -07:00
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#include <Ecore.h>
#include <Ecore_File.h>
/* define macros and variable for using the eina logging system */
#define EFREET_MODULE_LOG_DOM _efreet_mime_log_dom
static int _efreet_mime_log_dom = -1;
#include "Efreet.h"
#include "Efreet_Mime.h"
#include "efreet_private.h"
static Eina_List *globs = NULL; /* contains Efreet_Mime_Glob structs */
static Eina_List *magics = NULL; /* contains Efreet_Mime_Magic structs */
static Eina_Hash *wild = NULL; /* contains *.ext and mime.types globs*/
static Eina_Hash *monitors = NULL; /* contains file monitors */
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
static Eina_Hash *mime_icons = NULL; /* contains cache with mime->icons */
static Eina_Inlist *mime_icons_lru = NULL;
static unsigned int _efreet_mime_init_count = 0;
static const char *_mime_inode_symlink = NULL;
static const char *_mime_inode_fifo = NULL;
static const char *_mime_inode_chardevice = NULL;
static const char *_mime_inode_blockdevice = NULL;
static const char *_mime_inode_socket = NULL;
static const char *_mime_inode_mountpoint = NULL;
static const char *_mime_inode_directory = NULL;
static const char *_mime_application_x_executable = NULL;
static const char *_mime_application_octet_stream = NULL;
static const char *_mime_text_plain = NULL;
/**
2007-07-01 09:32:34 -07:00
* @internal
* @brief Holds whether we are big/little endian
* @note This is set during efreet_mime_init based on
* a runtime check.
*/
static enum
{
EFREET_ENDIAN_BIG = 0,
EFREET_ENDIAN_LITTLE = 1
} efreet_mime_endianess = EFREET_ENDIAN_BIG;
/*
* Buffer sized used for magic checks. The default is good enough for the
2007-07-01 09:32:34 -07:00
* current set of magic rules. This setting is only here for the future.
*/
#define EFREET_MIME_MAGIC_BUFFER_SIZE 512
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
/*
* Minimum timeout in seconds between mime-icons cache flush.
*/
#define EFREET_MIME_ICONS_FLUSH_TIMEOUT 60
/*
* Timeout in seconds, when older mime-icons items are expired.
*/
#define EFREET_MIME_ICONS_EXPIRE_TIMEOUT 600
/*
* mime-icons maximum population.
*/
#define EFREET_MIME_ICONS_MAX_POPULATION 512
/*
* If defined, dump mime-icons statistics after flush.
*/
//#define EFREET_MIME_ICONS_DEBUG
2007-07-01 09:32:34 -07:00
typedef struct Efreet_Mime_Glob Efreet_Mime_Glob;
struct Efreet_Mime_Glob
{
const char *glob;
const char *mime;
2007-07-01 09:32:34 -07:00
};
2007-07-01 09:32:34 -07:00
typedef struct Efreet_Mime_Magic Efreet_Mime_Magic;
struct Efreet_Mime_Magic
{
unsigned int priority;
const char *mime;
Eina_List *entries;
2007-07-01 09:32:34 -07:00
};
2007-07-01 09:32:34 -07:00
typedef struct Efreet_Mime_Magic_Entry Efreet_Mime_Magic_Entry;
struct Efreet_Mime_Magic_Entry
{
unsigned int indent;
unsigned int offset;
unsigned int word_size;
unsigned int range_len;
unsigned short value_len;
char *mask;
char *value;
2007-07-01 09:32:34 -07:00
};
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
typedef struct Efreet_Mime_Icon_Entry_Head Efreet_Mime_Icon_Entry_Head;
struct Efreet_Mime_Icon_Entry_Head
{
EINA_INLIST; /* node of mime_icons_lru */
Eina_Inlist *list;
const char *mime;
double timestamp;
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
};
typedef struct Efreet_Mime_Icon_Entry Efreet_Mime_Icon_Entry;
struct Efreet_Mime_Icon_Entry
{
EINA_INLIST;
const char *icon;
const char *theme;
unsigned int size;
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
};
static int efreet_mime_glob_remove(const char *glob);
static void efreet_mime_mime_types_load(const char *file);
static void efreet_mime_shared_mimeinfo_globs_load(const char *file);
static void efreet_mime_shared_mimeinfo_magic_load(const char *file);
static void efreet_mime_shared_mimeinfo_magic_parse(char *data, int size);
static const char *efreet_mime_magic_check_priority(const char *file,
unsigned int start,
unsigned int end);
static int efreet_mime_init_files(void);
2007-07-03 11:01:56 -07:00
static const char *efreet_mime_special_check(const char *file);
static const char *efreet_mime_fallback_check(const char *file);
static void efreet_mime_glob_free(void *data);
static void efreet_mime_magic_free(void *data);
static void efreet_mime_magic_entry_free(void *data);
static int efreet_mime_glob_match(const char *str, const char *glob);
static int efreet_mime_glob_case_match(char *str, const char *glob);
static int efreet_mime_endian_check(void);
static void efreet_mime_monitor_add(const char *file);
static void efreet_mime_cb_update_file(void *data,
Ecore_File_Monitor *monitor,
Ecore_File_Event event,
const char *path);
static void efreet_mime_icons_flush(double now);
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
static void efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry);
static void efreet_mime_icon_entry_add(const char *mime,
const char *icon,
const char *theme,
unsigned int size);
static const char *efreet_mime_icon_entry_find(const char *mime,
const char *theme,
unsigned int size);
static void efreet_mime_icons_debug(void);
2007-11-04 01:32:35 -08:00
EAPI int
efreet_mime_init(void)
{
if (++_efreet_mime_init_count != 1)
return _efreet_mime_init_count;
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
if (!ecore_init())
return --_efreet_mime_init_count;
if (!ecore_file_init())
goto shutdown_ecore;
if (!efreet_init())
goto shutdown_ecore_file;
_efreet_mime_log_dom = eina_log_domain_register
("efreet_mime", EFREET_DEFAULT_LOG_COLOR);
if (_efreet_mime_log_dom < 0)
{
EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_mime.");
2010-02-04 01:12:35 -08:00
goto shutdown_efreet;
}
efreet_mime_endianess = efreet_mime_endian_check();
monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
efreet_mime_type_cache_clear();
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
if (!efreet_mime_init_files())
goto unregister_log_domain;
return _efreet_mime_init_count;
2010-02-04 01:12:35 -08:00
unregister_log_domain:
eina_log_domain_unregister(_efreet_mime_log_dom);
_efreet_mime_log_dom = -1;
2010-02-04 01:12:35 -08:00
shutdown_efreet:
efreet_shutdown();
2010-02-04 01:12:35 -08:00
shutdown_ecore_file:
ecore_file_shutdown();
2010-02-04 01:12:35 -08:00
shutdown_ecore:
ecore_shutdown();
return --_efreet_mime_init_count;
}
EAPI int
efreet_mime_shutdown(void)
{
if (_efreet_mime_init_count <= 0)
{
EINA_LOG_ERR("Init count not greater than 0 in shutdown.");
return 0;
}
if (--_efreet_mime_init_count != 0)
return _efreet_mime_init_count;
efreet_mime_icons_debug();
IF_RELEASE(_mime_inode_symlink);
IF_RELEASE(_mime_inode_fifo);
IF_RELEASE(_mime_inode_chardevice);
IF_RELEASE(_mime_inode_blockdevice);
IF_RELEASE(_mime_inode_socket);
IF_RELEASE(_mime_inode_mountpoint);
IF_RELEASE(_mime_inode_directory);
IF_RELEASE(_mime_application_x_executable);
IF_RELEASE(_mime_application_octet_stream);
IF_RELEASE(_mime_text_plain);
IF_FREE_LIST(globs, efreet_mime_glob_free);
IF_FREE_LIST(magics, efreet_mime_magic_free);
IF_FREE_HASH(monitors);
IF_FREE_HASH(wild);
IF_FREE_HASH(mime_icons);
eina_log_domain_unregister(_efreet_mime_log_dom);
_efreet_mime_log_dom = -1;
efreet_shutdown();
ecore_file_shutdown();
ecore_shutdown();
return _efreet_mime_init_count;
}
2007-11-04 01:32:35 -08:00
EAPI const char *
efreet_mime_type_get(const char *file)
{
const char *type = NULL;
EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
if ((type = efreet_mime_special_check(file)))
return type;
/* Check magics with priority > 80 */
if ((type = efreet_mime_magic_check_priority(file, 0, 80)))
return type;
/* Check globs */
if ((type = efreet_mime_globs_type_get(file)))
return type;
/* Check rest of magics */
if ((type = efreet_mime_magic_check_priority(file, 80, 0)))
return type;
return efreet_mime_fallback_check(file);
}
EAPI const char *
efreet_mime_type_icon_get(const char *mime, const char *theme, unsigned int size)
{
const char *icon = NULL;
char *data;
Eina_List *icons = NULL;
const char *env = NULL;
char *p = NULL, *pp = NULL, *ppp = NULL;
char buf[PATH_MAX];
const char *cache;
EINA_SAFETY_ON_NULL_RETURN_VAL(mime, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
mime = eina_stringshare_add(mime);
theme = eina_stringshare_add(theme);
cache = efreet_mime_icon_entry_find(mime, theme, size);
if (cache)
{
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
eina_stringshare_del(mime);
eina_stringshare_del(theme);
return cache;
}
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
/* Standard icon name */
p = strdup(mime);
pp = p;
while (*pp)
{
2008-08-02 15:09:04 -07:00
if (*pp == '/') *pp = '-';
pp++;
}
icons = eina_list_append(icons, p);
/* Environment Based icon names */
if ((env = efreet_desktop_environment_get()))
{
snprintf(buf, sizeof(buf), "%s-mime-%s", env, p);
icons = eina_list_append(icons, strdup(buf));
snprintf(buf, sizeof(buf), "%s-%s", env, p);
icons = eina_list_append(icons, strdup(buf));
}
/* Mime prefixed icon names */
snprintf(buf, sizeof(buf), "mime-%s", p);
icons = eina_list_append(icons, strdup(buf));
/* Generic icons */
pp = strdup(p);
while ((ppp = strrchr(pp, '-')))
{
*ppp = '\0';
snprintf(buf, sizeof(buf), "%s-x-generic", pp);
icons = eina_list_append(icons, strdup(buf));
snprintf(buf, sizeof(buf), "%s-generic", pp);
icons = eina_list_append(icons, strdup(buf));
snprintf(buf, sizeof(buf), "%s", pp);
icons = eina_list_append(icons, strdup(buf));
}
FREE(pp);
/* Search for icons using list */
icon = efreet_icon_list_find(theme, icons, size);
while (icons)
{
data = eina_list_data_get(icons);
free(data);
icons = eina_list_remove_list(icons, icons);
}
efreet_mime_icon_entry_add(mime, eina_stringshare_add(icon), theme, size);
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
return icon;
}
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
EAPI void
efreet_mime_type_cache_clear(void)
{
if (mime_icons)
{
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
eina_hash_free(mime_icons);
mime_icons_lru = NULL;
}
mime_icons = eina_hash_stringshared_new(EINA_FREE_CB(efreet_mime_icon_entry_head_free));
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
}
EAPI void
efreet_mime_type_cache_flush(void)
{
efreet_mime_icons_flush(ecore_loop_time_get());
efreet mime types icon cache. This cache is very simple and should work fine when system does not change, it keeps a direct association of mime-types and found icons, remembering theme and icon size. Search is very fast since it uses stringshared strings and thus direct pointer comparison in hash search. We could optimize it even more if we assumed stringshared strings to come in, so no need to eina_stringshare_add() (which is a hash per se), using just eina_stringshare_ref(). Cache population is limited to compile-time value and just values older than a given threshold are deleted. I do not keep a LRU explicit list, so you might have some old but unused items always alive. I don't find this too bad, sure it will consume more memory, but will not hurt performance. We can change this to purge all expired items by not checking for number of items to remove, removing all that match. Next I plan to find out a good way to cache and speed up file->mime discovery. I plan to do auto-generated state-machine to match extensions, so you don't need to check the same extension character more than once. Example: Input: bla.edc Extensions: edc edj eps png bmp It would first try to match against 'e', 'p' and 'b'. It will match 'e' and then check for 'd' (edc or edj) or 'p' (eps). It will match 'd' and then check for 'c' or 'j'. This will reduce number of comparisons considerably. As I'm running out of time (4am, not much time left on this month), I could use some help here. SVN revision: 39343
2009-03-02 23:20:21 -08:00
}
2007-11-04 01:32:35 -08:00
EAPI const char *
2007-08-12 22:33:26 -07:00
efreet_mime_magic_type_get(const char *file)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
return efreet_mime_magic_check_priority(file, 0, 0);
}
2007-11-04 01:32:35 -08:00
EAPI const char *
2007-08-12 22:33:26 -07:00
efreet_mime_globs_type_get(const char *file)
{
Eina_List *l;
Efreet_Mime_Glob *g;
char *sl, *p;
const char *s;
char *ext, *mime;
EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
/* Check in the extension hash for the type */
ext = strchr(file, '.');
if (ext)
{
2007-08-12 12:28:02 -07:00
sl = alloca(strlen(ext) + 1);
for (s = ext, p = sl; *s; s++, p++) *p = tolower(*s);
*p = 0;
p = sl;
while (p)
{
p++;
if (p && (mime = eina_hash_find(wild, p))) return mime;
p = strchr(p, '.');
}
}
/* Fallback to the other globs if not found */
EINA_LIST_FOREACH(globs, l, g)
{
if (efreet_mime_glob_match(file, g->glob))
return g->mime;
}
ext = alloca(strlen(file) + 1);
for (s = file, p = ext; *s; s++, p++) *p = tolower(*s);
*p = 0;
EINA_LIST_FOREACH(globs, l, g)
{
if (efreet_mime_glob_case_match(ext, g->glob))
return g->mime;
}
return NULL;
}
2007-11-04 01:32:35 -08:00
EAPI const char *
2007-08-12 22:33:26 -07:00
efreet_mime_special_type_get(const char *file)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
return efreet_mime_special_check(file);
}
2007-11-04 01:32:35 -08:00
EAPI const char *
2007-08-12 22:33:26 -07:00
efreet_mime_fallback_type_get(const char *file)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
return efreet_mime_fallback_check(file);
}
/**
* @internal
* @return Returns the endianess
* @brief Retreive the endianess of the machine
*/
2007-07-01 09:32:34 -07:00
static int
efreet_mime_endian_check(void)
{
int test = 1;
return (*((char*)(&test)));
}
/**
* @internal
2011-01-27 09:49:45 -08:00
* @param file File to monitor
* @return Returns no value.
* @brief Creates a new file monitor if we aren't already monitoring the
* given file
*/
static void
efreet_mime_monitor_add(const char *file)
{
Ecore_File_Monitor *fm = NULL;
/* if this is already in our hash then we're already monitoring so no
* reason to re-monitor */
if (eina_hash_find(monitors, file))
return;
if ((fm = ecore_file_monitor_add(file, efreet_mime_cb_update_file, NULL)))
{
2009-08-28 22:29:05 -07:00
eina_hash_del(monitors, file, NULL);
eina_hash_add(monitors, file, fm);
}
}
/**
* @internal
2011-01-27 09:49:45 -08:00
* @param datadirs List of XDG data dirs
* @param datahome Path to XDG data home directory
* @return Returns no value
* @brief Read all glob files in XDG data/home dirs.
* Also reads the /etc/mime.types file.
*/
static void
efreet_mime_load_globs(Eina_List *datadirs, const char *datahome)
{
Eina_List *l;
char buf[4096];
const char *datadir = NULL;
IF_FREE_HASH(wild);
wild = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
while (globs)
{
efreet_mime_glob_free(eina_list_data_get(globs));
globs = eina_list_remove_list(globs, globs);
}
/*
* This is here for legacy reasons. It is mentioned briefly
* in the spec and seems to still be quite valid. It is
* loaded first so the globs files will override anything
* in here.
*/
efreet_mime_mime_types_load("/etc/mime.types");
datadir = datahome;
snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
efreet_mime_shared_mimeinfo_globs_load(buf);
EINA_LIST_FOREACH(datadirs, l, datadir)
{
snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
efreet_mime_shared_mimeinfo_globs_load(buf);
}
}
/**
* @internal
2011-01-27 09:49:45 -08:00
* @param datadirs List of XDG data dirs
* @param datahome Path to XDG data home directory
* @return Returns no value
* @brief Read all magic files in XDG data/home dirs.
*/
static void
efreet_mime_load_magics(Eina_List *datadirs, const char *datahome)
{
Eina_List *l;
char buf[4096];
const char *datadir = NULL;
while (magics)
{
efreet_mime_magic_free(eina_list_data_get(magics));
magics = eina_list_remove_list(magics, magics);
}
datadir = datahome;
snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
efreet_mime_shared_mimeinfo_magic_load(buf);
EINA_LIST_FOREACH(datadirs, l, datadir)
{
snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
efreet_mime_shared_mimeinfo_magic_load(buf);
}
}
/**
* @internal
2011-01-27 09:49:45 -08:00
* @param data Data pointer passed to monitor_add
* @param monitor Ecore_File_Monitor associated with this event
* @param event The type of event
* @param path Path to the file that was updated
* @return Returns no value
* @brief Callback for all file monitors. Just reloads the appropriate
* list depending on which file changed. If it was a magic file
* only the magic list is updated. If it was a glob file or /etc/mime.types,
* the globs are updated.
*/
static void
efreet_mime_cb_update_file(void *data EINA_UNUSED,
Ecore_File_Monitor *monitor EINA_UNUSED,
Ecore_File_Event event EINA_UNUSED,
const char *path)
{
Eina_List *datadirs = NULL;
const char *datahome = NULL;
if (!(datahome = efreet_data_home_get()))
return;
if (!(datadirs = efreet_data_dirs_get()))
return;
if (strstr(path, "magic"))
efreet_mime_load_magics(datadirs, datahome);
else
efreet_mime_load_globs(datadirs, datahome);
}
/**
* @internal
2011-01-27 09:49:45 -08:00
* @param datadirs List of XDG data dirs
* @param datahome Path to XDG data home directory
* @return Returns 1 on success, 0 on failure
* @brief Initializes globs, magics, and monitors lists.
*/
static int
2007-07-01 09:32:34 -07:00
efreet_mime_init_files(void)
{
Eina_List *l;
Eina_List *datadirs = NULL;
char buf[PATH_MAX];
const char *datahome, *datadir = NULL;
if (!(datahome = efreet_data_home_get()))
return 0;
if (!(datadirs = efreet_data_dirs_get()))
return 0;
/*
* Add our file monitors
* We watch the directories so we can watch for new files
*/
datadir = datahome;
snprintf(buf, sizeof(buf), "%s/mime", datadir);
efreet_mime_monitor_add(buf);
EINA_LIST_FOREACH(datadirs, l, datadir)
{
snprintf(buf, sizeof(buf), "%s/mime", datadir);
efreet_mime_monitor_add(buf);
}
efreet_mime_monitor_add("/etc/mime.types");
/* Load our mime information */
efreet_mime_load_globs(datadirs, datahome);
efreet_mime_load_magics(datadirs, datahome);
_mime_inode_symlink = eina_stringshare_add("inode/symlink");
_mime_inode_fifo = eina_stringshare_add("inode/fifo");
_mime_inode_chardevice = eina_stringshare_add("inode/chardevice");
_mime_inode_blockdevice = eina_stringshare_add("inode/blockdevice");
_mime_inode_socket = eina_stringshare_add("inode/socket");
_mime_inode_mountpoint = eina_stringshare_add("inode/mountpoint");
_mime_inode_directory = eina_stringshare_add("inode/directory");
_mime_application_x_executable = eina_stringshare_add("application/x-executable");
_mime_application_octet_stream = eina_stringshare_add("application/octet-stream");
_mime_text_plain = eina_stringshare_add("text/plain");
return 1;
}
/**
* @internal
2011-01-27 09:49:45 -08:00
* @param file File to examine
* @return Returns mime type if special file, else NULL
* @brief Returns a mime type based on the stat of a file.
* This is used first to catch directories and other special
* files. A NULL return doesn't necessarily mean failure, but
* can also mean the file is regular.
* @note Mapping of file types to mime types:
* Stat Macro File Type Mime Type
2007-07-01 09:32:34 -07:00
* S_IFREG regular NULL
* S_IFIFO named pipe (fifo) inode/fifo
* S_IFCHR character special inode/chardevice
* S_IFDIR directory inode/directory
* S_IFBLK block special inode/blockdevice
* S_IFLNK symbolic link inode/symlink
* S_IFSOCK socket inode/socket
*
* This function can also return inode/mount-point.
* This is calculated by comparing the st_dev of the directory
* against that of it's parent directory. If they differ it
* is considered a mount point.
*/
2007-07-01 09:32:34 -07:00
static const char *
efreet_mime_special_check(const char *file)
{
struct stat s;
int path_len = 0;
/* no link on Windows < Vista */
#ifdef _WIN32
if (!stat(file, &s))
#else
if (!lstat(file, &s))
#endif
{
if (S_ISREG(s.st_mode))
return NULL;
#ifndef _WIN32
if (S_ISLNK(s.st_mode))
return _mime_inode_symlink;
#endif
if (S_ISFIFO(s.st_mode))
return _mime_inode_fifo;
if (S_ISCHR(s.st_mode))
return _mime_inode_chardevice;
if (S_ISBLK(s.st_mode))
return _mime_inode_blockdevice;
#ifndef _WIN32
if (S_ISSOCK(s.st_mode))
return _mime_inode_socket;
#endif
if (S_ISDIR(s.st_mode))
{
struct stat s2;
char parent[PATH_MAX];
char path[PATH_MAX];
strncpy(path, file, PATH_MAX);
path_len = strlen(file);
strncpy(parent, path, PATH_MAX);
/* Kill any trailing slash */
parent[--path_len] = '\0';
/* Truncate to last slash */
while (parent[--path_len] != '/') parent[path_len] = '\0';
#ifdef _WIN32
if (!stat(file, &s2))
#else
2012-10-18 01:09:54 -07:00
if (!lstat(parent, &s2))
#endif
2012-10-18 01:09:54 -07:00
{
if (s.st_dev != s2.st_dev)
return _mime_inode_mountpoint;
}
return _mime_inode_directory;
}
return NULL;
}
return NULL;
}
/**
* @internal
2011-01-27 09:49:45 -08:00
* @param file File to examine
2007-07-03 11:01:56 -07:00
* @return Returns mime type or NULL if the file doesn't exist
* @brief Returns text/plain if the file appears to contain text and
2007-07-03 11:01:56 -07:00
* returns application/octet-stream if it appears to be binary.
*/
static const char *
efreet_mime_fallback_check(const char *file)
{
FILE *f = NULL;
char buf[32];
int i;
if (ecore_file_can_exec(file))
return _mime_application_x_executable;
2010-08-29 01:47:27 -07:00
if (!(f = fopen(file, "r"))) return NULL;
i = fread(buf, 1, sizeof(buf), f);
fclose(f);
if (i == 0) return _mime_application_octet_stream;
2008-08-02 15:09:04 -07:00
/*
* Check for ASCII control characters in the first 32 bytes.
* Line Feeds, carriage returns, and tabs are ignored as they are
* quite common in text files in the first 32 chars.
*/
for (i -= 1; i >= 0; --i)
{
if ((buf[i] < 0x20) &&
(buf[i] != '\n') && /* Line Feed */
(buf[i] != '\r') && /* Carriage Return */
(buf[i] != '\t')) /* Tab */
return _mime_application_octet_stream;
}
return _mime_text_plain;
}
/**
* @internal
2011-01-27 09:49:45 -08:00
* @param glob Glob to search for
* @return Returns 1 on success, 0 on failure
* @brief Removes a glob from the list
*/
static int
efreet_mime_glob_remove(const char *glob)
{
Efreet_Mime_Glob *mime = NULL;
if ((mime = eina_list_search_unsorted(globs, EINA_COMPARE_CB(strcmp), glob)))
{
globs = eina_list_remove(globs, mime);
2009-08-28 22:29:05 -07:00
IF_RELEASE(mime->glob);
IF_RELEASE(mime->mime);
FREE(mime);
return 1;
}
return 0;
}