5 #include "efreet_alloca.h"
15 # include <winsock2.h>
18 #ifdef HAVE_NETINET_IN_H
19 # include <netinet/in.h>
22 #ifdef HAVE_ARPA_INET_H
23 # include <arpa/inet.h>
27 #include <Ecore_File.h>
29 /* define macros and variable for using the eina logging system */
30 #define EFREET_MODULE_LOG_DOM _efreet_mime_log_dom
31 static int _efreet_mime_log_dom = -1;
34 #include "Efreet_Mime.h"
35 #include "efreet_private.h"
37 static Eina_List *globs = NULL; /* contains Efreet_Mime_Glob structs */
38 static Eina_List *magics = NULL; /* contains Efreet_Mime_Magic structs */
39 static Eina_Hash *wild = NULL; /* contains *.ext and mime.types globs*/
40 static Eina_Hash *monitors = NULL; /* contains file monitors */
41 static Eina_Hash *mime_icons = NULL; /* contains cache with mime->icons */
42 static Eina_Inlist *mime_icons_lru = NULL;
43 static unsigned int _efreet_mime_init_count = 0;
45 static const char *_mime_inode_symlink = NULL;
46 static const char *_mime_inode_fifo = NULL;
47 static const char *_mime_inode_chardevice = NULL;
48 static const char *_mime_inode_blockdevice = NULL;
49 static const char *_mime_inode_socket = NULL;
50 static const char *_mime_inode_mountpoint = NULL;
51 static const char *_mime_inode_directory = NULL;
52 static const char *_mime_application_x_executable = NULL;
53 static const char *_mime_application_octet_stream = NULL;
54 static const char *_mime_text_plain = NULL;
58 * @brief Holds whether we are big/little endian
59 * @note This is set during efreet_mime_init based on
64 EFREET_ENDIAN_BIG = 0,
65 EFREET_ENDIAN_LITTLE = 1
66 } efreet_mime_endianess = EFREET_ENDIAN_BIG;
69 * Buffer sized used for magic checks. The default is good enough for the
70 * current set of magic rules. This setting is only here for the future.
72 #define EFREET_MIME_MAGIC_BUFFER_SIZE 512
75 * Minimum timeout in seconds between mime-icons cache flush.
77 #define EFREET_MIME_ICONS_FLUSH_TIMEOUT 60
80 * Timeout in seconds, when older mime-icons items are expired.
82 #define EFREET_MIME_ICONS_EXPIRE_TIMEOUT 600
85 * mime-icons maximum population.
87 #define EFREET_MIME_ICONS_MAX_POPULATION 512
90 * If defined, dump mime-icons statistics after flush.
92 //#define EFREET_MIME_ICONS_DEBUG
94 typedef struct Efreet_Mime_Glob Efreet_Mime_Glob;
95 struct Efreet_Mime_Glob
101 typedef struct Efreet_Mime_Magic Efreet_Mime_Magic;
102 struct Efreet_Mime_Magic
104 unsigned int priority;
109 typedef struct Efreet_Mime_Magic_Entry Efreet_Mime_Magic_Entry;
110 struct Efreet_Mime_Magic_Entry
114 unsigned int word_size;
115 unsigned int range_len;
116 unsigned short value_len;
121 typedef struct Efreet_Mime_Icon_Entry_Head Efreet_Mime_Icon_Entry_Head;
122 struct Efreet_Mime_Icon_Entry_Head
124 EINA_INLIST; /* node of mime_icons_lru */
130 typedef struct Efreet_Mime_Icon_Entry Efreet_Mime_Icon_Entry;
131 struct Efreet_Mime_Icon_Entry
139 static int efreet_mime_glob_remove(const char *glob);
140 static void efreet_mime_mime_types_load(const char *file);
141 static void efreet_mime_shared_mimeinfo_globs_load(const char *file);
142 static void efreet_mime_shared_mimeinfo_magic_load(const char *file);
143 static void efreet_mime_shared_mimeinfo_magic_parse(char *data, int size);
144 static const char *efreet_mime_magic_check_priority(const char *file,
147 static int efreet_mime_init_files(void);
148 static const char *efreet_mime_special_check(const char *file);
149 static const char *efreet_mime_fallback_check(const char *file);
150 static void efreet_mime_glob_free(void *data);
151 static void efreet_mime_magic_free(void *data);
152 static void efreet_mime_magic_entry_free(void *data);
153 static int efreet_mime_glob_match(const char *str, const char *glob);
154 static int efreet_mime_glob_case_match(char *str, const char *glob);
155 static int efreet_mime_endian_check(void);
157 static void efreet_mime_monitor_add(const char *file);
158 static void efreet_mime_cb_update_file(void *data,
159 Ecore_File_Monitor *monitor,
160 Ecore_File_Event event,
163 static void efreet_mime_icons_flush(double now);
164 static void efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry);
165 static void efreet_mime_icon_entry_add(const char *mime,
169 static const char *efreet_mime_icon_entry_find(const char *mime,
172 static void efreet_mime_icons_debug(void);
175 efreet_mime_init(void)
177 if (++_efreet_mime_init_count != 1)
178 return _efreet_mime_init_count;
181 return --_efreet_mime_init_count;
183 if (!ecore_file_init())
187 goto shutdown_ecore_file;
189 _efreet_mime_log_dom = eina_log_domain_register
190 ("efreet_mime", EFREET_DEFAULT_LOG_COLOR);
192 if (_efreet_mime_log_dom < 0)
194 EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_mime.");
195 goto shutdown_efreet;
198 efreet_mime_endianess = efreet_mime_endian_check();
200 monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
202 efreet_mime_type_cache_clear();
204 if (!efreet_mime_init_files())
205 goto unregister_log_domain;
207 return _efreet_mime_init_count;
209 unregister_log_domain:
210 eina_log_domain_unregister(_efreet_mime_log_dom);
211 _efreet_mime_log_dom = -1;
215 ecore_file_shutdown();
219 return --_efreet_mime_init_count;
223 efreet_mime_shutdown(void)
225 if (--_efreet_mime_init_count != 0)
226 return _efreet_mime_init_count;
228 efreet_mime_icons_debug();
230 IF_RELEASE(_mime_inode_symlink);
231 IF_RELEASE(_mime_inode_fifo);
232 IF_RELEASE(_mime_inode_chardevice);
233 IF_RELEASE(_mime_inode_blockdevice);
234 IF_RELEASE(_mime_inode_socket);
235 IF_RELEASE(_mime_inode_mountpoint);
236 IF_RELEASE(_mime_inode_directory);
237 IF_RELEASE(_mime_application_x_executable);
238 IF_RELEASE(_mime_application_octet_stream);
239 IF_RELEASE(_mime_text_plain);
241 IF_FREE_LIST(globs, efreet_mime_glob_free);
242 IF_FREE_LIST(magics, efreet_mime_magic_free);
243 IF_FREE_HASH(monitors);
245 IF_FREE_HASH(mime_icons);
246 eina_log_domain_unregister(_efreet_mime_log_dom);
247 _efreet_mime_log_dom = -1;
249 ecore_file_shutdown();
252 return _efreet_mime_init_count;
256 efreet_mime_type_get(const char *file)
258 const char *type = NULL;
260 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
262 if ((type = efreet_mime_special_check(file)))
265 /* Check magics with priority > 80 */
266 if ((type = efreet_mime_magic_check_priority(file, 0, 80)))
270 if ((type = efreet_mime_globs_type_get(file)))
273 /* Check rest of magics */
274 if ((type = efreet_mime_magic_check_priority(file, 80, 0)))
277 return efreet_mime_fallback_check(file);
281 efreet_mime_type_icon_get(const char *mime, const char *theme, unsigned int size)
283 const char *icon = NULL;
285 Eina_List *icons = NULL;
286 const char *env = NULL;
287 char *p = NULL, *pp = NULL, *ppp = NULL;
291 EINA_SAFETY_ON_NULL_RETURN_VAL(mime, NULL);
292 EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
294 mime = eina_stringshare_add(mime);
295 theme = eina_stringshare_add(theme);
296 cache = efreet_mime_icon_entry_find(mime, theme, size);
299 eina_stringshare_del(mime);
300 eina_stringshare_del(theme);
304 /* Standard icon name */
309 if (*pp == '/') *pp = '-';
312 icons = eina_list_append(icons, p);
314 /* Environment Based icon names */
315 if ((env = efreet_desktop_environment_get()))
317 snprintf(buf, sizeof(buf), "%s-mime-%s", env, p);
318 icons = eina_list_append(icons, strdup(buf));
320 snprintf(buf, sizeof(buf), "%s-%s", env, p);
321 icons = eina_list_append(icons, strdup(buf));
324 /* Mime prefixed icon names */
325 snprintf(buf, sizeof(buf), "mime-%s", p);
326 icons = eina_list_append(icons, strdup(buf));
330 while ((ppp = strrchr(pp, '-')))
334 snprintf(buf, sizeof(buf), "%s-x-generic", pp);
335 icons = eina_list_append(icons, strdup(buf));
337 snprintf(buf, sizeof(buf), "%s-generic", pp);
338 icons = eina_list_append(icons, strdup(buf));
340 snprintf(buf, sizeof(buf), "%s", pp);
341 icons = eina_list_append(icons, strdup(buf));
345 /* Search for icons using list */
346 icon = efreet_icon_list_find(theme, icons, size);
349 data = eina_list_data_get(icons);
351 icons = eina_list_remove_list(icons, icons);
354 efreet_mime_icon_entry_add(mime, eina_stringshare_add(icon), theme, size);
360 efreet_mime_type_cache_clear(void)
364 eina_hash_free(mime_icons);
365 mime_icons_lru = NULL;
367 mime_icons = eina_hash_stringshared_new(EINA_FREE_CB(efreet_mime_icon_entry_head_free));
371 efreet_mime_type_cache_flush(void)
373 efreet_mime_icons_flush(ecore_loop_time_get());
378 efreet_mime_magic_type_get(const char *file)
380 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
381 return efreet_mime_magic_check_priority(file, 0, 0);
385 efreet_mime_globs_type_get(const char *file)
393 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
395 /* Check in the extension hash for the type */
396 ext = strchr(file, '.');
399 sl = alloca(strlen(ext) + 1);
400 for (s = ext, p = sl; *s; s++, p++) *p = tolower(*s);
406 if (p && (mime = eina_hash_find(wild, p))) return mime;
411 /* Fallback to the other globs if not found */
412 EINA_LIST_FOREACH(globs, l, g)
414 if (efreet_mime_glob_match(file, g->glob))
418 ext = alloca(strlen(file) + 1);
419 for (s = file, p = ext; *s; s++, p++) *p = tolower(*s);
421 EINA_LIST_FOREACH(globs, l, g)
423 if (efreet_mime_glob_case_match(ext, g->glob))
430 efreet_mime_special_type_get(const char *file)
432 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
433 return efreet_mime_special_check(file);
437 efreet_mime_fallback_type_get(const char *file)
439 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
440 return efreet_mime_fallback_check(file);
445 * @return Returns the endianess
446 * @brief Retreive the endianess of the machine
449 efreet_mime_endian_check(void)
452 return (*((char*)(&test)));
457 * @param file File to monitor
458 * @return Returns no value.
459 * @brief Creates a new file monitor if we aren't already monitoring the
463 efreet_mime_monitor_add(const char *file)
465 Ecore_File_Monitor *fm = NULL;
467 /* if this is already in our hash then we're already monitoring so no
468 * reason to re-monitor */
469 if (eina_hash_find(monitors, file))
472 if ((fm = ecore_file_monitor_add(file, efreet_mime_cb_update_file, NULL)))
474 eina_hash_del(monitors, file, NULL);
475 eina_hash_add(monitors, file, fm);
481 * @param datadirs List of XDG data dirs
482 * @param datahome Path to XDG data home directory
483 * @return Returns no value
484 * @brief Read all glob files in XDG data/home dirs.
485 * Also reads the /etc/mime.types file.
488 efreet_mime_load_globs(Eina_List *datadirs, const char *datahome)
492 const char *datadir = NULL;
495 wild = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
498 efreet_mime_glob_free(eina_list_data_get(globs));
499 globs = eina_list_remove_list(globs, globs);
503 * This is here for legacy reasons. It is mentioned briefly
504 * in the spec and seems to still be quite valid. It is
505 * loaded first so the globs files will override anything
508 efreet_mime_mime_types_load("/etc/mime.types");
511 snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
512 efreet_mime_shared_mimeinfo_globs_load(buf);
514 EINA_LIST_FOREACH(datadirs, l, datadir)
516 snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
517 efreet_mime_shared_mimeinfo_globs_load(buf);
523 * @param datadirs List of XDG data dirs
524 * @param datahome Path to XDG data home directory
525 * @return Returns no value
526 * @brief Read all magic files in XDG data/home dirs.
529 efreet_mime_load_magics(Eina_List *datadirs, const char *datahome)
533 const char *datadir = NULL;
537 efreet_mime_magic_free(eina_list_data_get(magics));
538 magics = eina_list_remove_list(magics, magics);
542 snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
543 efreet_mime_shared_mimeinfo_magic_load(buf);
545 EINA_LIST_FOREACH(datadirs, l, datadir)
547 snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
548 efreet_mime_shared_mimeinfo_magic_load(buf);
554 * @param data Data pointer passed to monitor_add
555 * @param monitor Ecore_File_Monitor associated with this event
556 * @param event The type of event
557 * @param path Path to the file that was updated
558 * @return Returns no value
559 * @brief Callback for all file monitors. Just reloads the appropriate
560 * list depending on which file changed. If it was a magic file
561 * only the magic list is updated. If it was a glob file or /etc/mime.types,
562 * the globs are updated.
565 efreet_mime_cb_update_file(void *data __UNUSED__,
566 Ecore_File_Monitor *monitor __UNUSED__,
567 Ecore_File_Event event __UNUSED__,
570 Eina_List *datadirs = NULL;
571 const char *datahome = NULL;
573 if (!(datahome = efreet_data_home_get()))
576 if (!(datadirs = efreet_data_dirs_get()))
579 if (strstr(path, "magic"))
580 efreet_mime_load_magics(datadirs, datahome);
582 efreet_mime_load_globs(datadirs, datahome);
587 * @param datadirs List of XDG data dirs
588 * @param datahome Path to XDG data home directory
589 * @return Returns 1 on success, 0 on failure
590 * @brief Initializes globs, magics, and monitors lists.
593 efreet_mime_init_files(void)
596 Eina_List *datadirs = NULL;
598 const char *datahome, *datadir = NULL;
600 if (!(datahome = efreet_data_home_get()))
603 if (!(datadirs = efreet_data_dirs_get()))
607 * Add our file monitors
608 * We watch the directories so we can watch for new files
611 snprintf(buf, sizeof(buf), "%s/mime", datadir);
612 efreet_mime_monitor_add(buf);
614 EINA_LIST_FOREACH(datadirs, l, datadir)
616 snprintf(buf, sizeof(buf), "%s/mime", datadir);
617 efreet_mime_monitor_add(buf);
619 efreet_mime_monitor_add("/etc/mime.types");
621 /* Load our mime information */
622 efreet_mime_load_globs(datadirs, datahome);
623 efreet_mime_load_magics(datadirs, datahome);
625 _mime_inode_symlink = eina_stringshare_add("inode/symlink");
626 _mime_inode_fifo = eina_stringshare_add("inode/fifo");
627 _mime_inode_chardevice = eina_stringshare_add("inode/chardevice");
628 _mime_inode_blockdevice = eina_stringshare_add("inode/blockdevice");
629 _mime_inode_socket = eina_stringshare_add("inode/socket");
630 _mime_inode_mountpoint = eina_stringshare_add("inode/mountpoint");
631 _mime_inode_directory = eina_stringshare_add("inode/directory");
632 _mime_application_x_executable = eina_stringshare_add("application/x-executable");
633 _mime_application_octet_stream = eina_stringshare_add("application/octet-stream");
634 _mime_text_plain = eina_stringshare_add("text/plain");
641 * @param file File to examine
642 * @return Returns mime type if special file, else NULL
643 * @brief Returns a mime type based on the stat of a file.
644 * This is used first to catch directories and other special
645 * files. A NULL return doesn't necessarily mean failure, but
646 * can also mean the file is regular.
647 * @note Mapping of file types to mime types:
648 * Stat Macro File Type Mime Type
649 * S_IFREG regular NULL
650 * S_IFIFO named pipe (fifo) inode/fifo
651 * S_IFCHR character special inode/chardevice
652 * S_IFDIR directory inode/directory
653 * S_IFBLK block special inode/blockdevice
654 * S_IFLNK symbolic link inode/symlink
655 * S_IFSOCK socket inode/socket
657 * This function can also return inode/mount-point.
658 * This is calculated by comparing the st_dev of the directory
659 * against that of it's parent directory. If they differ it
660 * is considered a mount point.
663 efreet_mime_special_check(const char *file)
668 /* no link on Windows < Vista */
672 if (!lstat(file, &s))
675 if (S_ISREG(s.st_mode))
679 if (S_ISLNK(s.st_mode))
680 return _mime_inode_symlink;
683 if (S_ISFIFO(s.st_mode))
684 return _mime_inode_fifo;
686 if (S_ISCHR(s.st_mode))
687 return _mime_inode_chardevice;
689 if (S_ISBLK(s.st_mode))
690 return _mime_inode_blockdevice;
693 if (S_ISSOCK(s.st_mode))
694 return _mime_inode_socket;
697 if (S_ISDIR(s.st_mode))
700 char parent[PATH_MAX];
703 strncpy(path, file, PATH_MAX);
705 path_len = strlen(file);
706 strncpy(parent, path, PATH_MAX);
708 /* Kill any trailing slash */
709 parent[--path_len] = '\0';
711 /* Truncate to last slash */
712 while (parent[--path_len] != '/') parent[path_len] = '\0';
715 if (!stat(file, &s2))
717 if (!lstat(parent, &s2))
720 if (s.st_dev != s2.st_dev)
721 return _mime_inode_mountpoint;
724 return _mime_inode_directory;
735 * @param file File to examine
736 * @return Returns mime type or NULL if the file doesn't exist
737 * @brief Returns text/plain if the file appears to contain text and
738 * returns application/octet-stream if it appears to be binary.
741 efreet_mime_fallback_check(const char *file)
747 if (ecore_file_can_exec(file))
748 return _mime_application_x_executable;
750 if (!(f = fopen(file, "r"))) return NULL;
752 i = fread(buf, 1, sizeof(buf), f);
755 if (i == 0) return _mime_application_octet_stream;
758 * Check for ASCII control characters in the first 32 bytes.
759 * Line Feeds, carriage returns, and tabs are ignored as they are
760 * quite common in text files in the first 32 chars.
762 for (i -= 1; i >= 0; --i)
764 if ((buf[i] < 0x20) &&
765 (buf[i] != '\n') && /* Line Feed */
766 (buf[i] != '\r') && /* Carriage Return */
767 (buf[i] != '\t')) /* Tab */
768 return _mime_application_octet_stream;
771 return _mime_text_plain;
776 * @param glob Glob to search for
777 * @return Returns 1 on success, 0 on failure
778 * @brief Removes a glob from the list
781 efreet_mime_glob_remove(const char *glob)
783 Efreet_Mime_Glob *mime = NULL;
785 if ((mime = eina_list_search_unsorted(globs, EINA_COMPARE_CB(strcmp), glob)))
787 globs = eina_list_remove(globs, mime);
788 IF_RELEASE(mime->glob);
789 IF_RELEASE(mime->mime);
797 static inline const char *
798 efreet_eat_space(const char *head, const Eina_File_Line *ln, Eina_Bool not)
802 while (!isspace(*head) && (head < ln->end))
807 while (isspace(*head) && (head < ln->end))
816 * @param file mime.types file to load
817 * @return Returns no value
818 * @brief Loads values from a mime.types style file
819 * into the globs list.
821 * application/msaccess mdb
822 * application/msword doc dot
825 efreet_mime_mime_types_load(const char *file)
827 const Eina_File_Line *ln;
830 const char *head_line;
831 const char *word_start;
832 const char *mimetype;
834 f = eina_file_open(file, 0);
837 it = eina_file_map_lines(f);
842 ext = eina_strbuf_new();
844 EINA_ITERATOR_FOREACH(it, ln)
846 head_line = efreet_eat_space(ln->start, ln, EINA_FALSE);
847 if (head_line == ln->end) continue ;
849 if (*head_line == '#') continue ;
851 word_start = head_line;
852 head_line = efreet_eat_space(head_line, ln, EINA_TRUE);
854 if (head_line == ln->end) continue ;
855 mimetype = eina_stringshare_add_length(word_start, head_line - word_start);
858 head_line = efreet_eat_space(head_line, ln, EINA_FALSE);
859 if (head_line == ln->end) break ;
861 word_start = head_line;
862 head_line = efreet_eat_space(head_line, ln, EINA_TRUE);
864 eina_strbuf_append_length(ext, word_start, head_line - word_start);
867 eina_strbuf_string_get(ext),
870 eina_strbuf_string_get(ext),
871 eina_stringshare_ref(mimetype));
873 eina_strbuf_reset(ext);
875 while (head_line < ln->end);
877 eina_stringshare_del(mimetype);
880 eina_strbuf_free(ext);
881 eina_iterator_free(it);
888 * @param file globs file to load
889 * @return Returns no value
890 * @brief Loads values from a mime.types style file
891 * into the globs list.
893 * text/vnd.wap.wml:*.wml
894 * application/x-7z-compressed:*.7z
895 * application/vnd.corel-draw:*.cdr
896 * text/spreadsheet:*.sylk
899 efreet_mime_shared_mimeinfo_globs_load(const char *file)
902 char buf[4096], mimetype[4096], ext[4096], *p, *pp;
903 Efreet_Mime_Glob *mime = NULL;
905 f = fopen(file, "rb");
908 while (fgets(buf, sizeof(buf), f))
911 while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
913 if (*p == '#') continue;
914 if ((*p == '\n') || (*p == 0)) continue;
917 while ((*p != ':') && (*p != 0) && (*p != '\n')) p++;
919 if ((*p == '\n') || (*p == 0)) continue;
920 strncpy(mimetype, pp, (p - pp));
921 mimetype[p - pp] = 0;
925 while ((*p != 0) && (*p != '\n'))
934 if (ext[0] == '*' && ext[1] == '.')
936 eina_hash_del(wild, &(ext[2]), NULL);
937 eina_hash_add(wild, &(ext[2]),
938 (void*)eina_stringshare_add(mimetype));
942 mime = NEW(Efreet_Mime_Glob, 1);
945 mime->mime = eina_stringshare_add(mimetype);
946 mime->glob = eina_stringshare_add(ext);
947 if ((!mime->mime) || (!mime->glob))
949 IF_RELEASE(mime->mime);
950 IF_RELEASE(mime->glob);
955 efreet_mime_glob_remove(ext);
956 globs = eina_list_append(globs, mime);
967 * @param in Number to count the digits
968 * @return Returns number of digits
969 * @brief Calculates and returns the number of digits
973 efreet_mime_count_digits(int in)
977 if (j < 10) return 1;
978 while ((j /= 10) > 0) ++i;
985 * @param file File to parse
986 * @return Returns no value
987 * @brief Loads a magic file and adds information to magics list
990 efreet_mime_shared_mimeinfo_magic_load(const char *file)
993 char *data = (void *)-1;
997 size = ecore_file_size(file);
998 if (size <= 0) return;
1000 fd = open(file, O_RDONLY);
1001 if (fd == -1) return;
1003 /* let's make mmap safe and just get 0 pages for IO erro */
1004 eina_mmap_safety_enabled_set(EINA_TRUE);
1006 data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
1007 if (data == MAP_FAILED)
1013 efreet_mime_shared_mimeinfo_magic_parse(data, size);
1020 * @param data The data from the file
1021 * @return Returns no value
1022 * @brief Parses a magic file
1025 * ----------------------------------------------------------------------
1027 * ----------------------------------------------------------------------
1028 * |4D 49 4D 45 2D 4D 61 67 69 63 00 0A 5B 39 30 3A | MIME-Magic..[90: |
1029 * |61 70 70 6C 69 63 61 74 69 6F 6E 2F 64 6F 63 62 | application/docb |
1030 * |6F 6F 6B 2B 78 6D 6C 5D 0A 3E 30 3D 00 05 3C 3F | ook+xml].>0=..<? |
1031 * |78 6D 6C 0A 31 3E 30 3D 00 19 2D 2F 2F 4F 41 53 | xml.1>0=..-//OAS |
1032 * |49 53 2F 2F 44 54 44 20 44 6F 63 42 6F 6F 6B 20 | IS//DTD DocBook |
1033 * |58 4D 4C 2B 31 30 31 0A 31 3E 30 3D 00 17 2D 2F | XML+101.1>0=..-/ |
1034 * ----------------------------------------------------------------------
1037 * The nesting depth of the rule, corresponding to the number of '>'
1038 * characters in the traditional file format.
1040 * The offset into the file to look for a match.
1042 * Two bytes giving the (big-endian) length of the value, followed by the
1045 * The mask, which (if present) is exactly the same length as the value.
1047 * On little-endian machines, the size of each group to byte-swap.
1049 * The length of the region in the file to check.
1051 * The indent, range-length, word-size and mask components are optional.
1052 * If missing, indent defaults to 0, range-length to 1, the word-size to 1,
1053 * and the mask to all 'one' bits. In our case, mask is null as it is
1054 * quicker, uses less memory and will achieve the same exact effect.
1057 efreet_mime_shared_mimeinfo_magic_parse(char *data, int size)
1059 Efreet_Mime_Magic *mime = NULL;
1060 Efreet_Mime_Magic_Entry *entry = NULL;
1065 /* make sure we're a magic file */
1066 if (!ptr || (size < 12) || strncmp(ptr, "MIME-Magic\0\n", 12))
1071 for (; (ptr - data) < size; )
1075 char *val, buf[512];
1077 mime = NEW(Efreet_Mime_Magic, 1);
1078 magics = eina_list_append(magics, mime);
1081 while ((*val != ':')) val++;
1082 memcpy(&buf, ptr, val - ptr);
1083 buf[val - ptr] = '\0';
1085 mime->priority = atoi(buf);
1088 while ((*val != ']')) val++;
1089 memcpy(&buf, ptr, val - ptr);
1090 buf[val - ptr] = '\0';
1092 mime->mime = eina_stringshare_add(buf);
1095 while (*ptr != '\n') ptr++;
1102 if (!mime) continue;
1105 if (!(entry = NEW(Efreet_Mime_Magic_Entry, 1)))
1107 IF_FREE_LIST(magics, efreet_mime_magic_free);
1113 entry->value_len = 0;
1114 entry->word_size = 1;
1115 entry->range_len = 1;
1117 entry->value = NULL;
1119 mime->entries = eina_list_append(mime->entries, entry);
1126 entry->offset = atoi(ptr);
1127 ptr += efreet_mime_count_digits(entry->offset);
1134 memcpy(&tshort, ptr, sizeof(short));
1135 entry->value_len = ntohs(tshort);
1138 entry->value = NEW(1, entry->value_len);
1139 memcpy(entry->value, ptr, entry->value_len);
1140 ptr += entry->value_len;
1145 entry->mask = NEW(1, entry->value_len);
1146 memcpy(entry->mask, ptr, entry->value_len);
1147 ptr += entry->value_len;
1152 entry->word_size = atoi(ptr);
1153 if ((entry->word_size != 0) && (((entry->word_size != 1)
1154 && (entry->word_size != 2)
1155 && (entry->word_size != 4))
1156 || (entry->value_len % entry->word_size)))
1158 /* Invalid, Destroy */
1163 while (*ptr != '\n') ptr++;
1167 if (efreet_mime_endianess == EFREET_ENDIAN_LITTLE)
1171 for (j = 0; j < entry->value_len; j += entry->word_size)
1173 if (entry->word_size == 2)
1175 ((short*)entry->value)[j] =
1176 ntohs(((short*)entry->value)[j]);
1179 ((short*)entry->mask)[j] =
1180 ntohs(((short*)entry->mask)[j]);
1182 else if (entry->word_size == 4)
1184 ((int*)entry->value)[j] =
1185 ntohl(((int*)entry->value)[j]);
1188 ((int*)entry->mask)[j] =
1189 ntohl(((int*)entry->mask)[j]);
1194 ptr += efreet_mime_count_digits(entry->word_size);
1199 entry->range_len = atoi(ptr);
1200 ptr += efreet_mime_count_digits(entry->range_len);
1211 entry->indent = atoi(ptr);
1212 ptr += efreet_mime_count_digits(entry->indent);
1221 IF_FREE(entry->value);
1222 IF_FREE(entry->mask);
1230 * @param file File to check
1231 * @param start Start priority, if 0 start at beginning
1232 * @param end End priority, should be less then start
1234 * @return Returns mime type for file if found, NULL if not
1235 * @brief Applies magic rules to a file given a start and end priority
1238 efreet_mime_magic_check_priority(const char *file,
1242 Efreet_Mime_Magic *m = NULL;
1243 Efreet_Mime_Magic_Entry *e = NULL;
1246 unsigned int i = 0, offset = 0,level = 0, match = 0, bytes_read = 0;
1247 const char *last_mime = NULL;
1248 char c, v, buf[EFREET_MIME_MAGIC_BUFFER_SIZE];
1250 f = fopen(file, "rb");
1251 if (!f) return NULL;
1259 if ((bytes_read = fread(buf, 1, sizeof(buf), f)) == 0)
1265 EINA_LIST_FOREACH(magics, l, m)
1267 if ((start != 0) && (m->priority > start))
1270 if (m->priority < end)
1273 EINA_LIST_FOREACH(m->entries, ll, e)
1275 if ((level < e->indent) && !match)
1278 if ((level >= e->indent) && !match)
1281 else if ((level > e->indent) && match)
1287 for (offset = e->offset; offset < e->offset + e->range_len; offset++)
1289 if (((offset + e->value_len) > bytes_read) &&
1290 (fseek(f, offset, SEEK_SET) == -1))
1294 for (i = 0; i < e->value_len; ++i)
1296 if (offset + e->value_len > bytes_read)
1299 c = buf[offset + i];
1302 if (e->mask) v &= e->mask[i];
1314 last_mime = m->mime;
1333 * @param data Data pointer that is being destroyed
1334 * @return Returns no value
1335 * @brief Callback for globs destroy
1338 efreet_mime_glob_free(void *data)
1340 Efreet_Mime_Glob *m = data;
1342 IF_RELEASE(m->mime);
1343 IF_RELEASE(m->glob);
1349 * @param data Data pointer that is being destroyed
1350 * @return Returns no value
1351 * @brief Callback for magics destroy
1354 efreet_mime_magic_free(void *data)
1356 Efreet_Mime_Magic *m = data;
1358 IF_RELEASE(m->mime);
1359 IF_FREE_LIST(m->entries, efreet_mime_magic_entry_free);
1365 * @param data Data pointer that is being destroyed
1366 * @return Returns no value
1367 * @brief Callback for magic entry destroy
1370 efreet_mime_magic_entry_free(void *data)
1372 Efreet_Mime_Magic_Entry *e = data;
1382 * @param str String (filename) to match
1383 * @param glob Glob to match str to
1384 * @return Returns 1 on success, 0 on failure
1385 * @brief Compares str to glob, case sensitive
1388 efreet_mime_glob_match(const char *str, const char *glob)
1390 if (!str || !glob) return 0;
1393 if (str[0] == 0) return 1;
1396 if (!fnmatch(glob, str, 0)) return 1;
1402 * @param str String (filename) to match
1403 * @param glob Glob to match str to
1404 * @return Returns 1 on success, 0 on failure
1405 * @brief Compares str to glob, case insensitive (expects str already in lower case)
1408 efreet_mime_glob_case_match(char *str, const char *glob)
1413 if (!str || !glob) return 0;
1416 if (str[0] == 0) return 1;
1419 tglob = alloca(strlen(glob) + 1);
1420 for (tp = tglob, p = glob; *p; p++, tp++) *tp = tolower(*p);
1422 if (!fnmatch(str, tglob, 0)) return 1;
1427 efreet_mime_icons_flush(double now)
1430 static double old = 0;
1433 if (now - old < EFREET_MIME_ICONS_FLUSH_TIMEOUT)
1437 todo = eina_hash_population(mime_icons) - EFREET_MIME_ICONS_MAX_POPULATION;
1441 l = mime_icons_lru->last; /* mime_icons_lru is not NULL, since todo > 0 */
1442 for (; todo > 0; todo--)
1444 Efreet_Mime_Icon_Entry_Head *entry = (Efreet_Mime_Icon_Entry_Head *)l;
1445 Eina_Inlist *prev = l->prev;
1447 mime_icons_lru = eina_inlist_remove(mime_icons_lru, l);
1448 eina_hash_del_by_key(mime_icons, entry->mime);
1452 efreet_mime_icons_debug();
1456 efreet_mime_icon_entry_free(Efreet_Mime_Icon_Entry *node)
1458 eina_stringshare_del(node->icon);
1459 eina_stringshare_del(node->theme);
1464 efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry)
1468 Efreet_Mime_Icon_Entry *n = (Efreet_Mime_Icon_Entry *)entry->list;
1469 entry->list = eina_inlist_remove(entry->list, entry->list);
1470 efreet_mime_icon_entry_free(n);
1473 eina_stringshare_del(entry->mime);
1477 static Efreet_Mime_Icon_Entry *
1478 efreet_mime_icon_entry_new(const char *icon,
1482 Efreet_Mime_Icon_Entry *entry;
1484 entry = malloc(sizeof(*entry));
1489 entry->theme = theme;
1496 efreet_mime_icon_entry_add(const char *mime,
1501 Efreet_Mime_Icon_Entry_Head *entry;
1502 Efreet_Mime_Icon_Entry *n;
1504 n = efreet_mime_icon_entry_new(icon, theme, size);
1507 entry = eina_hash_find(mime_icons, mime);
1513 l = EINA_INLIST_GET(n);
1514 entry->list = eina_inlist_prepend(entry->list, l);
1516 l = EINA_INLIST_GET(entry);
1517 mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
1523 entry = malloc(sizeof(*entry));
1526 efreet_mime_icon_entry_free(n);
1530 l = EINA_INLIST_GET(n);
1531 entry->list = eina_inlist_prepend(NULL, l);
1533 eina_hash_direct_add(mime_icons, mime, entry);
1535 l = EINA_INLIST_GET(entry);
1536 mime_icons_lru = eina_inlist_prepend(mime_icons_lru, l);
1539 entry->timestamp = ecore_loop_time_get();
1540 efreet_mime_icons_flush(entry->timestamp);
1544 efreet_mime_icon_entry_find(const char *mime,
1548 Efreet_Mime_Icon_Entry_Head *entry;
1549 Efreet_Mime_Icon_Entry *n;
1551 entry = eina_hash_find(mime_icons, mime);
1555 EINA_INLIST_FOREACH(entry->list, n)
1557 if ((n->theme == theme) && (n->size == size))
1561 l = EINA_INLIST_GET(n);
1562 if (entry->list != l)
1563 entry->list = eina_inlist_promote(entry->list, l);
1565 l = EINA_INLIST_GET(entry);
1566 if (mime_icons_lru != l)
1567 mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
1569 entry->timestamp = ecore_loop_time_get();
1577 #ifdef EFREET_MIME_ICONS_DEBUG
1579 efreet_mime_icons_debug(void)
1581 double now = ecore_loop_time_get();
1582 Efreet_Mime_Icon_Entry_Head *entry;
1583 EINA_INLIST_FOREACH(mime_icons_lru, entry)
1585 Efreet_Mime_Icon_Entry *n;
1588 (now - entry->timestamp >= EFREET_MIME_ICONS_EXPIRE_TIMEOUT))
1590 puts("*** FOLLOWING ENTRIES ARE AGED AND CAN BE EXPIRED ***");
1594 DBG("mime-icon entry: '%s' last used: %s",
1595 entry->mime, ctime(&entry->timestamp));
1597 EINA_INLIST_FOREACH(entry->list, n)
1598 DBG("\tsize: %3u theme: '%s' icon: '%s'",
1599 n->theme, n->size, n->icon);
1604 efreet_mime_icons_debug(void)