1 /* vim: set sw=4 ts=4 sts=4 et: */
10 #include <sys/types.h>
20 # include <winsock2.h>
24 # include <arpa/inet.h>
29 #elif defined __GNUC__
30 # define alloca __builtin_alloca
32 # define alloca __alloca
33 #elif defined _MSC_VER
35 # define alloca _alloca
41 void *alloca (size_t);
45 #include <Ecore_File.h>
48 #include <Efreet_Mime.h>
49 #include "efreet_private.h"
51 static Eina_List *globs = NULL; /* contains Efreet_Mime_Glob structs */
52 static Eina_List *magics = NULL; /* contains Efreet_Mime_Magic structs */
53 static Eina_Hash *wild = NULL; /* contains *.ext and mime.types globs*/
54 static Eina_Hash *monitors = NULL; /* contains file monitors */
55 static Eina_Hash *mime_icons = NULL; /* contains cache with mime->icons */
56 static Eina_Inlist *mime_icons_lru = NULL;
57 static unsigned int _init_count = 0;
61 * @brief Holds whether we are big/little endian
62 * @note This is set during efreet_mime_init based on
67 EFREET_ENDIAN_BIG = 0,
68 EFREET_ENDIAN_LITTLE = 1
69 } efreet_mime_endianess = EFREET_ENDIAN_BIG;
72 * Buffer sized used for magic checks. The default is good enough for the
73 * current set of magic rules. This setting is only here for the future.
75 #define EFREET_MIME_MAGIC_BUFFER_SIZE 512
78 * Minimum timeout in seconds between mime-icons cache flush.
80 #define EFREET_MIME_ICONS_FLUSH_TIMEOUT 60
83 * Timeout in seconds, when older mime-icons items are expired.
85 #define EFREET_MIME_ICONS_EXPIRE_TIMEOUT 600
88 * mime-icons maximum population.
90 #define EFREET_MIME_ICONS_MAX_POPULATION 512
93 * If defined, dump mime-icons statistics after flush.
95 //#define EFREET_MIME_ICONS_DEBUG
99 * @brief A parsed representation of a globs file
101 typedef struct Efreet_Mime_Glob Efreet_Mime_Glob;
102 struct Efreet_Mime_Glob
110 * @brief A parsed representation of a magic file section
112 typedef struct Efreet_Mime_Magic Efreet_Mime_Magic;
113 struct Efreet_Mime_Magic
115 unsigned int priority;
121 * Efreet_Mime_Magic_Entry
122 * @brief A parsed representation of a magic file entry
124 typedef struct Efreet_Mime_Magic_Entry Efreet_Mime_Magic_Entry;
125 struct Efreet_Mime_Magic_Entry
129 unsigned int word_size;
130 unsigned int range_len;
131 unsigned short value_len;
136 typedef struct Efreet_Mime_Icon_Entry_Head Efreet_Mime_Icon_Entry_Head;
137 struct Efreet_Mime_Icon_Entry_Head
139 EINA_INLIST; /* node of mime_icons_lru */
145 typedef struct Efreet_Mime_Icon_Entry Efreet_Mime_Icon_Entry;
146 struct Efreet_Mime_Icon_Entry
155 static int efreet_mime_glob_remove(const char *glob);
156 static void efreet_mime_mime_types_load(const char *file);
157 static void efreet_mime_shared_mimeinfo_globs_load(const char *file);
158 static void efreet_mime_shared_mimeinfo_magic_load(const char *file);
159 static void efreet_mime_shared_mimeinfo_magic_parse(char *data, int size);
160 static const char *efreet_mime_magic_check_priority(const char *file,
163 static int efreet_mime_init_files(void);
164 static const char *efreet_mime_special_check(const char *file);
165 static const char *efreet_mime_fallback_check(const char *file);
166 static void efreet_mime_glob_free(void *data);
167 static void efreet_mime_magic_free(void *data);
168 static void efreet_mime_magic_entry_free(void *data);
169 static int efreet_mime_glob_match(const char *str, const char *glob);
170 static int efreet_mime_glob_case_match(char *str, const char *glob);
171 static int efreet_mime_endian_check(void);
173 static void efreet_mime_monitor_add(const char *file);
174 static void efreet_mime_cb_update_file(void *data,
175 Ecore_File_Monitor *monitor,
176 Ecore_File_Event event,
179 static void efreet_mime_icons_flush(time_t now);
180 static void efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry);
181 static void efreet_mime_icon_entry_add(const char *mime,
185 static const char *efreet_mime_icon_entry_find(const char *mime,
188 static void efreet_mime_icons_debug(void);
191 * @return Returns 1 on success or 0 on failure
192 * @brief Initializes the efreet mime settings
195 efreet_mime_init(void)
204 if (!ecore_file_init())
210 efreet_mime_endianess = efreet_mime_endian_check();
212 monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
214 efreet_mime_type_cache_clear();
216 if (!efreet_mime_init_files())
223 * @return Returns no value
224 * @brief Cleans up the efreet mime settings system
227 efreet_mime_shutdown(void)
231 if (_init_count == 0)
237 efreet_mime_icons_debug();
239 IF_FREE_LIST(globs, efreet_mime_glob_free);
240 IF_FREE_LIST(magics, efreet_mime_magic_free);
241 IF_FREE_HASH(monitors);
243 IF_FREE_HASH(mime_icons);
246 ecore_file_shutdown();
251 * @param file: The file to find the mime type
252 * @return Returns mime type as a string
253 * @brief Retreive the mime type of a file
256 efreet_mime_type_get(const char *file)
258 const char *type = NULL;
260 if ((type = efreet_mime_special_check(file)))
263 /* Check magics with priority > 80 */
264 if ((type = efreet_mime_magic_check_priority(file, 0, 80)))
268 if ((type = efreet_mime_globs_type_get(file)))
271 /* Check rest of magics */
272 if ((type = efreet_mime_magic_check_priority(file, 80, 0)))
275 return efreet_mime_fallback_check(file);
279 * @param mime: The name of the mime type
280 * @param theme: The name of the theme to search icons in
281 * @param size: The wanted size of the icon
282 * @return Returns mime type icon path as a string
283 * @brief Retreive the mime type icon for a file
286 efreet_mime_type_icon_get(const char *mime, const char *theme, unsigned int size)
290 Eina_List *icons = NULL;
291 const char *env = NULL;
292 char *p = NULL, *pp = NULL, *ppp = NULL;
296 if (!mime || !theme || !size)
299 mime = eina_stringshare_add(mime);
300 theme = eina_stringshare_add(theme);
301 cache = efreet_mime_icon_entry_find(mime, theme, size);
304 eina_stringshare_del(mime);
305 eina_stringshare_del(theme);
306 return strdup(cache);
309 /* Standard icon name */
314 if (*pp == '/') *pp = '-';
317 icons = eina_list_append(icons, p);
319 /* Environment Based icon names */
320 if ((env = efreet_desktop_environment_get()))
322 snprintf(buf, sizeof(buf), "%s-mime-%s", env, p);
323 icons = eina_list_append(icons, strdup(buf));
325 snprintf(buf, sizeof(buf), "%s-%s", env, p);
326 icons = eina_list_append(icons, strdup(buf));
329 /* Mime prefixed icon names */
330 snprintf(buf, sizeof(buf), "mime-%s", p);
331 icons = eina_list_append(icons, strdup(buf));
335 while ((ppp = strrchr(pp, '-')))
339 snprintf(buf, sizeof(buf), "%s-generic", pp);
340 icons = eina_list_append(icons, strdup(buf));
342 snprintf(buf, sizeof(buf), "%s", pp);
343 icons = eina_list_append(icons, strdup(buf));
347 /* Search for icons using list */
348 icon = efreet_icon_list_find(theme, icons, size);
351 data = eina_list_data_get(icons);
353 icons = eina_list_remove_list(icons, icons);
356 efreet_mime_icon_entry_add(mime, eina_stringshare_add(icon), theme, size);
362 efreet_mime_type_cache_clear(void)
366 eina_hash_free(mime_icons);
367 mime_icons_lru = NULL;
369 mime_icons = eina_hash_pointer_new(EINA_FREE_CB(efreet_mime_icon_entry_head_free));
373 efreet_mime_type_cache_flush(void)
375 efreet_mime_icons_flush((time_t)ecore_loop_time_get());
380 * @param file: The file to check the mime type
381 * @return Returns mime type as a string
382 * @brief Retreive the mime type of a file using magic
385 efreet_mime_magic_type_get(const char *file)
387 return efreet_mime_magic_check_priority(file, 0, 0);
391 * @param file: The file to check the mime type
392 * @return Returns mime type as a string
393 * @brief Retreive the mime type of a file using globs
396 efreet_mime_globs_type_get(const char *file)
404 /* Check in the extension hash for the type */
405 ext = strchr(file, '.');
408 sl = alloca(strlen(ext) + 1);
409 for (s = ext, p = sl; *s; s++, p++) *p = tolower(*s);
415 if (p && (mime = eina_hash_find(wild, p))) return mime;
420 /* Fallback to the other globs if not found */
421 EINA_LIST_FOREACH(globs, l, g)
423 if (efreet_mime_glob_match(file, g->glob))
427 ext = alloca(strlen(file) + 1);
428 for (s = file, p = ext; *s; s++, p++) *p = tolower(*s);
430 EINA_LIST_FOREACH(globs, l, g)
432 if (efreet_mime_glob_case_match(ext, g->glob))
439 * @param file: The file to check the mime type
440 * @return Returns mime type as a string
441 * @brief Retreive the special mime type of a file
444 efreet_mime_special_type_get(const char *file)
446 return efreet_mime_special_check(file);
450 * @param file: The file to check the mime type
451 * @return Returns mime type as a string
452 * @brief Retreive the fallback mime type of a file
455 efreet_mime_fallback_type_get(const char *file)
457 return efreet_mime_fallback_check(file);
462 * @return Returns the endianess
463 * @brief Retreive the endianess of the machine
466 efreet_mime_endian_check(void)
469 return (*((char*)(&test)));
474 * @param file: File to monitor
475 * @return Returns no value.
476 * @brief Creates a new file monitor if we aren't already monitoring the
480 efreet_mime_monitor_add(const char *file)
482 Ecore_File_Monitor *fm = NULL;
484 /* if this is already in our hash then we're already monitoring so no
485 * reason to re-monitor */
486 if (eina_hash_find(monitors, file))
489 if ((fm = ecore_file_monitor_add(file, efreet_mime_cb_update_file, NULL)))
491 eina_hash_del(monitors, file, NULL);
492 eina_hash_add(monitors, file, fm);
498 * @param datadirs: List of XDG data dirs
499 * @param datahome: Path to XDG data home directory
500 * @return Returns no value
501 * @brief Read all glob files in XDG data/home dirs.
502 * Also reads the /etc/mime.types file.
505 efreet_mime_load_globs(Eina_List *datadirs, const char *datahome)
509 const char *datadir = NULL;
512 wild = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
515 efreet_mime_glob_free(eina_list_data_get(globs));
516 globs = eina_list_remove_list(globs, globs);
520 * This is here for legacy reasons. It is mentioned briefly
521 * in the spec and seems to still be quite valid. It is
522 * loaded first so the globs files will override anything
525 efreet_mime_mime_types_load("/etc/mime.types");
528 snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
529 efreet_mime_shared_mimeinfo_globs_load(buf);
531 EINA_LIST_FOREACH(datadirs, l, datadir)
533 snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
534 efreet_mime_shared_mimeinfo_globs_load(buf);
540 * @param datadirs: List of XDG data dirs
541 * @param datahome: Path to XDG data home directory
542 * @return Returns no value
543 * @brief Read all magic files in XDG data/home dirs.
546 efreet_mime_load_magics(Eina_List *datadirs, const char *datahome)
550 const char *datadir = NULL;
554 efreet_mime_magic_free(eina_list_data_get(magics));
555 magics = eina_list_remove_list(magics, magics);
559 snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
560 efreet_mime_shared_mimeinfo_magic_load(buf);
562 EINA_LIST_FOREACH(datadirs, l, datadir)
564 snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
565 efreet_mime_shared_mimeinfo_magic_load(buf);
571 * @param data: Data pointer passed to monitor_add
572 * @param monitor: Ecore_File_Monitor associated with this event
573 * @param event: The type of event
574 * @param path: Path to the file that was updated
575 * @return Returns no value
576 * @brief Callback for all file monitors. Just reloads the appropriate
577 * list depending on which file changed. If it was a magic file
578 * only the magic list is updated. If it was a glob file or /etc/mime.types,
579 * the globs are updated.
582 efreet_mime_cb_update_file(void *data __UNUSED__,
583 Ecore_File_Monitor *monitor __UNUSED__,
584 Ecore_File_Event event __UNUSED__,
587 Eina_List *datadirs = NULL;
588 const char *datahome = NULL;
590 if (!(datahome = efreet_data_home_get()))
593 if (!(datadirs = efreet_data_dirs_get()))
596 if (strstr(path, "magic"))
597 efreet_mime_load_magics(datadirs, datahome);
599 efreet_mime_load_globs(datadirs, datahome);
604 * @param datadirs: List of XDG data dirs
605 * @param datahome: Path to XDG data home directory
606 * @return Returns 1 on success, 0 on failure
607 * @brief Initializes globs, magics, and monitors lists.
610 efreet_mime_init_files(void)
613 Eina_List *datadirs = NULL;
615 const char *datahome, *datadir = NULL;
617 if (!(datahome = efreet_data_home_get()))
620 if (!(datadirs = efreet_data_dirs_get()))
624 * Add our file monitors
625 * We watch the directories so we can watch for new files
628 snprintf(buf, PATH_MAX, "%s/mime", datadir);
629 efreet_mime_monitor_add(buf);
631 EINA_LIST_FOREACH(datadirs, l, datadir)
633 snprintf(buf, PATH_MAX, "%s/mime", datadir);
634 efreet_mime_monitor_add(buf);
636 efreet_mime_monitor_add("/etc/mime.types");
638 /* Load our mime information */
639 efreet_mime_load_globs(datadirs, datahome);
640 efreet_mime_load_magics(datadirs, datahome);
647 * @param file: File to examine
648 * @return Returns mime type if special file, else NULL
649 * @brief Returns a mime type based on the stat of a file.
650 * This is used first to catch directories and other special
651 * files. A NULL return doesn't necessarily mean failure, but
652 * can also mean the file is regular.
653 * @note Mapping of file types to mime types:
654 * Stat Macro File Type Mime Type
655 * S_IFREG regular NULL
656 * S_IFIFO named pipe (fifo) inode/fifo
657 * S_IFCHR character special inode/chardevice
658 * S_IFDIR directory inode/directory
659 * S_IFBLK block special inode/blockdevice
660 * S_IFLNK symbolic link inode/symlink
661 * S_IFSOCK socket inode/socket
663 * This function can also return inode/mount-point.
664 * This is calculated by comparing the st_dev of the directory
665 * against that of it's parent directory. If they differ it
666 * is considered a mount point.
669 efreet_mime_special_check(const char *file)
674 if (!lstat(file, &s))
676 if (S_ISREG(s.st_mode))
680 if (S_ISLNK(s.st_mode))
681 return "inode/symlink";
684 if (S_ISFIFO(s.st_mode))
687 if (S_ISCHR(s.st_mode))
688 return "inode/chardevice";
690 if (S_ISBLK(s.st_mode))
691 return "inode/blockdevice";
694 if (S_ISSOCK(s.st_mode))
695 return "inode/socket";
698 if (S_ISDIR(s.st_mode))
701 char parent[PATH_MAX];
704 strncpy(path, file, PATH_MAX);
706 path_len = strlen(file);
707 strncpy(parent, path, PATH_MAX);
709 /* Kill any trailing slash */
710 parent[--path_len] = '\0';
712 /* Truncate to last slash */
713 while (parent[--path_len] != '/') parent[path_len] = '\0';
715 if (!lstat(parent, &s2))
717 if (s.st_dev != s2.st_dev)
718 return "inode/mount-point";
721 return "inode/directory";
732 * @param file: File to examine
733 * @return Returns mime type or NULL if the file doesn't exist
734 * @brief Returns text/plain if the file appears to contain text and
735 * returns application/octet-stream if it appears to be binary.
738 efreet_mime_fallback_check(const char *file)
744 if (!(f = fopen(file, "r"))) return NULL;
746 i = fread(buf, 1, sizeof(buf), f);
749 if (i == 0) return "application/octet-stream";
752 * Check for ASCII control characters in the first 32 bytes.
753 * Line Feeds, carriage returns, and tabs are ignored as they are
754 * quite common in text files in the first 32 chars.
756 for (i -= 1; i >= 0; --i)
758 if ((buf[i] < 0x20) &&
759 (buf[i] != '\n') && /* Line Feed */
760 (buf[i] != '\r') && /* Carriage Return */
761 (buf[i] != '\t')) /* Tab */
762 return "application/octet-stream";
770 * @param glob: Glob to search for
771 * @return Returns 1 on success, 0 on failure
772 * @brief Removes a glob from the list
775 efreet_mime_glob_remove(const char *glob)
777 Efreet_Mime_Glob *mime = NULL;
779 if ((mime = eina_list_search_unsorted(globs, EINA_COMPARE_CB(strcmp), glob)))
781 globs = eina_list_remove(globs, mime);
782 IF_RELEASE(mime->glob);
783 IF_RELEASE(mime->mime);
793 * @param file: mime.types file to load
794 * @return Returns no value
795 * @brief Loads values from a mime.types style file
796 * into the globs list.
798 * application/msaccess mdb
799 * application/msword doc dot
802 efreet_mime_mime_types_load(const char *file)
805 char buf[4096], mimetype[4096];
806 char ext[4096], *p = NULL, *pp = NULL;
808 f = fopen(file, "rb");
810 while (fgets(buf, sizeof(buf), f))
813 while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
815 if (*p == '#') continue;
816 if ((*p == '\n') || (*p == 0)) continue;
819 while (!isspace(*p) && (*p != 0) && (*p != '\n')) p++;
821 if ((*p == '\n') || (*p == 0)) continue;
822 strncpy(mimetype, pp, (p - pp));
823 mimetype[p - pp] = 0;
827 while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
829 if ((*p == '\n') || (*p == 0)) break;
832 while (!isspace(*p) && (*p != 0) && (*p != '\n')) p++;
834 strncpy(ext, pp, (p - pp));
837 eina_hash_del(wild, ext, NULL);
838 eina_hash_add(wild, ext, (void*)eina_stringshare_add(mimetype));
840 while ((*p != '\n') && (*p != 0));
847 * @param file: globs file to load
848 * @return Returns no value
849 * @brief Loads values from a mime.types style file
850 * into the globs list.
852 * text/vnd.wap.wml:*.wml
853 * application/x-7z-compressed:*.7z
854 * application/vnd.corel-draw:*.cdr
855 * text/spreadsheet:*.sylk
858 efreet_mime_shared_mimeinfo_globs_load(const char *file)
861 char buf[4096], mimetype[4096], ext[4096], *p, *pp;
862 Efreet_Mime_Glob *mime = NULL;
864 f = fopen(file, "rb");
867 while (fgets(buf, sizeof(buf), f))
870 while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
872 if (*p == '#') continue;
873 if ((*p == '\n') || (*p == 0)) continue;
876 while ((*p != ':') && (*p != 0) && (*p != '\n')) p++;
878 if ((*p == '\n') || (*p == 0)) continue;
879 strncpy(mimetype, pp, (p - pp));
880 mimetype[p - pp] = 0;
884 while ((*p != 0) && (*p != '\n'))
893 if (ext[0] == '*' && ext[1] == '.')
895 eina_hash_del(wild, &(ext[2]), NULL);
896 eina_hash_add(wild, &(ext[2]),
897 (void*)eina_stringshare_add(mimetype));
901 mime = NEW(Efreet_Mime_Glob, 1);
904 mime->mime = eina_stringshare_add(mimetype);
905 mime->glob = eina_stringshare_add(ext);
906 if ((!mime->mime) || (!mime->glob))
908 IF_RELEASE(mime->mime);
909 IF_RELEASE(mime->glob);
914 efreet_mime_glob_remove(ext);
915 globs = eina_list_append(globs, mime);
926 * @param in: Number to count the digits
927 * @return Returns number of digits
928 * @brief Calculates and returns the number of digits
932 efreet_mime_count_digits(int in)
936 if (j < 10) return 1;
937 while ((j /= 10) > 0) ++i;
944 * @param file: File to parse
945 * @return Returns no value
946 * @brief Loads a magic file and adds information to magics list
949 efreet_mime_shared_mimeinfo_magic_load(const char *file)
952 char *data = (void *)-1;
956 size = ecore_file_size(file);
957 if (size <= 0) return;
959 fd = open(file, O_RDONLY);
960 if (fd == -1) return;
962 data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
963 if (data == MAP_FAILED)
969 efreet_mime_shared_mimeinfo_magic_parse(data, size);
976 * @param data: The data from the file
977 * @return Returns no value
978 * @brief Parses a magic file
981 * ----------------------------------------------------------------------
983 * ----------------------------------------------------------------------
984 * |4D 49 4D 45 2D 4D 61 67 69 63 00 0A 5B 39 30 3A | MIME-Magic..[90: |
985 * |61 70 70 6C 69 63 61 74 69 6F 6E 2F 64 6F 63 62 | application/docb |
986 * |6F 6F 6B 2B 78 6D 6C 5D 0A 3E 30 3D 00 05 3C 3F | ook+xml].>0=..<? |
987 * |78 6D 6C 0A 31 3E 30 3D 00 19 2D 2F 2F 4F 41 53 | xml.1>0=..-//OAS |
988 * |49 53 2F 2F 44 54 44 20 44 6F 63 42 6F 6F 6B 20 | IS//DTD DocBook |
989 * |58 4D 4C 2B 31 30 31 0A 31 3E 30 3D 00 17 2D 2F | XML+101.1>0=..-/ |
990 * ----------------------------------------------------------------------
993 * The nesting depth of the rule, corresponding to the number of '>'
994 * characters in the traditional file format.
996 * The offset into the file to look for a match.
998 * Two bytes giving the (big-endian) length of the value, followed by the
1001 * The mask, which (if present) is exactly the same length as the value.
1003 * On little-endian machines, the size of each group to byte-swap.
1005 * The length of the region in the file to check.
1007 * The indent, range-length, word-size and mask components are optional.
1008 * If missing, indent defaults to 0, range-length to 1, the word-size to 1,
1009 * and the mask to all 'one' bits. In our case, mask is null as it is
1010 * quicker, uses less memory and will acheive the same exact effect.
1013 efreet_mime_shared_mimeinfo_magic_parse(char *data, int size)
1015 Efreet_Mime_Magic *mime = NULL;
1016 Efreet_Mime_Magic_Entry *entry = NULL;
1021 /* make sure we're a magic file */
1022 if (!ptr || (size < 12) || strncmp(ptr, "MIME-Magic\0\n", 12))
1027 for (; (ptr - data) < size; )
1031 char *val, buf[512];
1033 mime = NEW(Efreet_Mime_Magic, 1);
1034 magics = eina_list_append(magics, mime);
1037 while ((*val != ':')) val++;
1038 memcpy(&buf, ptr, val - ptr);
1039 buf[val - ptr] = '\0';
1041 mime->priority = atoi(buf);
1044 while ((*val != ']')) val++;
1045 memcpy(&buf, ptr, val - ptr);
1046 buf[val - ptr] = '\0';
1048 mime->mime = eina_stringshare_add(buf);
1051 while (*ptr != '\n') ptr++;
1057 if (!mime) continue;
1060 if (!(entry = NEW(Efreet_Mime_Magic_Entry, 1)))
1062 IF_FREE_LIST(magics, efreet_mime_magic_free);
1068 entry->value_len = 0;
1069 entry->word_size = 1;
1070 entry->range_len = 1;
1072 entry->value = NULL;
1075 mime->entries = eina_list_append(mime->entries, entry);
1082 entry->offset = atoi(ptr);
1083 ptr += efreet_mime_count_digits(entry->offset);
1089 memcpy(&tshort, ptr, sizeof(short));
1090 entry->value_len = ntohs(tshort);
1093 entry->value = NEW(1, entry->value_len);
1094 memcpy(entry->value, ptr, entry->value_len);
1095 ptr += entry->value_len;
1100 entry->mask = NEW(1, entry->value_len);
1101 memcpy(entry->mask, ptr, entry->value_len);
1102 ptr += entry->value_len;
1107 entry->word_size = atoi(ptr);
1108 if (((entry->word_size != 0)
1109 && (entry->word_size != 1)
1110 && (entry->word_size != 2)
1111 && (entry->word_size != 4))
1112 || (entry->value_len % entry->word_size))
1114 /* Invalid, Destroy */
1119 while (*ptr != '\n') ptr++;
1123 if (efreet_mime_endianess == EFREET_ENDIAN_LITTLE)
1127 for (j = 0; j < entry->value_len; j += entry->word_size)
1129 if (entry->word_size == 2)
1131 ((short*)entry->value)[j] =
1132 ntohs(((short*)entry->value)[j]);
1135 ((short*)entry->mask)[j] =
1136 ntohs(((short*)entry->mask)[j]);
1138 else if (entry->word_size == 4)
1140 ((int*)entry->value)[j] =
1141 ntohl(((int*)entry->value)[j]);
1144 ((int*)entry->mask)[j] =
1145 ntohl(((int*)entry->mask)[j]);
1150 ptr += efreet_mime_count_digits(entry->word_size);
1155 entry->range_len = atoi(ptr);
1156 ptr += efreet_mime_count_digits(entry->range_len);
1167 entry->indent = atoi(ptr);
1168 ptr += efreet_mime_count_digits(entry->indent);
1177 IF_FREE(entry->value);
1178 IF_FREE(entry->mask);
1186 * @param file: File to check
1187 * @param start: Start priority, if 0 start at beginning
1188 * @param end: End priority, should be less then start
1190 * @return Returns mime type for file if found, NULL if not
1191 * @brief Applies magic rules to a file given a start and end priority
1194 efreet_mime_magic_check_priority(const char *file,
1198 Efreet_Mime_Magic *m = NULL;
1199 Efreet_Mime_Magic_Entry *e = NULL;
1202 unsigned int i = 0, offset = 0,level = 0, match = 0, bytes_read = 0;
1203 const char *last_mime = NULL;
1204 char c, v, buf[EFREET_MIME_MAGIC_BUFFER_SIZE];
1206 f = fopen(file, "rb");
1207 if (!f) return NULL;
1215 if ((bytes_read = fread(buf, 1, sizeof(buf), f)) == 0)
1221 EINA_LIST_FOREACH(magics, l, m)
1223 if ((start != 0) && (m->priority > start))
1226 if (m->priority < end)
1229 EINA_LIST_FOREACH(m->entries, ll, e)
1231 if ((level < e->indent) && !match)
1234 if ((level >= e->indent) && !match)
1237 else if ((level > e->indent) && match)
1240 if (last_mime) return last_mime;
1243 for (offset = e->offset; offset < e->offset + e->range_len; offset++)
1245 if (((offset + e->value_len) > bytes_read) &&
1246 (fseek(f, offset, SEEK_SET) == -1))
1250 for (i = 0; i < e->value_len; ++i)
1252 if (offset + e->value_len > bytes_read)
1255 c = buf[offset + i];
1258 if (e->mask) v &= e->mask[i];
1270 last_mime = m->mime;
1283 * @param data: Data pointer that is being destroyed
1284 * @return Returns no value
1285 * @brief Callback for globs destroy
1288 efreet_mime_glob_free(void *data)
1290 Efreet_Mime_Glob *m = data;
1292 IF_RELEASE(m->mime);
1293 IF_RELEASE(m->glob);
1299 * @param data: Data pointer that is being destroyed
1300 * @return Returns no value
1301 * @brief Callback for magics destroy
1304 efreet_mime_magic_free(void *data)
1306 Efreet_Mime_Magic *m = data;
1307 Efreet_Mime_Magic_Entry *entry = NULL;
1309 IF_RELEASE(m->mime);
1312 entry = eina_list_data_get(m->entries);
1313 efreet_mime_magic_entry_free(entry);
1314 m->entries = eina_list_remove_list(m->entries, m->entries);
1321 * @param data: Data pointer that is being destroyed
1322 * @return Returns no value
1323 * @brief Callback for magic entry destroy
1326 efreet_mime_magic_entry_free(void *data)
1328 Efreet_Mime_Magic_Entry *e = data;
1338 * @param str: String (filename) to match
1339 * @param glob: Glob to match str to
1340 * @return Returns 1 on success, 0 on failure
1341 * @brief Compares str to glob, case sensitive
1344 efreet_mime_glob_match(const char *str, const char *glob)
1346 if (!str || !glob) return 0;
1349 if (str[0] == 0) return 1;
1352 if (!fnmatch(glob, str, 0)) return 1;
1358 * @param str: String (filename) to match
1359 * @param glob: Glob to match str to
1360 * @return Returns 1 on success, 0 on failure
1361 * @brief Compares str to glob, case insensitive (expects str already in lower case)
1364 efreet_mime_glob_case_match(char *str, const char *glob)
1369 if (!str || !glob) return 0;
1372 if (str[0] == 0) return 1;
1375 tglob = alloca(strlen(glob) + 1);
1376 for (tp = tglob, p = glob; *p; p++, tp++) *tp = tolower(*p);
1378 if (!fnmatch(str, tglob, 0)) return 1;
1383 efreet_mime_icons_flush(time_t now)
1386 static time_t old = 0;
1389 if (now - old < EFREET_MIME_ICONS_FLUSH_TIMEOUT)
1393 todo = eina_hash_population(mime_icons) - EFREET_MIME_ICONS_MAX_POPULATION;
1397 l = mime_icons_lru->last; /* mime_icons_lru is not NULL, since todo > 0 */
1398 for (; todo > 0; todo--)
1400 Efreet_Mime_Icon_Entry_Head *entry = (Efreet_Mime_Icon_Entry_Head *)l;
1401 Eina_Inlist *prev = l->prev;
1403 mime_icons_lru = eina_inlist_remove(mime_icons_lru, l);
1404 eina_hash_del_by_key(mime_icons, entry->mime);
1408 efreet_mime_icons_debug();
1412 efreet_mime_icon_entry_free(Efreet_Mime_Icon_Entry *node)
1414 eina_stringshare_del(node->icon);
1415 eina_stringshare_del(node->theme);
1420 efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry)
1424 Efreet_Mime_Icon_Entry *n = (Efreet_Mime_Icon_Entry *)entry->list;
1425 entry->list = eina_inlist_remove(entry->list, entry->list);
1426 efreet_mime_icon_entry_free(n);
1429 eina_stringshare_del(entry->mime);
1433 static Efreet_Mime_Icon_Entry *
1434 efreet_mime_icon_entry_new(const char *icon,
1438 Efreet_Mime_Icon_Entry *entry;
1440 entry = malloc(sizeof(*entry));
1445 entry->theme = theme;
1452 efreet_mime_icon_entry_add(const char *mime,
1457 Efreet_Mime_Icon_Entry_Head *entry;
1458 Efreet_Mime_Icon_Entry *n;
1460 n = efreet_mime_icon_entry_new(icon, theme, size);
1463 entry = eina_hash_find(mime_icons, mime);
1469 l = EINA_INLIST_GET(n);
1470 entry->list = eina_inlist_prepend(entry->list, l);
1472 l = EINA_INLIST_GET(entry);
1473 mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
1479 entry = malloc(sizeof(*entry));
1482 efreet_mime_icon_entry_free(n);
1486 l = EINA_INLIST_GET(n);
1487 entry->list = eina_inlist_prepend(NULL, l);
1489 eina_hash_direct_add(mime_icons, mime, entry);
1491 l = EINA_INLIST_GET(entry);
1492 mime_icons_lru = eina_inlist_prepend(mime_icons_lru, l);
1495 entry->timestamp = (time_t)ecore_loop_time_get();
1496 efreet_mime_icons_flush(entry->timestamp);
1500 efreet_mime_icon_entry_find(const char *mime,
1504 Efreet_Mime_Icon_Entry_Head *entry;
1505 Efreet_Mime_Icon_Entry *n;
1507 entry = eina_hash_find(mime_icons, mime);
1511 EINA_INLIST_FOREACH(entry->list, n)
1513 if ((n->theme == theme) && (n->size == size))
1517 l = EINA_INLIST_GET(n);
1518 if (entry->list != l)
1519 entry->list = eina_inlist_promote(entry->list, l);
1521 l = EINA_INLIST_GET(entry);
1522 if (mime_icons_lru != l)
1523 mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
1525 entry->timestamp = (time_t)ecore_loop_time_get();
1533 #ifdef EFREET_MIME_ICONS_DEBUG
1535 efreet_mime_icons_debug(void)
1537 time_t now = (time_t)ecore_loop_time_get();
1538 Efreet_Mime_Icon_Entry_Head *entry;
1539 EINA_INLIST_FOREACH(mime_icons_lru, entry)
1541 Efreet_Mime_Icon_Entry *n;
1544 (now - entry->timestamp >= EFREET_MIME_ICONS_EXPIRE_TIMEOUT))
1546 puts("*** FOLLOWING ENTRIES ARE AGED AND CAN BE EXPIRED ***");
1550 printf("mime-icon entry: '%s' last used: %s",
1551 entry->mime, ctime(&entry->timestamp));
1553 EINA_INLIST_FOREACH(entry->list, n)
1554 printf("\tsize: %3u theme: '%s' icon: '%s'\n",
1555 n->theme, n->size, n->icon);
1560 efreet_mime_icons_debug(void)