1 /* vim: set sw=4 ts=4 sts=4 et: */
3 #include <Efreet_Mime.h>
4 #include "efreet_private.h"
10 static Eina_List *globs = NULL; /* contains Efreet_Mime_Glob structs */
11 static Eina_List *magics = NULL; /* contains Efreet_Mime_Magic structs */
12 static Eina_Hash *wild = NULL; /* contains *.ext and mime.types globs*/
13 static Eina_Hash *monitors = NULL; /* contains file monitors */
14 static Eina_Hash *mime_icons = NULL; /* contains cache with mime->icons */
15 static Eina_Inlist *mime_icons_lru = NULL;
16 static unsigned int _init_count = 0;
20 * @brief Holds whether we are big/little endian
21 * @note This is set during efreet_mime_init based on
26 EFREET_ENDIAN_BIG = 0,
27 EFREET_ENDIAN_LITTLE = 1
28 } efreet_mime_endianess = EFREET_ENDIAN_BIG;
31 * Buffer sized used for magic checks. The default is good enough for the
32 * current set of magic rules. This setting is only here for the future.
34 #define EFREET_MIME_MAGIC_BUFFER_SIZE 512
37 * Minimum timeout in seconds between mime-icons cache flush.
39 #define EFREET_MIME_ICONS_FLUSH_TIMEOUT 60
42 * Timeout in seconds, when older mime-icons items are expired.
44 #define EFREET_MIME_ICONS_EXPIRE_TIMEOUT 600
47 * mime-icons maximum population.
49 #define EFREET_MIME_ICONS_MAX_POPULATION 512
52 * If defined, dump mime-icons statistics after flush.
54 //#define EFREET_MIME_ICONS_DEBUG
58 * @brief A parsed representation of a globs file
60 typedef struct Efreet_Mime_Glob Efreet_Mime_Glob;
61 struct Efreet_Mime_Glob
69 * @brief A parsed representation of a magic file section
71 typedef struct Efreet_Mime_Magic Efreet_Mime_Magic;
72 struct Efreet_Mime_Magic
74 unsigned int priority;
80 * Efreet_Mime_Magic_Entry
81 * @brief A parsed representation of a magic file entry
83 typedef struct Efreet_Mime_Magic_Entry Efreet_Mime_Magic_Entry;
84 struct Efreet_Mime_Magic_Entry
88 unsigned int word_size;
89 unsigned int range_len;
90 unsigned short value_len;
95 typedef struct Efreet_Mime_Icon_Entry_Head Efreet_Mime_Icon_Entry_Head;
96 struct Efreet_Mime_Icon_Entry_Head
98 EINA_INLIST; /* node of mime_icons_lru */
104 typedef struct Efreet_Mime_Icon_Entry Efreet_Mime_Icon_Entry;
105 struct Efreet_Mime_Icon_Entry
114 static int efreet_mime_glob_remove(const char *glob);
115 static void efreet_mime_mime_types_load(const char *file);
116 static void efreet_mime_shared_mimeinfo_globs_load(const char *file);
117 static void efreet_mime_shared_mimeinfo_magic_load(const char *file);
118 static void efreet_mime_shared_mimeinfo_magic_parse(char *data, int size);
119 static const char *efreet_mime_magic_check_priority(const char *file,
122 static int efreet_mime_init_files(void);
123 static const char *efreet_mime_special_check(const char *file);
124 static const char *efreet_mime_fallback_check(const char *file);
125 static void efreet_mime_glob_free(void *data);
126 static void efreet_mime_magic_free(void *data);
127 static void efreet_mime_magic_entry_free(void *data);
128 static int efreet_mime_glob_match(const char *str, const char *glob);
129 static int efreet_mime_glob_case_match(char *str, const char *glob);
130 static int efreet_mime_endian_check(void);
132 static void efreet_mime_monitor_add(const char *file);
133 static void efreet_mime_cb_update_file(void *data,
134 Ecore_File_Monitor *monitor,
135 Ecore_File_Event event,
138 static void efreet_mime_icons_flush(time_t now);
139 static void efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry);
140 static void efreet_mime_icon_entry_add(const char *mime,
144 static const char *efreet_mime_icon_entry_find(const char *mime,
147 static void efreet_mime_icons_debug(void);
150 * @return Returns 1 on success or 0 on failure
151 * @brief Initializes the efreet mime settings
154 efreet_mime_init(void)
163 if (!ecore_file_init())
169 efreet_mime_endianess = efreet_mime_endian_check();
171 monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
173 efreet_mime_type_cache_clear();
175 if (!efreet_mime_init_files())
182 * @return Returns no value
183 * @brief Cleans up the efreet mime settings system
186 efreet_mime_shutdown(void)
190 if (_init_count == 0)
196 efreet_mime_icons_debug();
198 IF_FREE_LIST(globs, efreet_mime_glob_free);
199 IF_FREE_LIST(magics, efreet_mime_magic_free);
200 IF_FREE_HASH(monitors);
202 IF_FREE_HASH(mime_icons);
205 ecore_file_shutdown();
210 * @param file: The file to find the mime type
211 * @return Returns mime type as a string
212 * @brief Retreive the mime type of a file
215 efreet_mime_type_get(const char *file)
217 const char *type = NULL;
219 if ((type = efreet_mime_special_check(file)))
222 /* Check magics with priority > 80 */
223 if ((type = efreet_mime_magic_check_priority(file, 0, 80)))
227 if ((type = efreet_mime_globs_type_get(file)))
230 /* Check rest of magics */
231 if ((type = efreet_mime_magic_check_priority(file, 80, 0)))
234 return efreet_mime_fallback_check(file);
238 * @param mime: The name of the mime type
239 * @param theme: The name of the theme to search icons in
240 * @param size: The wanted size of the icon
241 * @return Returns mime type icon path as a string
242 * @brief Retreive the mime type icon for a file
245 efreet_mime_type_icon_get(const char *mime, const char *theme, unsigned int size)
249 Eina_List *icons = NULL;
250 const char *env = NULL;
251 char *p = NULL, *pp = NULL, *ppp = NULL;
255 if (!mime || !theme || !size)
258 mime = eina_stringshare_add(mime);
259 theme = eina_stringshare_add(theme);
260 cache = efreet_mime_icon_entry_find(mime, theme, size);
263 eina_stringshare_del(mime);
264 eina_stringshare_del(theme);
265 return strdup(cache);
268 /* Standard icon name */
273 if (*pp == '/') *pp = '-';
276 icons = eina_list_append(icons, p);
278 /* Environment Based icon names */
279 if ((env = efreet_desktop_environment_get()))
281 snprintf(buf, sizeof(buf), "%s-mime-%s", env, p);
282 icons = eina_list_append(icons, strdup(buf));
284 snprintf(buf, sizeof(buf), "%s-%s", env, p);
285 icons = eina_list_append(icons, strdup(buf));
288 /* Mime prefixed icon names */
289 snprintf(buf, sizeof(buf), "mime-%s", p);
290 icons = eina_list_append(icons, strdup(buf));
294 while ((ppp = strrchr(pp, '-')))
298 snprintf(buf, sizeof(buf), "%s-generic", pp);
299 icons = eina_list_append(icons, strdup(buf));
301 snprintf(buf, sizeof(buf), "%s", pp);
302 icons = eina_list_append(icons, strdup(buf));
306 /* Search for icons using list */
307 icon = efreet_icon_list_find(theme, icons, size);
310 data = eina_list_data_get(icons);
312 icons = eina_list_remove_list(icons, icons);
315 efreet_mime_icon_entry_add(mime, eina_stringshare_add(icon), theme, size);
321 efreet_mime_type_cache_clear(void)
325 eina_hash_free(mime_icons);
326 mime_icons_lru = NULL;
328 mime_icons = eina_hash_pointer_new(EINA_FREE_CB(efreet_mime_icon_entry_head_free));
332 efreet_mime_type_cache_flush(void)
334 efreet_mime_icons_flush((time_t)ecore_loop_time_get());
339 * @param file: The file to check the mime type
340 * @return Returns mime type as a string
341 * @brief Retreive the mime type of a file using magic
344 efreet_mime_magic_type_get(const char *file)
346 return efreet_mime_magic_check_priority(file, 0, 0);
350 * @param file: The file to check the mime type
351 * @return Returns mime type as a string
352 * @brief Retreive the mime type of a file using globs
355 efreet_mime_globs_type_get(const char *file)
363 /* Check in the extension hash for the type */
364 ext = strchr(file, '.');
367 sl = alloca(strlen(ext) + 1);
368 for (s = ext, p = sl; *s; s++, p++) *p = tolower(*s);
374 if (p && (mime = eina_hash_find(wild, p))) return mime;
379 /* Fallback to the other globs if not found */
380 EINA_LIST_FOREACH(globs, l, g)
382 if (efreet_mime_glob_match(file, g->glob))
386 ext = alloca(strlen(file) + 1);
387 for (s = file, p = ext; *s; s++, p++) *p = tolower(*s);
389 EINA_LIST_FOREACH(globs, l, g)
391 if (efreet_mime_glob_case_match(ext, g->glob))
398 * @param file: The file to check the mime type
399 * @return Returns mime type as a string
400 * @brief Retreive the special mime type of a file
403 efreet_mime_special_type_get(const char *file)
405 return efreet_mime_special_check(file);
409 * @param file: The file to check the mime type
410 * @return Returns mime type as a string
411 * @brief Retreive the fallback mime type of a file
414 efreet_mime_fallback_type_get(const char *file)
416 return efreet_mime_fallback_check(file);
421 * @return Returns the endianess
422 * @brief Retreive the endianess of the machine
425 efreet_mime_endian_check(void)
428 return (*((char*)(&test)));
433 * @param file: File to monitor
434 * @return Returns no value.
435 * @brief Creates a new file monitor if we aren't already monitoring the
439 efreet_mime_monitor_add(const char *file)
441 Ecore_File_Monitor *fm = NULL;
443 /* if this is already in our hash then we're already monitoring so no
444 * reason to re-monitor */
445 if (eina_hash_find(monitors, file))
448 if ((fm = ecore_file_monitor_add(file, efreet_mime_cb_update_file, NULL)))
450 eina_hash_del(monitors, file, NULL);
451 eina_hash_add(monitors, file, fm);
457 * @param datadirs: List of XDG data dirs
458 * @param datahome: Path to XDG data home directory
459 * @return Returns no value
460 * @brief Read all glob files in XDG data/home dirs.
461 * Also reads the /etc/mime.types file.
464 efreet_mime_load_globs(Eina_List *datadirs, const char *datahome)
468 const char *datadir = NULL;
471 wild = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
474 efreet_mime_glob_free(eina_list_data_get(globs));
475 globs = eina_list_remove_list(globs, globs);
479 * This is here for legacy reasons. It is mentioned briefly
480 * in the spec and seems to still be quite valid. It is
481 * loaded first so the globs files will override anything
484 efreet_mime_mime_types_load("/etc/mime.types");
487 snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
488 efreet_mime_shared_mimeinfo_globs_load(buf);
490 EINA_LIST_FOREACH(datadirs, l, datadir)
492 snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
493 efreet_mime_shared_mimeinfo_globs_load(buf);
499 * @param datadirs: List of XDG data dirs
500 * @param datahome: Path to XDG data home directory
501 * @return Returns no value
502 * @brief Read all magic files in XDG data/home dirs.
505 efreet_mime_load_magics(Eina_List *datadirs, const char *datahome)
509 const char *datadir = NULL;
513 efreet_mime_magic_free(eina_list_data_get(magics));
514 magics = eina_list_remove_list(magics, magics);
518 snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
519 efreet_mime_shared_mimeinfo_magic_load(buf);
521 EINA_LIST_FOREACH(datadirs, l, datadir)
523 snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
524 efreet_mime_shared_mimeinfo_magic_load(buf);
530 * @param data: Data pointer passed to monitor_add
531 * @param monitor: Ecore_File_Monitor associated with this event
532 * @param event: The type of event
533 * @param path: Path to the file that was updated
534 * @return Returns no value
535 * @brief Callback for all file monitors. Just reloads the appropriate
536 * list depending on which file changed. If it was a magic file
537 * only the magic list is updated. If it was a glob file or /etc/mime.types,
538 * the globs are updated.
541 efreet_mime_cb_update_file(void *data __UNUSED__,
542 Ecore_File_Monitor *monitor __UNUSED__,
543 Ecore_File_Event event __UNUSED__,
546 Eina_List *datadirs = NULL;
547 const char *datahome = NULL;
549 if (!(datahome = efreet_data_home_get()))
552 if (!(datadirs = efreet_data_dirs_get()))
555 if (strstr(path, "magic"))
556 efreet_mime_load_magics(datadirs, datahome);
558 efreet_mime_load_globs(datadirs, datahome);
563 * @param datadirs: List of XDG data dirs
564 * @param datahome: Path to XDG data home directory
565 * @return Returns 1 on success, 0 on failure
566 * @brief Initializes globs, magics, and monitors lists.
569 efreet_mime_init_files(void)
572 Eina_List *datadirs = NULL;
574 const char *datahome, *datadir = NULL;
576 if (!(datahome = efreet_data_home_get()))
579 if (!(datadirs = efreet_data_dirs_get()))
583 * Add our file monitors
584 * We watch the directories so we can watch for new files
587 snprintf(buf, PATH_MAX, "%s/mime", datadir);
588 efreet_mime_monitor_add(buf);
590 EINA_LIST_FOREACH(datadirs, l, datadir)
592 snprintf(buf, PATH_MAX, "%s/mime", datadir);
593 efreet_mime_monitor_add(buf);
595 efreet_mime_monitor_add("/etc/mime.types");
597 /* Load our mime information */
598 efreet_mime_load_globs(datadirs, datahome);
599 efreet_mime_load_magics(datadirs, datahome);
606 * @param file: File to examine
607 * @return Returns mime type if special file, else NULL
608 * @brief Returns a mime type based on the stat of a file.
609 * This is used first to catch directories and other special
610 * files. A NULL return doesn't necessarily mean failure, but
611 * can also mean the file is regular.
612 * @note Mapping of file types to mime types:
613 * Stat Macro File Type Mime Type
614 * S_IFREG regular NULL
615 * S_IFIFO named pipe (fifo) inode/fifo
616 * S_IFCHR character special inode/chardevice
617 * S_IFDIR directory inode/directory
618 * S_IFBLK block special inode/blockdevice
619 * S_IFLNK symbolic link inode/symlink
620 * S_IFSOCK socket inode/socket
622 * This function can also return inode/mount-point.
623 * This is calculated by comparing the st_dev of the directory
624 * against that of it's parent directory. If they differ it
625 * is considered a mount point.
628 efreet_mime_special_check(const char *file)
633 if (!lstat(file, &s))
635 if (S_ISREG(s.st_mode))
638 if (S_ISLNK(s.st_mode))
639 return "inode/symlink";
641 if (S_ISFIFO(s.st_mode))
644 if (S_ISCHR(s.st_mode))
645 return "inode/chardevice";
647 if (S_ISBLK(s.st_mode))
648 return "inode/blockdevice";
650 if (S_ISSOCK(s.st_mode))
651 return "inode/socket";
653 if (S_ISDIR(s.st_mode))
656 char parent[PATH_MAX];
659 strncpy(path, file, PATH_MAX);
661 path_len = strlen(file);
662 strncpy(parent, path, PATH_MAX);
664 /* Kill any trailing slash */
665 parent[--path_len] = '\0';
667 /* Truncate to last slash */
668 while (parent[--path_len] != '/') parent[path_len] = '\0';
670 if (!lstat(parent, &s2))
672 if (s.st_dev != s2.st_dev)
673 return "inode/mount-point";
676 return "inode/directory";
687 * @param file: File to examine
688 * @return Returns mime type or NULL if the file doesn't exist
689 * @brief Returns text/plain if the file appears to contain text and
690 * returns application/octet-stream if it appears to be binary.
693 efreet_mime_fallback_check(const char *file)
699 if (!(f = fopen(file, "r"))) return NULL;
701 i = fread(buf, 1, sizeof(buf), f);
704 if (i == 0) return "application/octet-stream";
707 * Check for ASCII control characters in the first 32 bytes.
708 * Line Feeds, carriage returns, and tabs are ignored as they are
709 * quite common in text files in the first 32 chars.
711 for (i -= 1; i >= 0; --i)
713 if ((buf[i] < 0x20) &&
714 (buf[i] != '\n') && /* Line Feed */
715 (buf[i] != '\r') && /* Carriage Return */
716 (buf[i] != '\t')) /* Tab */
717 return "application/octet-stream";
725 * @param glob: Glob to search for
726 * @return Returns 1 on success, 0 on failure
727 * @brief Removes a glob from the list
730 efreet_mime_glob_remove(const char *glob)
732 Efreet_Mime_Glob *mime = NULL;
734 if ((mime = eina_list_search_unsorted(globs, EINA_COMPARE_CB(strcmp), glob)))
736 globs = eina_list_remove(globs, mime);
737 IF_RELEASE(mime->glob);
738 IF_RELEASE(mime->mime);
748 * @param file: mime.types file to load
749 * @return Returns no value
750 * @brief Loads values from a mime.types style file
751 * into the globs list.
753 * application/msaccess mdb
754 * application/msword doc dot
757 efreet_mime_mime_types_load(const char *file)
760 char buf[4096], mimetype[4096];
761 char ext[4096], *p = NULL, *pp = NULL;
763 f = fopen(file, "rb");
765 while (fgets(buf, sizeof(buf), f))
768 while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
770 if (*p == '#') continue;
771 if ((*p == '\n') || (*p == 0)) continue;
774 while (!isspace(*p) && (*p != 0) && (*p != '\n')) p++;
776 if ((*p == '\n') || (*p == 0)) continue;
777 strncpy(mimetype, pp, (p - pp));
778 mimetype[p - pp] = 0;
782 while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
784 if ((*p == '\n') || (*p == 0)) break;
787 while (!isspace(*p) && (*p != 0) && (*p != '\n')) p++;
789 strncpy(ext, pp, (p - pp));
792 eina_hash_del(wild, ext, NULL);
793 eina_hash_add(wild, ext, (void*)eina_stringshare_add(mimetype));
795 while ((*p != '\n') && (*p != 0));
802 * @param file: globs file to load
803 * @return Returns no value
804 * @brief Loads values from a mime.types style file
805 * into the globs list.
807 * text/vnd.wap.wml:*.wml
808 * application/x-7z-compressed:*.7z
809 * application/vnd.corel-draw:*.cdr
810 * text/spreadsheet:*.sylk
813 efreet_mime_shared_mimeinfo_globs_load(const char *file)
816 char buf[4096], mimetype[4096], ext[4096], *p, *pp;
817 Efreet_Mime_Glob *mime = NULL;
819 f = fopen(file, "rb");
822 while (fgets(buf, sizeof(buf), f))
825 while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
827 if (*p == '#') continue;
828 if ((*p == '\n') || (*p == 0)) continue;
831 while ((*p != ':') && (*p != 0) && (*p != '\n')) p++;
833 if ((*p == '\n') || (*p == 0)) continue;
834 strncpy(mimetype, pp, (p - pp));
835 mimetype[p - pp] = 0;
839 while ((*p != 0) && (*p != '\n'))
848 if (ext[0] == '*' && ext[1] == '.')
850 eina_hash_del(wild, &(ext[2]), NULL);
851 eina_hash_add(wild, &(ext[2]),
852 (void*)eina_stringshare_add(mimetype));
856 mime = NEW(Efreet_Mime_Glob, 1);
859 mime->mime = eina_stringshare_add(mimetype);
860 mime->glob = eina_stringshare_add(ext);
861 if ((!mime->mime) || (!mime->glob))
863 IF_RELEASE(mime->mime);
864 IF_RELEASE(mime->glob);
869 efreet_mime_glob_remove(ext);
870 globs = eina_list_append(globs, mime);
881 * @param in: Number to count the digits
882 * @return Returns number of digits
883 * @brief Calculates and returns the number of digits
887 efreet_mime_count_digits(int in)
891 if (j < 10) return 1;
892 while ((j /= 10) > 0) ++i;
899 * @param file: File to parse
900 * @return Returns no value
901 * @brief Loads a magic file and adds information to magics list
904 efreet_mime_shared_mimeinfo_magic_load(const char *file)
907 char *data = (void *)-1;
911 size = ecore_file_size(file);
912 if (size <= 0) return;
914 fd = open(file, O_RDONLY);
915 if (fd == -1) return;
917 data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
918 if (data == MAP_FAILED)
924 efreet_mime_shared_mimeinfo_magic_parse(data, size);
931 * @param data: The data from the file
932 * @return Returns no value
933 * @brief Parses a magic file
936 * ----------------------------------------------------------------------
938 * ----------------------------------------------------------------------
939 * |4D 49 4D 45 2D 4D 61 67 69 63 00 0A 5B 39 30 3A | MIME-Magic..[90: |
940 * |61 70 70 6C 69 63 61 74 69 6F 6E 2F 64 6F 63 62 | application/docb |
941 * |6F 6F 6B 2B 78 6D 6C 5D 0A 3E 30 3D 00 05 3C 3F | ook+xml].>0=..<? |
942 * |78 6D 6C 0A 31 3E 30 3D 00 19 2D 2F 2F 4F 41 53 | xml.1>0=..-//OAS |
943 * |49 53 2F 2F 44 54 44 20 44 6F 63 42 6F 6F 6B 20 | IS//DTD DocBook |
944 * |58 4D 4C 2B 31 30 31 0A 31 3E 30 3D 00 17 2D 2F | XML+101.1>0=..-/ |
945 * ----------------------------------------------------------------------
948 * The nesting depth of the rule, corresponding to the number of '>'
949 * characters in the traditional file format.
951 * The offset into the file to look for a match.
953 * Two bytes giving the (big-endian) length of the value, followed by the
956 * The mask, which (if present) is exactly the same length as the value.
958 * On little-endian machines, the size of each group to byte-swap.
960 * The length of the region in the file to check.
962 * The indent, range-length, word-size and mask components are optional.
963 * If missing, indent defaults to 0, range-length to 1, the word-size to 1,
964 * and the mask to all 'one' bits. In our case, mask is null as it is
965 * quicker, uses less memory and will acheive the same exact effect.
968 efreet_mime_shared_mimeinfo_magic_parse(char *data, int size)
970 Efreet_Mime_Magic *mime = NULL;
971 Efreet_Mime_Magic_Entry *entry = NULL;
976 /* make sure we're a magic file */
977 if (!ptr || (size < 12) || strncmp(ptr, "MIME-Magic\0\n", 12))
982 for (; (ptr - data) < size; )
988 mime = NEW(Efreet_Mime_Magic, 1);
989 magics = eina_list_append(magics, mime);
992 while ((*val != ':')) val++;
993 memcpy(&buf, ptr, val - ptr);
994 buf[val - ptr] = '\0';
996 mime->priority = atoi(buf);
999 while ((*val != ']')) val++;
1000 memcpy(&buf, ptr, val - ptr);
1001 buf[val - ptr] = '\0';
1003 mime->mime = eina_stringshare_add(buf);
1006 while (*ptr != '\n') ptr++;
1012 if (!mime) continue;
1015 if (!(entry = NEW(Efreet_Mime_Magic_Entry, 1)))
1017 IF_FREE_LIST(magics, efreet_mime_magic_free);
1023 entry->value_len = 0;
1024 entry->word_size = 1;
1025 entry->range_len = 1;
1027 entry->value = NULL;
1030 mime->entries = eina_list_append(mime->entries, entry);
1037 entry->offset = atoi(ptr);
1038 ptr += efreet_mime_count_digits(entry->offset);
1044 memcpy(&tshort, ptr, sizeof(short));
1045 entry->value_len = ntohs(tshort);
1048 entry->value = NEW(1, entry->value_len);
1049 memcpy(entry->value, ptr, entry->value_len);
1050 ptr += entry->value_len;
1055 entry->mask = NEW(1, entry->value_len);
1056 memcpy(entry->mask, ptr, entry->value_len);
1057 ptr += entry->value_len;
1062 entry->word_size = atoi(ptr);
1063 if (((entry->word_size != 0)
1064 && (entry->word_size != 1)
1065 && (entry->word_size != 2)
1066 && (entry->word_size != 4))
1067 || (entry->value_len % entry->word_size))
1069 /* Invalid, Destroy */
1074 while (*ptr != '\n') ptr++;
1078 if (efreet_mime_endianess == EFREET_ENDIAN_LITTLE)
1082 for (j = 0; j < entry->value_len; j += entry->word_size)
1084 if (entry->word_size == 2)
1086 ((short*)entry->value)[j] =
1087 ntohs(((short*)entry->value)[j]);
1090 ((short*)entry->mask)[j] =
1091 ntohs(((short*)entry->mask)[j]);
1093 else if (entry->word_size == 4)
1095 ((int*)entry->value)[j] =
1096 ntohl(((int*)entry->value)[j]);
1099 ((int*)entry->mask)[j] =
1100 ntohl(((int*)entry->mask)[j]);
1105 ptr += efreet_mime_count_digits(entry->word_size);
1110 entry->range_len = atoi(ptr);
1111 ptr += efreet_mime_count_digits(entry->range_len);
1122 entry->indent = atoi(ptr);
1123 ptr += efreet_mime_count_digits(entry->indent);
1132 IF_FREE(entry->value);
1133 IF_FREE(entry->mask);
1141 * @param file: File to check
1142 * @param start: Start priority, if 0 start at beginning
1143 * @param end: End priority, should be less then start
1145 * @return Returns mime type for file if found, NULL if not
1146 * @brief Applies magic rules to a file given a start and end priority
1149 efreet_mime_magic_check_priority(const char *file,
1153 Efreet_Mime_Magic *m = NULL;
1154 Efreet_Mime_Magic_Entry *e = NULL;
1157 unsigned int i = 0, offset = 0,level = 0, match = 0, bytes_read = 0;
1158 const char *last_mime = NULL;
1159 char c, v, buf[EFREET_MIME_MAGIC_BUFFER_SIZE];
1161 f = fopen(file, "rb");
1162 if (!f) return NULL;
1170 if ((bytes_read = fread(buf, 1, sizeof(buf), f)) == 0)
1176 EINA_LIST_FOREACH(magics, l, m)
1178 if ((start != 0) && (m->priority > start))
1181 if (m->priority < end)
1184 EINA_LIST_FOREACH(m->entries, ll, e)
1186 if ((level < e->indent) && !match)
1189 if ((level >= e->indent) && !match)
1192 else if ((level > e->indent) && match)
1195 if (last_mime) return last_mime;
1198 for (offset = e->offset; offset < e->offset + e->range_len; offset++)
1200 if (((offset + e->value_len) > bytes_read) &&
1201 (fseek(f, offset, SEEK_SET) == -1))
1205 for (i = 0; i < e->value_len; ++i)
1207 if (offset + e->value_len > bytes_read)
1210 c = buf[offset + i];
1213 if (e->mask) v &= e->mask[i];
1225 last_mime = m->mime;
1238 * @param data: Data pointer that is being destroyed
1239 * @return Returns no value
1240 * @brief Callback for globs destroy
1243 efreet_mime_glob_free(void *data)
1245 Efreet_Mime_Glob *m = data;
1247 IF_RELEASE(m->mime);
1248 IF_RELEASE(m->glob);
1254 * @param data: Data pointer that is being destroyed
1255 * @return Returns no value
1256 * @brief Callback for magics destroy
1259 efreet_mime_magic_free(void *data)
1261 Efreet_Mime_Magic *m = data;
1262 Efreet_Mime_Magic_Entry *entry = NULL;
1264 IF_RELEASE(m->mime);
1267 entry = eina_list_data_get(m->entries);
1268 efreet_mime_magic_entry_free(entry);
1269 m->entries = eina_list_remove_list(m->entries, m->entries);
1276 * @param data: Data pointer that is being destroyed
1277 * @return Returns no value
1278 * @brief Callback for magic entry destroy
1281 efreet_mime_magic_entry_free(void *data)
1283 Efreet_Mime_Magic_Entry *e = data;
1293 * @param str: String (filename) to match
1294 * @param glob: Glob to match str to
1295 * @return Returns 1 on success, 0 on failure
1296 * @brief Compares str to glob, case sensitive
1299 efreet_mime_glob_match(const char *str, const char *glob)
1301 if (!str || !glob) return 0;
1304 if (str[0] == 0) return 1;
1307 if (!fnmatch(glob, str, 0)) return 1;
1313 * @param str: String (filename) to match
1314 * @param glob: Glob to match str to
1315 * @return Returns 1 on success, 0 on failure
1316 * @brief Compares str to glob, case insensitive (expects str already in lower case)
1319 efreet_mime_glob_case_match(char *str, const char *glob)
1324 if (!str || !glob) return 0;
1327 if (str[0] == 0) return 1;
1330 tglob = alloca(strlen(glob) + 1);
1331 for (tp = tglob, p = glob; *p; p++, tp++) *tp = tolower(*p);
1333 if (!fnmatch(str, tglob, 0)) return 1;
1338 efreet_mime_icons_flush(time_t now)
1341 static time_t old = 0;
1344 if (now - old < EFREET_MIME_ICONS_FLUSH_TIMEOUT)
1348 todo = eina_hash_population(mime_icons) - EFREET_MIME_ICONS_MAX_POPULATION;
1352 l = mime_icons_lru->last; /* mime_icons_lru is not NULL, since todo > 0 */
1353 for (; todo > 0; todo--)
1355 Efreet_Mime_Icon_Entry_Head *entry = (Efreet_Mime_Icon_Entry_Head *)l;
1356 Eina_Inlist *prev = l->prev;
1358 mime_icons_lru = eina_inlist_remove(mime_icons_lru, l);
1359 eina_hash_del_by_key(mime_icons, entry->mime);
1363 efreet_mime_icons_debug();
1367 efreet_mime_icon_entry_free(Efreet_Mime_Icon_Entry *node)
1369 eina_stringshare_del(node->icon);
1370 eina_stringshare_del(node->theme);
1375 efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry)
1379 Efreet_Mime_Icon_Entry *n = (Efreet_Mime_Icon_Entry *)entry->list;
1380 entry->list = eina_inlist_remove(entry->list, entry->list);
1381 efreet_mime_icon_entry_free(n);
1384 eina_stringshare_del(entry->mime);
1388 static Efreet_Mime_Icon_Entry *
1389 efreet_mime_icon_entry_new(const char *icon,
1393 Efreet_Mime_Icon_Entry *entry;
1395 entry = malloc(sizeof(*entry));
1400 entry->theme = theme;
1407 efreet_mime_icon_entry_add(const char *mime,
1412 Efreet_Mime_Icon_Entry_Head *entry;
1413 Efreet_Mime_Icon_Entry *n;
1415 n = efreet_mime_icon_entry_new(icon, theme, size);
1418 entry = eina_hash_find(mime_icons, mime);
1424 l = EINA_INLIST_GET(n);
1425 entry->list = eina_inlist_prepend(entry->list, l);
1427 l = EINA_INLIST_GET(entry);
1428 mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
1434 entry = malloc(sizeof(*entry));
1437 efreet_mime_icon_entry_free(n);
1441 l = EINA_INLIST_GET(n);
1442 entry->list = eina_inlist_prepend(NULL, l);
1444 eina_hash_direct_add(mime_icons, mime, entry);
1446 l = EINA_INLIST_GET(entry);
1447 mime_icons_lru = eina_inlist_prepend(mime_icons_lru, l);
1450 entry->timestamp = (time_t)ecore_loop_time_get();
1451 efreet_mime_icons_flush(entry->timestamp);
1455 efreet_mime_icon_entry_find(const char *mime,
1459 Efreet_Mime_Icon_Entry_Head *entry;
1460 Efreet_Mime_Icon_Entry *n;
1462 entry = eina_hash_find(mime_icons, mime);
1466 EINA_INLIST_FOREACH(entry->list, n)
1468 if ((n->theme == theme) && (n->size == size))
1472 l = EINA_INLIST_GET(n);
1473 if (entry->list != l)
1474 entry->list = eina_inlist_promote(entry->list, l);
1476 l = EINA_INLIST_GET(entry);
1477 if (mime_icons_lru != l)
1478 mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
1480 entry->timestamp = (time_t)ecore_loop_time_get();
1488 #ifdef EFREET_MIME_ICONS_DEBUG
1490 efreet_mime_icons_debug(void)
1492 time_t now = (time_t)ecore_loop_time_get();
1493 Efreet_Mime_Icon_Entry_Head *entry;
1494 EINA_INLIST_FOREACH(mime_icons_lru, entry)
1496 Efreet_Mime_Icon_Entry *n;
1499 (now - entry->timestamp >= EFREET_MIME_ICONS_EXPIRE_TIMEOUT))
1501 puts("*** FOLLOWING ENTRIES ARE AGED AND CAN BE EXPIRED ***");
1505 printf("mime-icon entry: '%s' last used: %s",
1506 entry->mime, ctime(&entry->timestamp));
1508 EINA_INLIST_FOREACH(entry->list, n)
1509 printf("\tsize: %3u theme: '%s' icon: '%s'\n",
1510 n->theme, n->size, n->icon);
1515 efreet_mime_icons_debug(void)