efl/legacy/efreet/src/lib/efreet_mime.c

1240 lines
32 KiB
C

/* vim: set sw=4 ts=4 sts=4 et: */
#include <Efreet.h>
#include <Ecore_Data.h>
#include <Efreet_Mime.h>
#include "efreet_private.h"
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/time.h>
static Ecore_List *globs = NULL; /* contains Efreet_Mime_Glob structs */
static Ecore_List *magics = NULL; /* contains Efreet_Mime_Magic structs */
static Ecore_Hash *wild = NULL; /* contains *.ext and mime.types globs*/
static Ecore_Hash *monitors = NULL; /* contains file monitors */
/**
* @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
* current set of magic rules. This setting is only here for the future.
*/
#define EFREET_MIME_MAGIC_BUFFER_SIZE 512
/**
* Efreet_Mime_Glob
* @brief A parsed representation of a globs file
*/
typedef struct Efreet_Mime_Glob Efreet_Mime_Glob;
struct Efreet_Mime_Glob
{
const char *glob;
const char *mime;
};
/**
* Efreet_Mime_Magic
* @brief A parsed representation of a magic file section
*/
typedef struct Efreet_Mime_Magic Efreet_Mime_Magic;
struct Efreet_Mime_Magic
{
unsigned int priority;
const char *mime;
Ecore_List *entries;
};
/**
* Efreet_Mime_Magic_Entry
* @brief A parsed representation of a magic file entry
*/
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;
};
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);
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);
/**
* @return Returns 1 on success or 0 on failure
* @brief Initializes the efreet mime settings
*/
int
efreet_mime_init(void)
{
if (!ecore_init())
return 0;
if (!ecore_file_init())
return 0;
if (!efreet_init())
return 0;
efreet_mime_endianess = efreet_mime_endian_check();
monitors = ecore_hash_new(ecore_str_hash, ecore_str_compare);
ecore_hash_free_key_cb_set(monitors, ECORE_FREE_CB(free));
ecore_hash_free_value_cb_set(monitors,
ECORE_FREE_CB(ecore_file_monitor_del));
if (!efreet_mime_init_files())
return 0;
return 1;
}
/**
* @return Returns no value
* @brief Cleans up the efreet mime settings system
*/
void
efreet_mime_shutdown(void)
{
IF_FREE_LIST(globs);
IF_FREE_LIST(magics);
IF_FREE_HASH(monitors);
IF_FREE_HASH(wild);
efreet_shutdown();
ecore_file_shutdown();
ecore_shutdown();
}
/**
* @param file: The file to find the mime type
* @return Returns mime type as a string
* @brief Retreive the mime type of a file
*/
const char *
efreet_mime_type_get(const char *file)
{
const char *type = 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);
}
/**
* @param file: The file to find the mime type icon
* @return Returns mime type icon path as a string
* @brief Retreive the mime type icon for a file
*/
char *
efreet_mime_type_icon_get(const char *mime, const char *theme, const char *size)
{
char *icon = NULL;
Ecore_List *icons = NULL;
const char *env = NULL;
char *p = NULL, *pp = NULL, *ppp = NULL;
char buf[PATH_MAX];
if (!mime || !theme || !size)
return NULL;
icons = ecore_list_new();
ecore_list_free_cb_set(icons, free);
/* Standard icon name */
p = strdup(mime);
pp = p;
while (*pp)
{
if (*pp == '/')
*pp = '-';
pp++;
}
ecore_list_append(icons, p);
/* Environment Based icon names */
if ((env = efreet_desktop_environment_get()))
{
snprintf(buf, sizeof(buf), "%s-mime-%s", env, p);
ecore_list_append(icons, strdup(buf));
snprintf(buf, sizeof(buf), "%s-%s", env, p);
ecore_list_append(icons, strdup(buf));
}
/* Mime prefixed icon names */
snprintf(buf, sizeof(buf), "mime-%s", p);
ecore_list_append(icons, strdup(buf));
/* Generic icons */
pp = strdup(p);
while ((ppp = strrchr(pp, '-')))
{
*ppp = '\0';
snprintf(buf, sizeof(buf), "%s-generic", pp);
ecore_list_append(icons, strdup(buf));
snprintf(buf, sizeof(buf), "%s", pp);
ecore_list_append(icons, strdup(buf));
}
FREE(pp);
/* Search for icons using list */
icon = efreet_icon_list_find(theme, icons, size);
ecore_list_destroy(icons);
return icon;
}
/**
* @param file: The file to check the mime type
* @return Returns mime type as a string
* @brief Retreive the mime type of a file using magic
*/
const char *
efreet_mime_magic_type_get(const char *file)
{
return efreet_mime_magic_check_priority(file, 0, 0);
}
/**
* @param file: The file to check the mime type
* @return Returns mime type as a string
* @brief Retreive the mime type of a file using globs
*/
const char *
efreet_mime_globs_type_get(const char *file)
{
Efreet_Mime_Glob *g;
char *sl, *p;
const char *s;
char *ext, *mime;
/*
* Check in the extension hash for the type
*/
ext = strchr(file, '.');
if (ext)
{
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 = ecore_hash_get(wild, p))) return mime;
p = strchr(p, '.');
}
}
/*
* Fallback to the other globs if not found
*/
ecore_list_first_goto(globs);
while ((g = ecore_list_next(globs)))
{
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;
ecore_list_first_goto(globs);
while ((g = ecore_list_next(globs)))
{
if (efreet_mime_glob_case_match(ext, g->glob))
return g->mime;
}
return NULL;
}
/**
* @param file: The file to check the mime type
* @param methods: The methods to use, see Efreet_Mime_Method structure
* @return Returns mime type as a string
* @brief Retreive the special mime type of a file
*/
const char *
efreet_mime_special_type_get(const char *file)
{
return efreet_mime_special_check(file);
}
/**
* @param file: The file to check the mime type
* @param methods: The methods to use, see Efreet_Mime_Method structure
* @return Returns mime type as a string
* @brief Retreive the fallback mime type of a file
*/
const char *
efreet_mime_fallback_type_get(const char *file)
{
return efreet_mime_fallback_check(file);
}
/**
* @internal
* @return Returns the endianess
* @brief Retreive the endianess of the machine
*/
static int
efreet_mime_endian_check(void)
{
int test = 1;
return (*((char*)(&test)));
}
/**
* @internal
* @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 (ecore_hash_get(monitors, file))
return;
if ((fm = ecore_file_monitor_add(file, efreet_mime_cb_update_file, NULL)))
ecore_hash_set(monitors, strdup(file), fm);
}
/**
* @internal
* @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(Ecore_List *datadirs, const char *datahome)
{
char buf[4096];
const char *datadir = NULL;
IF_FREE_HASH(wild);
wild = ecore_hash_new(ecore_str_hash, ecore_str_compare);
ecore_hash_free_key_cb_set(wild, ECORE_FREE_CB(ecore_string_release));
ecore_hash_free_value_cb_set(wild,
ECORE_FREE_CB(ecore_string_release));
IF_FREE_LIST(globs);
globs = ecore_list_new();
ecore_list_free_cb_set(globs, efreet_mime_glob_free);
/*
* 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;
ecore_list_first_goto(datadirs);
while (datadir)
{
snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
efreet_mime_shared_mimeinfo_globs_load(buf);
datadir = ecore_list_next(datadirs);
}
}
/**
* @internal
* @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(Ecore_List *datadirs, const char *datahome)
{
char buf[4096];
const char *datadir = NULL;
IF_FREE_LIST(magics);
magics = ecore_list_new();
ecore_list_free_cb_set(magics, efreet_mime_magic_free);
datadir = datahome;
ecore_list_first_goto(datadirs);
while (datadir)
{
snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
efreet_mime_shared_mimeinfo_magic_load(buf);
datadir = ecore_list_next(datadirs);
}
}
/**
* @internal
* @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 __UNUSED__,
Ecore_File_Monitor *monitor __UNUSED__,
Ecore_File_Event event __UNUSED__,
const char *path)
{
Ecore_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
* @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
efreet_mime_init_files(void)
{
Ecore_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;
ecore_list_first_goto(datadirs);
while (datadir)
{
snprintf(buf, PATH_MAX, "%s/mime", datadir);
efreet_mime_monitor_add(buf);
datadir = ecore_list_next(datadirs);
}
efreet_mime_monitor_add("/etc/mime.types");
/* Load our mime information */
efreet_mime_load_globs(datadirs, datahome);
efreet_mime_load_magics(datadirs, datahome);
return 1;
}
/**
* @internal
* @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
* 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.
*/
static const char *
efreet_mime_special_check(const char *file)
{
struct stat s;
int path_len = 0;
if (!lstat(file, &s))
{
if (S_ISREG(s.st_mode))
return NULL;
if (S_ISLNK(s.st_mode))
return "inode/symlink";
if (S_ISFIFO(s.st_mode))
return "inode/fifo";
if (S_ISCHR(s.st_mode))
return "inode/chardevice";
if (S_ISBLK(s.st_mode))
return "inode/blockdevice";
if (S_ISSOCK(s.st_mode))
return "inode/socket";
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';
if (!lstat(parent, &s2))
{
if (s.st_dev != s2.st_dev)
return "inode/mount-point";
}
return "inode/directory";
}
return NULL;
}
return NULL;
}
/**
* @internal
* @param file: File to examine
* @return Returns mime type or NULL if the file doesn't exist
* @brief Returns text/plain if the file appears to contain text and
* 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 (!(f = fopen(file, "r")))
return NULL;
i = fread(buf, 1, sizeof(buf), f);
fclose(f);
if (i == 0)
return "application/octet-stream";
/*
* 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 "application/octet-stream";
}
return "text/plain";
}
/**
* @internal
* @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;
mime = ecore_list_first_goto(globs);
while ((mime = ecore_list_current(globs)))
{
if (!strcmp(glob, mime->glob))
{
ecore_list_remove(globs);
IF_RELEASE(mime->glob);
IF_RELEASE(mime->mime);
FREE(mime);
return 1;
}
ecore_list_next(globs);
}
return 0;
}
/**
* @internal
* @param file: mime.types file to load
* @return Returns no value
* @brief Loads values from a mime.types style file
* into the globs list.
* @note Format:
* application/msaccess mdb
* application/msword doc dot
*/
static void
efreet_mime_mime_types_load(const char *file)
{
FILE *f = NULL;
char buf[4096], mimetype[4096];
char ext[4096], *p = NULL, *pp = NULL;
f = fopen(file, "rb");
if (!f) return;
while (fgets(buf, sizeof(buf), f))
{
p = buf;
while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
if (*p == '#') continue;
if ((*p == '\n') || (*p == 0)) continue;
pp = p;
while (!isspace(*p) && (*p != 0) && (*p != '\n')) p++;
if ((*p == '\n') || (*p == 0)) continue;
strncpy(mimetype, pp, (p - pp));
mimetype[p - pp] = 0;
do
{
while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
if ((*p == '\n') || (*p == 0)) break;
pp = p;
while (!isspace(*p) && (*p != 0) && (*p != '\n')) p++;
strncpy(ext, pp, (p - pp));
ext[p - pp] = 0;
ecore_hash_set(wild, (void*)ecore_string_instance(ext),
(void*)ecore_string_instance(mimetype));
}
while ((*p != '\n') && (*p != 0));
}
fclose(f);
}
/**
* @internal
* @param file: globs file to load
* @return Returns no value
* @brief Loads values from a mime.types style file
* into the globs list.
* @note Format:
* text/vnd.wap.wml:*.wml
* application/x-7z-compressed:*.7z
* application/vnd.corel-draw:*.cdr
* text/spreadsheet:*.sylk
*/
static void
efreet_mime_shared_mimeinfo_globs_load(const char *file)
{
FILE *f = NULL;
char buf[4096], mimetype[4096], ext[4096], *p, *pp;
Efreet_Mime_Glob *mime = NULL;
f = fopen(file, "rb");
if (!f) return;
while (fgets(buf, sizeof(buf), f))
{
p = buf;
while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
if (*p == '#') continue;
if ((*p == '\n') || (*p == 0)) continue;
pp = p;
while ((*p != ':') && (*p != 0) && (*p != '\n')) p++;
if ((*p == '\n') || (*p == 0)) continue;
strncpy(mimetype, pp, (p - pp));
mimetype[p - pp] = 0;
p++;
pp = ext;
while ((*p != 0) && (*p != '\n'))
{
*pp = *p;
pp++;
p++;
}
*pp = 0;
if (ext[0] == '*' && ext[1] == '.')
{
ecore_hash_set(wild, (void*)ecore_string_instance(&(ext[2])),
(void*)ecore_string_instance(mimetype));
}
else
{
mime = NEW(Efreet_Mime_Glob, 1);
if (mime)
{
mime->mime = ecore_string_instance(mimetype);
mime->glob = ecore_string_instance(ext);
if ((!mime->mime) || (!mime->glob))
{
IF_RELEASE(mime->mime);
IF_RELEASE(mime->glob);
FREE(mime);
}
else
{
efreet_mime_glob_remove(ext);
ecore_list_append(globs, mime);
}
}
}
}
fclose(f);
}
/**
* @internal
* @param in: Number to count the digits
* @return Returns number of digits
* @brief Calculates and returns the number of digits
* in a number.
*/
static int
efreet_mime_count_digits(int in)
{
int i = 1, j = in;
if (j < 10) return 1;
while ((j /= 10) > 0) ++i;
return i;
}
/**
* @internal
* @param file: File to parse
* @return Returns no value
* @brief Loads a magic file and adds information to magics list
*/
static void
efreet_mime_shared_mimeinfo_magic_load(const char *file)
{
int fd = -1, size;
char *data = (void *)-1;
if (!file) return;
size = ecore_file_size(file);
if (size <= 0) return;
fd = open(file, O_RDONLY);
if (fd == -1) return;
data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (data == (void *)-1)
{
close(fd);
return;
}
efreet_mime_shared_mimeinfo_magic_parse(data, size);
munmap(data, size);
close(fd);
}
/**
* @param data: The data from the file
* @return Returns no value
* @brief Parses a magic file
* @note Format:
*
* ----------------------------------------------------------------------
* | HEX | ASCII |
* ----------------------------------------------------------------------
* |4D 49 4D 45 2D 4D 61 67 69 63 00 0A 5B 39 30 3A | MIME-Magic..[90: |
* |61 70 70 6C 69 63 61 74 69 6F 6E 2F 64 6F 63 62 | application/docb |
* |6F 6F 6B 2B 78 6D 6C 5D 0A 3E 30 3D 00 05 3C 3F | ook+xml].>0=..<? |
* |78 6D 6C 0A 31 3E 30 3D 00 19 2D 2F 2F 4F 41 53 | xml.1>0=..-//OAS |
* |49 53 2F 2F 44 54 44 20 44 6F 63 42 6F 6F 6B 20 | IS//DTD DocBook |
* |58 4D 4C 2B 31 30 31 0A 31 3E 30 3D 00 17 2D 2F | XML+101.1>0=..-/ |
* ----------------------------------------------------------------------
*
* indent
* The nesting depth of the rule, corresponding to the number of '>'
* characters in the traditional file format.
* ">" start-offset
* The offset into the file to look for a match.
* "=" value
* Two bytes giving the (big-endian) length of the value, followed by the
* value itself.
* "&" mask
* The mask, which (if present) is exactly the same length as the value.
* "~" word-size
* On little-endian machines, the size of each group to byte-swap.
* "+" range-length
* The length of the region in the file to check.
*
* The indent, range-length, word-size and mask components are optional.
* If missing, indent defaults to 0, range-length to 1, the word-size to 1,
* and the mask to all 'one' bits. In our case, mask is null as it is
* quicker, uses less memory and will acheive the same exact effect.
*/
static void
efreet_mime_shared_mimeinfo_magic_parse(char *data, int size)
{
Efreet_Mime_Magic *mime = NULL;
Efreet_Mime_Magic_Entry *entry = NULL;
char *ptr;
ptr = data;
/* make sure we're a magic file */
if (!ptr || strncmp(ptr, "MIME-Magic\0\n", 12))
return;
ptr += 12;
for (; (ptr - data) < size; )
{
if (*ptr == '[')
{
char *val, buf[512];
mime = NEW(Efreet_Mime_Magic, 1);
mime->entries = ecore_list_new();
ecore_list_free_cb_set(mime->entries,
efreet_mime_magic_entry_free);
ecore_list_append(magics, mime);
val = ++ptr;
while ((*val != ':')) val++;
memcpy(&buf, ptr, val - ptr);
buf[val - ptr] = '\0';
mime->priority = atoi(buf);
ptr = ++val;
while ((*val != ']')) val++;
memcpy(&buf, ptr, val - ptr);
buf[val - ptr] = '\0';
mime->mime = ecore_string_instance(buf);
ptr = ++val;
while (*ptr != '\n') ptr++;
}
else
{
if (!mime) continue;
if (!entry)
{
if (!(entry = NEW(Efreet_Mime_Magic_Entry, 1)))
{
IF_FREE_LIST(magics);
return;
}
entry->indent = 0;
entry->offset = 0;
entry->value_len = 0;
entry->word_size = 1;
entry->range_len = 1;
entry->mask = NULL;
entry->value = NULL;
ptr++;
ecore_list_append(mime->entries, entry);
}
switch(*ptr)
{
case '>':
ptr ++;
entry->offset = atoi(ptr);
ptr += efreet_mime_count_digits(entry->offset);
break;
case '=':
ptr++;
entry->value_len = ntohs(*((short*)(ptr)));
ptr += 2;
entry->value = NEW(1, entry->value_len);
memcpy(entry->value, ptr, entry->value_len);
ptr += entry->value_len;
break;
case '&':
ptr++;
entry->mask = NEW(1, entry->value_len);
memcpy(entry->mask, ptr, entry->value_len);
ptr += entry->value_len;
break;
case '~':
ptr++;
entry->word_size = atoi(ptr);
if (((entry->word_size != 0)
&& (entry->word_size != 1)
&& (entry->word_size != 2)
&& (entry->word_size != 4))
|| (entry->value_len % entry->word_size))
{
/* Invalid, Destroy */
FREE(entry->value);
FREE(entry->mask);
FREE(entry);
while (*ptr != '\n') ptr++;
break;
}
if (efreet_mime_endianess == EFREET_ENDIAN_LITTLE)
{
int j;
for (j = 0; j < entry->value_len; j += entry->word_size)
{
if (entry->word_size == 2)
{
((short*)entry->value)[j] =
ntohs(((short*)entry->value)[j]);
if (entry->mask)
((short*)entry->mask)[j] =
ntohs(((short*)entry->mask)[j]);
}
else if (entry->word_size == 4)
{
((long*)entry->value)[j] =
ntohl(((long*)entry->value)[j]);
if (entry->mask)
((long*)entry->mask)[j] =
ntohl(((long*)entry->mask)[j]);
}
}
}
ptr += efreet_mime_count_digits(entry->word_size);
break;
case '+':
ptr++;
entry->range_len = atoi(ptr);
ptr += efreet_mime_count_digits(entry->range_len);
break;
case '\n':
ptr++;
entry = NULL;
break;
default:
if (isdigit(*ptr))
{
entry->indent = atoi(ptr);
ptr += efreet_mime_count_digits(entry->indent);
}
break;
}
}
}
if (entry)
{
IF_FREE(entry->value);
IF_FREE(entry->mask);
FREE(entry);
}
}
/**
* @internal
* @param file: File to check
* @param start: Start priority, if 0 start at beginning
* @param end: End priority, should be less then start
* unless start
* @return Returns mime type for file if found, NULL if not
* @brief Applies magic rules to a file given a start and end priority
*/
static const char *
efreet_mime_magic_check_priority(const char *file,
unsigned int start,
unsigned int end)
{
Efreet_Mime_Magic *m = NULL;
Efreet_Mime_Magic_Entry *e = NULL;
FILE *f = NULL;
unsigned int i = 0, offset = 0,level = 0, match = 0, bytes_read = 0;
const char *last_mime = NULL;
char c, v, buf[EFREET_MIME_MAGIC_BUFFER_SIZE];
f = fopen(file, "rb");
if (!f) return NULL;
if (!(m = ecore_list_first_goto(magics)))
{
fclose(f);
return NULL;
}
if ((bytes_read = fread(buf, 1, sizeof(buf), f)) == 0)
{
fclose(f);
return NULL;
}
while ((m = ecore_list_next(magics)))
{
if ((start != 0) && (m->priority > start))
continue;
if (m->priority < end)
break;
ecore_list_first_goto(m->entries);
while ((e = ecore_list_next(m->entries)))
{
if ((level < e->indent) && !match)
continue;
if ((level >= e->indent) && !match)
level = e->indent;
else if ((level > e->indent) && match)
{
fclose(f);
if (last_mime) return last_mime;
}
for (offset = e->offset; offset < e->offset + e->range_len; offset++)
{
if (((offset + e->value_len) > bytes_read) &&
(fseek(f, offset, SEEK_SET) == -1))
break;
match = 1;
for (i = 0; i < e->value_len; ++i)
{
if (offset + e->value_len > bytes_read)
c = fgetc(f);
else
c = buf[offset + i];
v = e->value[i];
if (e->mask) v &= e->mask[i];
if (!(c == v))
{
match = 0;
break;
}
}
if (match)
{
level += 1;
last_mime = m->mime;
break;
}
}
}
}
fclose(f);
return NULL;
}
/**
* @internal
* @param data: Data pointer that is being destroyed
* @return Returns no value
* @brief Callback for globs destroy
*/
static void
efreet_mime_glob_free(void *data)
{
Efreet_Mime_Glob *m = data;
IF_RELEASE(m->mime);
IF_RELEASE(m->glob);
IF_FREE(m);
}
/**
* @internal
* @param data: Data pointer that is being destroyed
* @return Returns no value
* @brief Callback for magics destroy
*/
static void
efreet_mime_magic_free(void *data)
{
Efreet_Mime_Magic *m = data;
IF_RELEASE(m->mime);
IF_FREE_LIST(m->entries);
IF_FREE(m);
}
/**
* @internal
* @param data: Data pointer that is being destroyed
* @return Returns no value
* @brief Callback for magic entry destroy
*/
static void
efreet_mime_magic_entry_free(void *data)
{
Efreet_Mime_Magic_Entry *e = data;
IF_FREE(e->mask);
IF_FREE(e->value);
IF_FREE(e);
}
/**
* @internal
* @param str: String (filename) to match
* @param glob: Glob to match str to
* @return Returns 1 on success, 0 on failure
* @brief Compares str to glob, case sensitive
*/
static int
efreet_mime_glob_match(const char *str, const char *glob)
{
if (!str || !glob) return 0;
if (glob[0] == 0)
{
if (str[0] == 0) return 1;
return 0;
}
if (!fnmatch(glob, str, 0)) return 1;
return 0;
}
/**
* @internal
* @param str: String (filename) to match
* @param glob: Glob to match str to
* @return Returns 1 on success, 0 on failure
* @brief Compares str to glob, case insensitive (expects str already in lower case)
*/
static int
efreet_mime_glob_case_match(char *str, const char *glob)
{
const char *p;
char *tglob, *tp;
if (!str || !glob) return 0;
if (glob[0] == 0)
{
if (str[0] == 0) return 1;
return 0;
}
tglob = alloca(strlen(glob) + 1);
for (tp = tglob, p = glob; *p; p++, tp++) *tp = tolower(*p);
*tp = 0;
if (!fnmatch(str, tglob, 0)) return 1;
return 0;
}