8 # define alloca __builtin_alloca
10 # define alloca __alloca
11 #elif defined _MSC_VER
13 # define alloca _alloca
19 void *alloca (size_t);
30 # include <winsock2.h>
33 #ifdef HAVE_NETINET_IN_H
34 # include <netinet/in.h>
37 #ifdef HAVE_ARPA_INET_H
38 # include <arpa/inet.h>
42 #include <Ecore_File.h>
44 /* define macros and variable for using the eina logging system */
45 #define EFREET_MODULE_LOG_DOM _efreet_mime_log_dom
46 static int _efreet_mime_log_dom = -1;
49 #include "Efreet_Mime.h"
50 #include "efreet_private.h"
52 static Eina_List *globs = NULL; /* contains Efreet_Mime_Glob structs */
53 static Eina_List *magics = NULL; /* contains Efreet_Mime_Magic structs */
54 static Eina_Hash *wild = NULL; /* contains *.ext and mime.types globs*/
55 static Eina_Hash *monitors = NULL; /* contains file monitors */
56 static Eina_Hash *mime_icons = NULL; /* contains cache with mime->icons */
57 static Eina_Inlist *mime_icons_lru = NULL;
58 static unsigned int _efreet_mime_init_count = 0;
60 static const char *_mime_inode_symlink = NULL;
61 static const char *_mime_inode_fifo = NULL;
62 static const char *_mime_inode_chardevice = NULL;
63 static const char *_mime_inode_blockdevice = NULL;
64 static const char *_mime_inode_socket = NULL;
65 static const char *_mime_inode_mountpoint = NULL;
66 static const char *_mime_inode_directory = NULL;
67 static const char *_mime_application_x_executable = NULL;
68 static const char *_mime_application_octet_stream = NULL;
69 static const char *_mime_text_plain = NULL;
73 * @brief Holds whether we are big/little endian
74 * @note This is set during efreet_mime_init based on
79 EFREET_ENDIAN_BIG = 0,
80 EFREET_ENDIAN_LITTLE = 1
81 } efreet_mime_endianess = EFREET_ENDIAN_BIG;
84 * Buffer sized used for magic checks. The default is good enough for the
85 * current set of magic rules. This setting is only here for the future.
87 #define EFREET_MIME_MAGIC_BUFFER_SIZE 512
90 * Minimum timeout in seconds between mime-icons cache flush.
92 #define EFREET_MIME_ICONS_FLUSH_TIMEOUT 60
95 * Timeout in seconds, when older mime-icons items are expired.
97 #define EFREET_MIME_ICONS_EXPIRE_TIMEOUT 600
100 * mime-icons maximum population.
102 #define EFREET_MIME_ICONS_MAX_POPULATION 512
105 * If defined, dump mime-icons statistics after flush.
107 //#define EFREET_MIME_ICONS_DEBUG
109 typedef struct Efreet_Mime_Glob Efreet_Mime_Glob;
110 struct Efreet_Mime_Glob
116 typedef struct Efreet_Mime_Magic Efreet_Mime_Magic;
117 struct Efreet_Mime_Magic
119 unsigned int priority;
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
154 static int efreet_mime_glob_remove(const char *glob);
155 static void efreet_mime_mime_types_load(const char *file);
156 static void efreet_mime_shared_mimeinfo_globs_load(const char *file);
157 static void efreet_mime_shared_mimeinfo_magic_load(const char *file);
158 static void efreet_mime_shared_mimeinfo_magic_parse(char *data, int size);
159 static const char *efreet_mime_magic_check_priority(const char *file,
162 static int efreet_mime_init_files(void);
163 static const char *efreet_mime_special_check(const char *file);
164 static const char *efreet_mime_fallback_check(const char *file);
165 static void efreet_mime_glob_free(void *data);
166 static void efreet_mime_magic_free(void *data);
167 static void efreet_mime_magic_entry_free(void *data);
168 static int efreet_mime_glob_match(const char *str, const char *glob);
169 static int efreet_mime_glob_case_match(char *str, const char *glob);
170 static int efreet_mime_endian_check(void);
172 static void efreet_mime_monitor_add(const char *file);
173 static void efreet_mime_cb_update_file(void *data,
174 Ecore_File_Monitor *monitor,
175 Ecore_File_Event event,
178 static void efreet_mime_icons_flush(double now);
179 static void efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry);
180 static void efreet_mime_icon_entry_add(const char *mime,
184 static const char *efreet_mime_icon_entry_find(const char *mime,
187 static void efreet_mime_icons_debug(void);
190 efreet_mime_init(void)
192 if (++_efreet_mime_init_count != 1)
193 return _efreet_mime_init_count;
196 return --_efreet_mime_init_count;
198 if (!ecore_file_init())
202 goto shutdown_ecore_file;
204 _efreet_mime_log_dom = eina_log_domain_register
205 ("efreet_mime", EFREET_DEFAULT_LOG_COLOR);
207 if (_efreet_mime_log_dom < 0)
209 EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_mime.");
210 goto shutdown_efreet;
213 efreet_mime_endianess = efreet_mime_endian_check();
215 monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
217 efreet_mime_type_cache_clear();
219 if (!efreet_mime_init_files())
220 goto unregister_log_domain;
222 return _efreet_mime_init_count;
224 unregister_log_domain:
225 eina_log_domain_unregister(_efreet_mime_log_dom);
226 _efreet_mime_log_dom = -1;
230 ecore_file_shutdown();
234 return --_efreet_mime_init_count;
238 efreet_mime_shutdown(void)
240 if (--_efreet_mime_init_count != 0)
241 return _efreet_mime_init_count;
243 efreet_mime_icons_debug();
245 IF_RELEASE(_mime_inode_symlink);
246 IF_RELEASE(_mime_inode_fifo);
247 IF_RELEASE(_mime_inode_chardevice);
248 IF_RELEASE(_mime_inode_blockdevice);
249 IF_RELEASE(_mime_inode_socket);
250 IF_RELEASE(_mime_inode_mountpoint);
251 IF_RELEASE(_mime_inode_directory);
252 IF_RELEASE(_mime_application_x_executable);
253 IF_RELEASE(_mime_application_octet_stream);
254 IF_RELEASE(_mime_text_plain);
256 IF_FREE_LIST(globs, efreet_mime_glob_free);
257 IF_FREE_LIST(magics, efreet_mime_magic_free);
258 IF_FREE_HASH(monitors);
260 IF_FREE_HASH(mime_icons);
261 eina_log_domain_unregister(_efreet_mime_log_dom);
262 _efreet_mime_log_dom = -1;
264 ecore_file_shutdown();
267 return _efreet_mime_init_count;
271 efreet_mime_type_get(const char *file)
273 const char *type = NULL;
275 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
277 if ((type = efreet_mime_special_check(file)))
280 /* Check magics with priority > 80 */
281 if ((type = efreet_mime_magic_check_priority(file, 0, 80)))
285 if ((type = efreet_mime_globs_type_get(file)))
288 /* Check rest of magics */
289 if ((type = efreet_mime_magic_check_priority(file, 80, 0)))
292 return efreet_mime_fallback_check(file);
296 efreet_mime_type_icon_get(const char *mime, const char *theme, unsigned int size)
298 const char *icon = NULL;
300 Eina_List *icons = NULL;
301 const char *env = NULL;
302 char *p = NULL, *pp = NULL, *ppp = NULL;
306 EINA_SAFETY_ON_NULL_RETURN_VAL(mime, NULL);
307 EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
309 mime = eina_stringshare_add(mime);
310 theme = eina_stringshare_add(theme);
311 cache = efreet_mime_icon_entry_find(mime, theme, size);
314 eina_stringshare_del(mime);
315 eina_stringshare_del(theme);
319 /* Standard icon name */
324 if (*pp == '/') *pp = '-';
327 icons = eina_list_append(icons, p);
329 /* Environment Based icon names */
330 if ((env = efreet_desktop_environment_get()))
332 snprintf(buf, sizeof(buf), "%s-mime-%s", env, p);
333 icons = eina_list_append(icons, strdup(buf));
335 snprintf(buf, sizeof(buf), "%s-%s", env, p);
336 icons = eina_list_append(icons, strdup(buf));
339 /* Mime prefixed icon names */
340 snprintf(buf, sizeof(buf), "mime-%s", p);
341 icons = eina_list_append(icons, strdup(buf));
345 while ((ppp = strrchr(pp, '-')))
349 snprintf(buf, sizeof(buf), "%s-x-generic", pp);
350 icons = eina_list_append(icons, strdup(buf));
352 snprintf(buf, sizeof(buf), "%s-generic", pp);
353 icons = eina_list_append(icons, strdup(buf));
355 snprintf(buf, sizeof(buf), "%s", pp);
356 icons = eina_list_append(icons, strdup(buf));
360 /* Search for icons using list */
361 icon = efreet_icon_list_find(theme, icons, size);
364 data = eina_list_data_get(icons);
366 icons = eina_list_remove_list(icons, icons);
369 efreet_mime_icon_entry_add(mime, eina_stringshare_add(icon), theme, size);
375 efreet_mime_type_cache_clear(void)
379 eina_hash_free(mime_icons);
380 mime_icons_lru = NULL;
382 mime_icons = eina_hash_stringshared_new(EINA_FREE_CB(efreet_mime_icon_entry_head_free));
386 efreet_mime_type_cache_flush(void)
388 efreet_mime_icons_flush(ecore_loop_time_get());
393 efreet_mime_magic_type_get(const char *file)
395 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
396 return efreet_mime_magic_check_priority(file, 0, 0);
400 efreet_mime_globs_type_get(const char *file)
408 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
410 /* Check in the extension hash for the type */
411 ext = strchr(file, '.');
414 sl = alloca(strlen(ext) + 1);
415 for (s = ext, p = sl; *s; s++, p++) *p = tolower(*s);
421 if (p && (mime = eina_hash_find(wild, p))) return mime;
426 /* Fallback to the other globs if not found */
427 EINA_LIST_FOREACH(globs, l, g)
429 if (efreet_mime_glob_match(file, g->glob))
433 ext = alloca(strlen(file) + 1);
434 for (s = file, p = ext; *s; s++, p++) *p = tolower(*s);
436 EINA_LIST_FOREACH(globs, l, g)
438 if (efreet_mime_glob_case_match(ext, g->glob))
445 efreet_mime_special_type_get(const char *file)
447 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
448 return efreet_mime_special_check(file);
452 efreet_mime_fallback_type_get(const char *file)
454 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
455 return efreet_mime_fallback_check(file);
460 * @return Returns the endianess
461 * @brief Retreive the endianess of the machine
464 efreet_mime_endian_check(void)
467 return (*((char*)(&test)));
472 * @param file File to monitor
473 * @return Returns no value.
474 * @brief Creates a new file monitor if we aren't already monitoring the
478 efreet_mime_monitor_add(const char *file)
480 Ecore_File_Monitor *fm = NULL;
482 /* if this is already in our hash then we're already monitoring so no
483 * reason to re-monitor */
484 if (eina_hash_find(monitors, file))
487 if ((fm = ecore_file_monitor_add(file, efreet_mime_cb_update_file, NULL)))
489 eina_hash_del(monitors, file, NULL);
490 eina_hash_add(monitors, file, fm);
496 * @param datadirs List of XDG data dirs
497 * @param datahome Path to XDG data home directory
498 * @return Returns no value
499 * @brief Read all glob files in XDG data/home dirs.
500 * Also reads the /etc/mime.types file.
503 efreet_mime_load_globs(Eina_List *datadirs, const char *datahome)
507 const char *datadir = NULL;
510 wild = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
513 efreet_mime_glob_free(eina_list_data_get(globs));
514 globs = eina_list_remove_list(globs, globs);
518 * This is here for legacy reasons. It is mentioned briefly
519 * in the spec and seems to still be quite valid. It is
520 * loaded first so the globs files will override anything
523 efreet_mime_mime_types_load("/etc/mime.types");
526 snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
527 efreet_mime_shared_mimeinfo_globs_load(buf);
529 EINA_LIST_FOREACH(datadirs, l, datadir)
531 snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
532 efreet_mime_shared_mimeinfo_globs_load(buf);
538 * @param datadirs List of XDG data dirs
539 * @param datahome Path to XDG data home directory
540 * @return Returns no value
541 * @brief Read all magic files in XDG data/home dirs.
544 efreet_mime_load_magics(Eina_List *datadirs, const char *datahome)
548 const char *datadir = NULL;
552 efreet_mime_magic_free(eina_list_data_get(magics));
553 magics = eina_list_remove_list(magics, magics);
557 snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
558 efreet_mime_shared_mimeinfo_magic_load(buf);
560 EINA_LIST_FOREACH(datadirs, l, datadir)
562 snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
563 efreet_mime_shared_mimeinfo_magic_load(buf);
569 * @param data Data pointer passed to monitor_add
570 * @param monitor Ecore_File_Monitor associated with this event
571 * @param event The type of event
572 * @param path Path to the file that was updated
573 * @return Returns no value
574 * @brief Callback for all file monitors. Just reloads the appropriate
575 * list depending on which file changed. If it was a magic file
576 * only the magic list is updated. If it was a glob file or /etc/mime.types,
577 * the globs are updated.
580 efreet_mime_cb_update_file(void *data __UNUSED__,
581 Ecore_File_Monitor *monitor __UNUSED__,
582 Ecore_File_Event event __UNUSED__,
585 Eina_List *datadirs = NULL;
586 const char *datahome = NULL;
588 if (!(datahome = efreet_data_home_get()))
591 if (!(datadirs = efreet_data_dirs_get()))
594 if (strstr(path, "magic"))
595 efreet_mime_load_magics(datadirs, datahome);
597 efreet_mime_load_globs(datadirs, datahome);
602 * @param datadirs List of XDG data dirs
603 * @param datahome Path to XDG data home directory
604 * @return Returns 1 on success, 0 on failure
605 * @brief Initializes globs, magics, and monitors lists.
608 efreet_mime_init_files(void)
611 Eina_List *datadirs = NULL;
613 const char *datahome, *datadir = NULL;
615 if (!(datahome = efreet_data_home_get()))
618 if (!(datadirs = efreet_data_dirs_get()))
622 * Add our file monitors
623 * We watch the directories so we can watch for new files
626 snprintf(buf, sizeof(buf), "%s/mime", datadir);
627 efreet_mime_monitor_add(buf);
629 EINA_LIST_FOREACH(datadirs, l, datadir)
631 snprintf(buf, sizeof(buf), "%s/mime", datadir);
632 efreet_mime_monitor_add(buf);
634 efreet_mime_monitor_add("/etc/mime.types");
636 /* Load our mime information */
637 efreet_mime_load_globs(datadirs, datahome);
638 efreet_mime_load_magics(datadirs, datahome);
640 _mime_inode_symlink = eina_stringshare_add("inode/symlink");
641 _mime_inode_fifo = eina_stringshare_add("inode/fifo");
642 _mime_inode_chardevice = eina_stringshare_add("inode/chardevice");
643 _mime_inode_blockdevice = eina_stringshare_add("inode/blockdevice");
644 _mime_inode_socket = eina_stringshare_add("inode/socket");
645 _mime_inode_mountpoint = eina_stringshare_add("inode/mountpoint");
646 _mime_inode_directory = eina_stringshare_add("inode/directory");
647 _mime_application_x_executable = eina_stringshare_add("application/x-executable");
648 _mime_application_octet_stream = eina_stringshare_add("application/octet-stream");
649 _mime_text_plain = eina_stringshare_add("text/plain");
656 * @param file File to examine
657 * @return Returns mime type if special file, else NULL
658 * @brief Returns a mime type based on the stat of a file.
659 * This is used first to catch directories and other special
660 * files. A NULL return doesn't necessarily mean failure, but
661 * can also mean the file is regular.
662 * @note Mapping of file types to mime types:
663 * Stat Macro File Type Mime Type
664 * S_IFREG regular NULL
665 * S_IFIFO named pipe (fifo) inode/fifo
666 * S_IFCHR character special inode/chardevice
667 * S_IFDIR directory inode/directory
668 * S_IFBLK block special inode/blockdevice
669 * S_IFLNK symbolic link inode/symlink
670 * S_IFSOCK socket inode/socket
672 * This function can also return inode/mount-point.
673 * This is calculated by comparing the st_dev of the directory
674 * against that of it's parent directory. If they differ it
675 * is considered a mount point.
678 efreet_mime_special_check(const char *file)
683 /* no link on Windows < Vista */
687 if (!lstat(file, &s))
690 if (S_ISREG(s.st_mode))
694 if (S_ISLNK(s.st_mode))
695 return _mime_inode_symlink;
698 if (S_ISFIFO(s.st_mode))
699 return _mime_inode_fifo;
701 if (S_ISCHR(s.st_mode))
702 return _mime_inode_chardevice;
704 if (S_ISBLK(s.st_mode))
705 return _mime_inode_blockdevice;
708 if (S_ISSOCK(s.st_mode))
709 return _mime_inode_socket;
712 if (S_ISDIR(s.st_mode))
715 char parent[PATH_MAX];
718 strncpy(path, file, PATH_MAX);
720 path_len = strlen(file);
721 strncpy(parent, path, PATH_MAX);
723 /* Kill any trailing slash */
724 parent[--path_len] = '\0';
726 /* Truncate to last slash */
727 while (parent[--path_len] != '/') parent[path_len] = '\0';
730 if (!stat(file, &s2))
732 if (!lstat(parent, &s2))
735 if (s.st_dev != s2.st_dev)
736 return _mime_inode_mountpoint;
739 return _mime_inode_directory;
750 * @param file File to examine
751 * @return Returns mime type or NULL if the file doesn't exist
752 * @brief Returns text/plain if the file appears to contain text and
753 * returns application/octet-stream if it appears to be binary.
756 efreet_mime_fallback_check(const char *file)
762 if (ecore_file_can_exec(file))
763 return _mime_application_x_executable;
765 if (!(f = fopen(file, "r"))) return NULL;
767 i = fread(buf, 1, sizeof(buf), f);
770 if (i == 0) return _mime_application_octet_stream;
773 * Check for ASCII control characters in the first 32 bytes.
774 * Line Feeds, carriage returns, and tabs are ignored as they are
775 * quite common in text files in the first 32 chars.
777 for (i -= 1; i >= 0; --i)
779 if ((buf[i] < 0x20) &&
780 (buf[i] != '\n') && /* Line Feed */
781 (buf[i] != '\r') && /* Carriage Return */
782 (buf[i] != '\t')) /* Tab */
783 return _mime_application_octet_stream;
786 return _mime_text_plain;
791 * @param glob Glob to search for
792 * @return Returns 1 on success, 0 on failure
793 * @brief Removes a glob from the list
796 efreet_mime_glob_remove(const char *glob)
798 Efreet_Mime_Glob *mime = NULL;
800 if ((mime = eina_list_search_unsorted(globs, EINA_COMPARE_CB(strcmp), glob)))
802 globs = eina_list_remove(globs, mime);
803 IF_RELEASE(mime->glob);
804 IF_RELEASE(mime->mime);
812 static inline const char *
813 efreet_eat_space(const char *head, const Eina_File_Line *ln, Eina_Bool not)
817 while (!isspace(*head) && (head < ln->end))
822 while (isspace(*head) && (head < ln->end))
831 * @param file mime.types file to load
832 * @return Returns no value
833 * @brief Loads values from a mime.types style file
834 * into the globs list.
836 * application/msaccess mdb
837 * application/msword doc dot
840 efreet_mime_mime_types_load(const char *file)
842 const Eina_File_Line *ln;
845 const char *head_line;
846 const char *word_start;
847 const char *mimetype;
849 f = eina_file_open(file, 0);
852 it = eina_file_map_lines(f);
857 ext = eina_strbuf_new();
859 EINA_ITERATOR_FOREACH(it, ln)
861 head_line = efreet_eat_space(ln->start, ln, EINA_FALSE);
862 if (head_line == ln->end) continue ;
864 if (*head_line == '#') continue ;
866 word_start = head_line;
867 head_line = efreet_eat_space(head_line, ln, EINA_TRUE);
869 if (head_line == ln->end) continue ;
870 mimetype = eina_stringshare_add_length(word_start, head_line - word_start);
873 head_line = efreet_eat_space(head_line, ln, EINA_FALSE);
874 if (head_line == ln->end) break ;
876 word_start = head_line;
877 head_line = efreet_eat_space(head_line, ln, EINA_TRUE);
879 eina_strbuf_append_length(ext, word_start, head_line - word_start);
882 eina_strbuf_string_get(ext),
885 eina_strbuf_string_get(ext),
886 eina_stringshare_ref(mimetype));
888 eina_strbuf_reset(ext);
890 while (head_line < ln->end);
892 eina_stringshare_del(mimetype);
895 eina_strbuf_free(ext);
896 eina_iterator_free(it);
903 * @param file globs file to load
904 * @return Returns no value
905 * @brief Loads values from a mime.types style file
906 * into the globs list.
908 * text/vnd.wap.wml:*.wml
909 * application/x-7z-compressed:*.7z
910 * application/vnd.corel-draw:*.cdr
911 * text/spreadsheet:*.sylk
914 efreet_mime_shared_mimeinfo_globs_load(const char *file)
917 char buf[4096], mimetype[4096], ext[4096], *p, *pp;
918 Efreet_Mime_Glob *mime = NULL;
920 f = fopen(file, "rb");
923 while (fgets(buf, sizeof(buf), f))
926 while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
928 if (*p == '#') continue;
929 if ((*p == '\n') || (*p == 0)) continue;
932 while ((*p != ':') && (*p != 0) && (*p != '\n')) p++;
934 if ((*p == '\n') || (*p == 0)) continue;
935 strncpy(mimetype, pp, (p - pp));
936 mimetype[p - pp] = 0;
940 while ((*p != 0) && (*p != '\n'))
949 if (ext[0] == '*' && ext[1] == '.')
951 eina_hash_del(wild, &(ext[2]), NULL);
952 eina_hash_add(wild, &(ext[2]),
953 (void*)eina_stringshare_add(mimetype));
957 mime = NEW(Efreet_Mime_Glob, 1);
960 mime->mime = eina_stringshare_add(mimetype);
961 mime->glob = eina_stringshare_add(ext);
962 if ((!mime->mime) || (!mime->glob))
964 IF_RELEASE(mime->mime);
965 IF_RELEASE(mime->glob);
970 efreet_mime_glob_remove(ext);
971 globs = eina_list_append(globs, mime);
982 * @param in Number to count the digits
983 * @return Returns number of digits
984 * @brief Calculates and returns the number of digits
988 efreet_mime_count_digits(int in)
992 if (j < 10) return 1;
993 while ((j /= 10) > 0) ++i;
1000 * @param file File to parse
1001 * @return Returns no value
1002 * @brief Loads a magic file and adds information to magics list
1005 efreet_mime_shared_mimeinfo_magic_load(const char *file)
1008 char *data = (void *)-1;
1012 size = ecore_file_size(file);
1013 if (size <= 0) return;
1015 fd = open(file, O_RDONLY);
1016 if (fd == -1) return;
1018 /* let's make mmap safe and just get 0 pages for IO erro */
1019 eina_mmap_safety_enabled_set(EINA_TRUE);
1021 data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
1022 if (data == MAP_FAILED)
1028 efreet_mime_shared_mimeinfo_magic_parse(data, size);
1035 * @param data The data from the file
1036 * @return Returns no value
1037 * @brief Parses a magic file
1040 * ----------------------------------------------------------------------
1042 * ----------------------------------------------------------------------
1043 * |4D 49 4D 45 2D 4D 61 67 69 63 00 0A 5B 39 30 3A | MIME-Magic..[90: |
1044 * |61 70 70 6C 69 63 61 74 69 6F 6E 2F 64 6F 63 62 | application/docb |
1045 * |6F 6F 6B 2B 78 6D 6C 5D 0A 3E 30 3D 00 05 3C 3F | ook+xml].>0=..<? |
1046 * |78 6D 6C 0A 31 3E 30 3D 00 19 2D 2F 2F 4F 41 53 | xml.1>0=..-//OAS |
1047 * |49 53 2F 2F 44 54 44 20 44 6F 63 42 6F 6F 6B 20 | IS//DTD DocBook |
1048 * |58 4D 4C 2B 31 30 31 0A 31 3E 30 3D 00 17 2D 2F | XML+101.1>0=..-/ |
1049 * ----------------------------------------------------------------------
1052 * The nesting depth of the rule, corresponding to the number of '>'
1053 * characters in the traditional file format.
1055 * The offset into the file to look for a match.
1057 * Two bytes giving the (big-endian) length of the value, followed by the
1060 * The mask, which (if present) is exactly the same length as the value.
1062 * On little-endian machines, the size of each group to byte-swap.
1064 * The length of the region in the file to check.
1066 * The indent, range-length, word-size and mask components are optional.
1067 * If missing, indent defaults to 0, range-length to 1, the word-size to 1,
1068 * and the mask to all 'one' bits. In our case, mask is null as it is
1069 * quicker, uses less memory and will achieve the same exact effect.
1072 efreet_mime_shared_mimeinfo_magic_parse(char *data, int size)
1074 Efreet_Mime_Magic *mime = NULL;
1075 Efreet_Mime_Magic_Entry *entry = NULL;
1080 /* make sure we're a magic file */
1081 if (!ptr || (size < 12) || strncmp(ptr, "MIME-Magic\0\n", 12))
1086 for (; (ptr - data) < size; )
1090 char *val, buf[512];
1092 mime = NEW(Efreet_Mime_Magic, 1);
1093 magics = eina_list_append(magics, mime);
1096 while ((*val != ':')) val++;
1097 memcpy(&buf, ptr, val - ptr);
1098 buf[val - ptr] = '\0';
1100 mime->priority = atoi(buf);
1103 while ((*val != ']')) val++;
1104 memcpy(&buf, ptr, val - ptr);
1105 buf[val - ptr] = '\0';
1107 mime->mime = eina_stringshare_add(buf);
1110 while (*ptr != '\n') ptr++;
1117 if (!mime) continue;
1120 if (!(entry = NEW(Efreet_Mime_Magic_Entry, 1)))
1122 IF_FREE_LIST(magics, efreet_mime_magic_free);
1128 entry->value_len = 0;
1129 entry->word_size = 1;
1130 entry->range_len = 1;
1132 entry->value = NULL;
1134 mime->entries = eina_list_append(mime->entries, entry);
1141 entry->offset = atoi(ptr);
1142 ptr += efreet_mime_count_digits(entry->offset);
1149 memcpy(&tshort, ptr, sizeof(short));
1150 entry->value_len = ntohs(tshort);
1153 entry->value = NEW(1, entry->value_len);
1154 memcpy(entry->value, ptr, entry->value_len);
1155 ptr += entry->value_len;
1160 entry->mask = NEW(1, entry->value_len);
1161 memcpy(entry->mask, ptr, entry->value_len);
1162 ptr += entry->value_len;
1167 entry->word_size = atoi(ptr);
1168 if ((entry->word_size != 0) && (((entry->word_size != 1)
1169 && (entry->word_size != 2)
1170 && (entry->word_size != 4))
1171 || (entry->value_len % entry->word_size)))
1173 /* Invalid, Destroy */
1178 while (*ptr != '\n') ptr++;
1182 if (efreet_mime_endianess == EFREET_ENDIAN_LITTLE)
1186 for (j = 0; j < entry->value_len; j += entry->word_size)
1188 if (entry->word_size == 2)
1190 ((short*)entry->value)[j] =
1191 ntohs(((short*)entry->value)[j]);
1194 ((short*)entry->mask)[j] =
1195 ntohs(((short*)entry->mask)[j]);
1197 else if (entry->word_size == 4)
1199 ((int*)entry->value)[j] =
1200 ntohl(((int*)entry->value)[j]);
1203 ((int*)entry->mask)[j] =
1204 ntohl(((int*)entry->mask)[j]);
1209 ptr += efreet_mime_count_digits(entry->word_size);
1214 entry->range_len = atoi(ptr);
1215 ptr += efreet_mime_count_digits(entry->range_len);
1226 entry->indent = atoi(ptr);
1227 ptr += efreet_mime_count_digits(entry->indent);
1236 IF_FREE(entry->value);
1237 IF_FREE(entry->mask);
1245 * @param file File to check
1246 * @param start Start priority, if 0 start at beginning
1247 * @param end End priority, should be less then start
1249 * @return Returns mime type for file if found, NULL if not
1250 * @brief Applies magic rules to a file given a start and end priority
1253 efreet_mime_magic_check_priority(const char *file,
1257 Efreet_Mime_Magic *m = NULL;
1258 Efreet_Mime_Magic_Entry *e = NULL;
1261 unsigned int i = 0, offset = 0,level = 0, match = 0, bytes_read = 0;
1262 const char *last_mime = NULL;
1263 char c, v, buf[EFREET_MIME_MAGIC_BUFFER_SIZE];
1265 f = fopen(file, "rb");
1266 if (!f) return NULL;
1274 if ((bytes_read = fread(buf, 1, sizeof(buf), f)) == 0)
1280 EINA_LIST_FOREACH(magics, l, m)
1282 if ((start != 0) && (m->priority > start))
1285 if (m->priority < end)
1288 EINA_LIST_FOREACH(m->entries, ll, e)
1290 if ((level < e->indent) && !match)
1293 if ((level >= e->indent) && !match)
1296 else if ((level > e->indent) && match)
1302 for (offset = e->offset; offset < e->offset + e->range_len; offset++)
1304 if (((offset + e->value_len) > bytes_read) &&
1305 (fseek(f, offset, SEEK_SET) == -1))
1309 for (i = 0; i < e->value_len; ++i)
1311 if (offset + e->value_len > bytes_read)
1314 c = buf[offset + i];
1317 if (e->mask) v &= e->mask[i];
1329 last_mime = m->mime;
1348 * @param data Data pointer that is being destroyed
1349 * @return Returns no value
1350 * @brief Callback for globs destroy
1353 efreet_mime_glob_free(void *data)
1355 Efreet_Mime_Glob *m = data;
1357 IF_RELEASE(m->mime);
1358 IF_RELEASE(m->glob);
1364 * @param data Data pointer that is being destroyed
1365 * @return Returns no value
1366 * @brief Callback for magics destroy
1369 efreet_mime_magic_free(void *data)
1371 Efreet_Mime_Magic *m = data;
1373 IF_RELEASE(m->mime);
1374 IF_FREE_LIST(m->entries, efreet_mime_magic_entry_free);
1380 * @param data Data pointer that is being destroyed
1381 * @return Returns no value
1382 * @brief Callback for magic entry destroy
1385 efreet_mime_magic_entry_free(void *data)
1387 Efreet_Mime_Magic_Entry *e = data;
1397 * @param str String (filename) to match
1398 * @param glob Glob to match str to
1399 * @return Returns 1 on success, 0 on failure
1400 * @brief Compares str to glob, case sensitive
1403 efreet_mime_glob_match(const char *str, const char *glob)
1405 if (!str || !glob) return 0;
1408 if (str[0] == 0) return 1;
1411 if (!fnmatch(glob, str, 0)) return 1;
1417 * @param str String (filename) to match
1418 * @param glob Glob to match str to
1419 * @return Returns 1 on success, 0 on failure
1420 * @brief Compares str to glob, case insensitive (expects str already in lower case)
1423 efreet_mime_glob_case_match(char *str, const char *glob)
1428 if (!str || !glob) return 0;
1431 if (str[0] == 0) return 1;
1434 tglob = alloca(strlen(glob) + 1);
1435 for (tp = tglob, p = glob; *p; p++, tp++) *tp = tolower(*p);
1437 if (!fnmatch(str, tglob, 0)) return 1;
1442 efreet_mime_icons_flush(double now)
1445 static double old = 0;
1448 if (now - old < EFREET_MIME_ICONS_FLUSH_TIMEOUT)
1452 todo = eina_hash_population(mime_icons) - EFREET_MIME_ICONS_MAX_POPULATION;
1456 l = mime_icons_lru->last; /* mime_icons_lru is not NULL, since todo > 0 */
1457 for (; todo > 0; todo--)
1459 Efreet_Mime_Icon_Entry_Head *entry = (Efreet_Mime_Icon_Entry_Head *)l;
1460 Eina_Inlist *prev = l->prev;
1462 mime_icons_lru = eina_inlist_remove(mime_icons_lru, l);
1463 eina_hash_del_by_key(mime_icons, entry->mime);
1467 efreet_mime_icons_debug();
1471 efreet_mime_icon_entry_free(Efreet_Mime_Icon_Entry *node)
1473 eina_stringshare_del(node->icon);
1474 eina_stringshare_del(node->theme);
1479 efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry)
1483 Efreet_Mime_Icon_Entry *n = (Efreet_Mime_Icon_Entry *)entry->list;
1484 entry->list = eina_inlist_remove(entry->list, entry->list);
1485 efreet_mime_icon_entry_free(n);
1488 eina_stringshare_del(entry->mime);
1492 static Efreet_Mime_Icon_Entry *
1493 efreet_mime_icon_entry_new(const char *icon,
1497 Efreet_Mime_Icon_Entry *entry;
1499 entry = malloc(sizeof(*entry));
1504 entry->theme = theme;
1511 efreet_mime_icon_entry_add(const char *mime,
1516 Efreet_Mime_Icon_Entry_Head *entry;
1517 Efreet_Mime_Icon_Entry *n;
1519 n = efreet_mime_icon_entry_new(icon, theme, size);
1522 entry = eina_hash_find(mime_icons, mime);
1528 l = EINA_INLIST_GET(n);
1529 entry->list = eina_inlist_prepend(entry->list, l);
1531 l = EINA_INLIST_GET(entry);
1532 mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
1538 entry = malloc(sizeof(*entry));
1541 efreet_mime_icon_entry_free(n);
1545 l = EINA_INLIST_GET(n);
1546 entry->list = eina_inlist_prepend(NULL, l);
1548 eina_hash_direct_add(mime_icons, mime, entry);
1550 l = EINA_INLIST_GET(entry);
1551 mime_icons_lru = eina_inlist_prepend(mime_icons_lru, l);
1554 entry->timestamp = ecore_loop_time_get();
1555 efreet_mime_icons_flush(entry->timestamp);
1559 efreet_mime_icon_entry_find(const char *mime,
1563 Efreet_Mime_Icon_Entry_Head *entry;
1564 Efreet_Mime_Icon_Entry *n;
1566 entry = eina_hash_find(mime_icons, mime);
1570 EINA_INLIST_FOREACH(entry->list, n)
1572 if ((n->theme == theme) && (n->size == size))
1576 l = EINA_INLIST_GET(n);
1577 if (entry->list != l)
1578 entry->list = eina_inlist_promote(entry->list, l);
1580 l = EINA_INLIST_GET(entry);
1581 if (mime_icons_lru != l)
1582 mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
1584 entry->timestamp = ecore_loop_time_get();
1592 #ifdef EFREET_MIME_ICONS_DEBUG
1594 efreet_mime_icons_debug(void)
1596 double now = ecore_loop_time_get();
1597 Efreet_Mime_Icon_Entry_Head *entry;
1598 EINA_INLIST_FOREACH(mime_icons_lru, entry)
1600 Efreet_Mime_Icon_Entry *n;
1603 (now - entry->timestamp >= EFREET_MIME_ICONS_EXPIRE_TIMEOUT))
1605 puts("*** FOLLOWING ENTRIES ARE AGED AND CAN BE EXPIRED ***");
1609 DBG("mime-icon entry: '%s' last used: %s",
1610 entry->mime, ctime(&entry->timestamp));
1612 EINA_INLIST_FOREACH(entry->list, n)
1613 DBG("\tsize: %3u theme: '%s' icon: '%s'",
1614 n->theme, n->size, n->icon);
1619 efreet_mime_icons_debug(void)