1 /* -*- mode: C; c-file-style: "gnu" -*- */ 2 /* xdgmime.c: XDG Mime Spec mime resolver. Based on version 0.11 of the spec. 3 * 4 * More info can be found at http://www.freedesktop.org/standards/ 5 * 6 * Copyright (C) 2003,2004 Red Hat, Inc. 7 * Copyright (C) 2003,2004 Jonathan Blandford <jrb (at) alum.mit.edu> 8 * 9 * Licensed under the Academic Free License version 2.0 10 * Or under the following terms: 11 * 12 * This library is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU Lesser General Public 14 * License as published by the Free Software Foundation; either 15 * version 2 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * Lesser General Public License for more details. 21 * 22 * You should have received a copy of the GNU Lesser General Public 23 * License along with this library; if not, write to the 24 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 25 * Boston, MA 02111-1307, USA. 26 */ 27 28 #ifdef HAVE_CONFIG_H 29 #include "config.h" 30 #endif 31 32 #include "xdgmime.h" 33 #include "xdgmimeint.h" 34 #include "xdgmimeglob.h" 35 #include "xdgmimemagic.h" 36 #include "xdgmimealias.h" 37 #include "xdgmimeicon.h" 38 #include "xdgmimeparent.h" 39 #include "xdgmimecache.h" 40 #include <stdio.h> 41 #include <string.h> 42 #include <sys/stat.h> 43 #include <sys/types.h> 44 #include <sys/time.h> 45 #include <unistd.h> 46 #include <assert.h> 47 48 typedef struct XdgDirTimeList XdgDirTimeList; 49 typedef struct XdgCallbackList XdgCallbackList; 50 51 static int need_reread = TRUE; 52 static time_t last_stat_time = 0; 53 54 static XdgGlobHash *global_hash = NULL; 55 static XdgMimeMagic *global_magic = NULL; 56 static XdgAliasList *alias_list = NULL; 57 static XdgParentList *parent_list = NULL; 58 static XdgDirTimeList *dir_time_list = NULL; 59 static XdgCallbackList *callback_list = NULL; 60 static XdgIconList *icon_list = NULL; 61 static XdgIconList *generic_icon_list = NULL; 62 63 XdgMimeCache **_caches = NULL; 64 static int n_caches = 0; 65 66 const char xdg_mime_type_unknown[] = "application/octet-stream"; 67 68 69 enum 70 { 71 XDG_CHECKED_UNCHECKED, 72 XDG_CHECKED_VALID, 73 XDG_CHECKED_INVALID 74 }; 75 76 struct XdgDirTimeList 77 { 78 time_t mtime; 79 char *directory_name; 80 int checked; 81 XdgDirTimeList *next; 82 }; 83 84 struct XdgCallbackList 85 { 86 XdgCallbackList *next; 87 XdgCallbackList *prev; 88 int callback_id; 89 XdgMimeCallback callback; 90 void *data; 91 XdgMimeDestroy destroy; 92 }; 93 94 /* Function called by xdg_run_command_on_dirs. If it returns TRUE, further 95 * directories aren't looked at */ 96 typedef int (*XdgDirectoryFunc) (const char *directory, 97 void *user_data); 98 99 static void 100 xdg_dir_time_list_add (char *file_name, 101 time_t mtime) 102 { 103 XdgDirTimeList *list; 104 105 for (list = dir_time_list; list; list = list->next) 106 { 107 if (strcmp (list->directory_name, file_name) == 0) 108 { 109 free (file_name); 110 return; 111 } 112 } 113 114 list = calloc (1, sizeof (XdgDirTimeList)); 115 list->checked = XDG_CHECKED_UNCHECKED; 116 list->directory_name = file_name; 117 list->mtime = mtime; 118 list->next = dir_time_list; 119 dir_time_list = list; 120 } 121 122 static void 123 xdg_dir_time_list_free (XdgDirTimeList *list) 124 { 125 XdgDirTimeList *next; 126 127 while (list) 128 { 129 next = list->next; 130 free (list->directory_name); 131 free (list); 132 list = next; 133 } 134 } 135 136 static int 137 xdg_mime_init_from_directory (const char *directory) 138 { 139 char *file_name; 140 struct stat st; 141 142 assert (directory != NULL); 143 144 file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); 145 strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); 146 if (stat (file_name, &st) == 0) 147 { 148 XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name); 149 150 if (cache != NULL) 151 { 152 xdg_dir_time_list_add (file_name, st.st_mtime); 153 154 _caches = realloc (_caches, sizeof (XdgMimeCache *) * (n_caches + 2)); 155 _caches[n_caches] = cache; 156 _caches[n_caches + 1] = NULL; 157 n_caches++; 158 159 return FALSE; 160 } 161 } 162 free (file_name); 163 164 file_name = malloc (strlen (directory) + strlen ("/mime/globs2") + 1); 165 strcpy (file_name, directory); strcat (file_name, "/mime/globs2"); 166 if (stat (file_name, &st) == 0) 167 { 168 _xdg_mime_glob_read_from_file (global_hash, file_name); 169 xdg_dir_time_list_add (file_name, st.st_mtime); 170 } 171 else 172 { 173 free (file_name); 174 file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); 175 strcpy (file_name, directory); strcat (file_name, "/mime/globs"); 176 if (stat (file_name, &st) == 0) 177 { 178 _xdg_mime_glob_read_from_file (global_hash, file_name); 179 xdg_dir_time_list_add (file_name, st.st_mtime); 180 } 181 else 182 { 183 free (file_name); 184 } 185 } 186 187 file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); 188 strcpy (file_name, directory); strcat (file_name, "/mime/magic"); 189 if (stat (file_name, &st) == 0) 190 { 191 _xdg_mime_magic_read_from_file (global_magic, file_name); 192 xdg_dir_time_list_add (file_name, st.st_mtime); 193 } 194 else 195 { 196 free (file_name); 197 } 198 199 file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1); 200 strcpy (file_name, directory); strcat (file_name, "/mime/aliases"); 201 _xdg_mime_alias_read_from_file (alias_list, file_name); 202 free (file_name); 203 204 file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1); 205 strcpy (file_name, directory); strcat (file_name, "/mime/subclasses"); 206 _xdg_mime_parent_read_from_file (parent_list, file_name); 207 free (file_name); 208 209 file_name = malloc (strlen (directory) + strlen ("/mime/icons") + 1); 210 strcpy (file_name, directory); strcat (file_name, "/mime/icons"); 211 _xdg_mime_icon_read_from_file (icon_list, file_name); 212 free (file_name); 213 214 file_name = malloc (strlen (directory) + strlen ("/mime/generic-icons") + 1); 215 strcpy (file_name, directory); strcat (file_name, "/mime/generic-icons"); 216 _xdg_mime_icon_read_from_file (generic_icon_list, file_name); 217 free (file_name); 218 219 return FALSE; /* Keep processing */ 220 } 221 222 /* Runs a command on all the directories in the search path */ 223 static void 224 xdg_run_command_on_dirs (XdgDirectoryFunc func, 225 void *user_data) 226 { 227 const char *xdg_data_home; 228 const char *xdg_data_dirs; 229 const char *ptr; 230 231 xdg_data_home = getenv ("XDG_DATA_HOME"); 232 if (xdg_data_home) 233 { 234 if ((func) (xdg_data_home, user_data)) 235 return; 236 } 237 else 238 { 239 const char *home; 240 241 home = getenv ("HOME"); 242 if (home != NULL) 243 { 244 char *guessed_xdg_home; 245 int stop_processing; 246 247 guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1); 248 strcpy (guessed_xdg_home, home); 249 strcat (guessed_xdg_home, "/.local/share/"); 250 stop_processing = (func) (guessed_xdg_home, user_data); 251 free (guessed_xdg_home); 252 253 if (stop_processing) 254 return; 255 } 256 } 257 258 xdg_data_dirs = getenv ("XDG_DATA_DIRS"); 259 if (xdg_data_dirs == NULL) 260 xdg_data_dirs = "/usr/local/share/:/usr/share/"; 261 262 ptr = xdg_data_dirs; 263 264 while (*ptr != '\000') 265 { 266 const char *end_ptr; 267 char *dir; 268 int len; 269 int stop_processing; 270 271 end_ptr = ptr; 272 while (*end_ptr != ':' && *end_ptr != '\000') 273 end_ptr ++; 274 275 if (end_ptr == ptr) 276 { 277 ptr++; 278 continue; 279 } 280 281 if (*end_ptr == ':') 282 len = end_ptr - ptr; 283 else 284 len = end_ptr - ptr + 1; 285 dir = malloc (len + 1); 286 strncpy (dir, ptr, len); 287 dir[len] = '\0'; 288 stop_processing = (func) (dir, user_data); 289 free (dir); 290 291 if (stop_processing) 292 return; 293 294 ptr = end_ptr; 295 } 296 } 297 298 /* Checks file_path to make sure it has the same mtime as last time it was 299 * checked. If it has a different mtime, or if the file doesn't exist, it 300 * returns FALSE. 301 * 302 * FIXME: This doesn't protect against permission changes. 303 */ 304 static int 305 xdg_check_file (const char *file_path, 306 int *exists) 307 { 308 struct stat st; 309 310 /* If the file exists */ 311 if (stat (file_path, &st) == 0) 312 { 313 XdgDirTimeList *list; 314 315 if (exists) 316 *exists = TRUE; 317 318 for (list = dir_time_list; list; list = list->next) 319 { 320 if (! strcmp (list->directory_name, file_path)) 321 { 322 if (st.st_mtime == list->mtime) 323 list->checked = XDG_CHECKED_VALID; 324 else 325 list->checked = XDG_CHECKED_INVALID; 326 327 return (list->checked != XDG_CHECKED_VALID); 328 } 329 } 330 return TRUE; 331 } 332 333 if (exists) 334 *exists = FALSE; 335 336 return FALSE; 337 } 338 339 static int 340 xdg_check_dir (const char *directory, 341 int *invalid_dir_list) 342 { 343 int invalid, exists; 344 char *file_name; 345 346 assert (directory != NULL); 347 348 /* Check the mime.cache file */ 349 file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); 350 strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); 351 invalid = xdg_check_file (file_name, &exists); 352 free (file_name); 353 if (invalid) 354 { 355 *invalid_dir_list = TRUE; 356 return TRUE; 357 } 358 else if (exists) 359 { 360 return FALSE; 361 } 362 363 /* Check the globs file */ 364 file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); 365 strcpy (file_name, directory); strcat (file_name, "/mime/globs"); 366 invalid = xdg_check_file (file_name, NULL); 367 free (file_name); 368 if (invalid) 369 { 370 *invalid_dir_list = TRUE; 371 return TRUE; 372 } 373 374 /* Check the magic file */ 375 file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); 376 strcpy (file_name, directory); strcat (file_name, "/mime/magic"); 377 invalid = xdg_check_file (file_name, NULL); 378 free (file_name); 379 if (invalid) 380 { 381 *invalid_dir_list = TRUE; 382 return TRUE; 383 } 384 385 return FALSE; /* Keep processing */ 386 } 387 388 /* Walks through all the mime files stat()ing them to see if they've changed. 389 * Returns TRUE if they have. */ 390 static int 391 xdg_check_dirs (void) 392 { 393 XdgDirTimeList *list; 394 int invalid_dir_list = FALSE; 395 396 for (list = dir_time_list; list; list = list->next) 397 list->checked = XDG_CHECKED_UNCHECKED; 398 399 xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir, 400 &invalid_dir_list); 401 402 if (invalid_dir_list) 403 return TRUE; 404 405 for (list = dir_time_list; list; list = list->next) 406 { 407 if (list->checked != XDG_CHECKED_VALID) 408 return TRUE; 409 } 410 411 return FALSE; 412 } 413 414 /* We want to avoid stat()ing on every single mime call, so we only look for 415 * newer files every 5 seconds. This will return TRUE if we need to reread the 416 * mime data from disk. 417 */ 418 static int 419 xdg_check_time_and_dirs (void) 420 { 421 struct timeval tv; 422 time_t current_time; 423 int retval = FALSE; 424 425 gettimeofday (&tv, NULL); 426 current_time = tv.tv_sec; 427 428 if (current_time >= last_stat_time + 5) 429 { 430 retval = xdg_check_dirs (); 431 last_stat_time = current_time; 432 } 433 434 return retval; 435 } 436 437 /* Called in every public function. It reloads the hash function if need be. 438 */ 439 static void 440 xdg_mime_init (void) 441 { 442 if (xdg_check_time_and_dirs ()) 443 { 444 xdg_mime_shutdown (); 445 } 446 447 if (need_reread) 448 { 449 global_hash = _xdg_glob_hash_new (); 450 global_magic = _xdg_mime_magic_new (); 451 alias_list = _xdg_mime_alias_list_new (); 452 parent_list = _xdg_mime_parent_list_new (); 453 icon_list = _xdg_mime_icon_list_new (); 454 generic_icon_list = _xdg_mime_icon_list_new (); 455 456 xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory, 457 NULL); 458 459 need_reread = FALSE; 460 } 461 } 462 463 const char * 464 xdg_mime_get_mime_type_for_data (const void *data, 465 size_t len, 466 int *result_prio) 467 { 468 const char *mime_type; 469 470 xdg_mime_init (); 471 472 if (_caches) 473 return _xdg_mime_cache_get_mime_type_for_data (data, len, result_prio); 474 475 mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, result_prio, NULL, 0); 476 477 if (mime_type) 478 return mime_type; 479 480 return XDG_MIME_TYPE_UNKNOWN; 481 } 482 483 const char * 484 xdg_mime_get_mime_type_for_file (const char *file_name, 485 struct stat *statbuf) 486 { 487 const char *mime_type; 488 /* currently, only a few globs occur twice, and none 489 * more often, so 5 seems plenty. 490 */ 491 const char *mime_types[5]; 492 FILE *file; 493 unsigned char *data; 494 int max_extent; 495 int bytes_read; 496 struct stat buf; 497 const char *base_name; 498 int n; 499 500 if (file_name == NULL) 501 return NULL; 502 if (! _xdg_utf8_validate (file_name)) 503 return NULL; 504 505 xdg_mime_init (); 506 507 if (_caches) 508 return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf); 509 510 base_name = _xdg_get_base_name (file_name); 511 n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5); 512 513 if (n == 1) 514 return mime_types[0]; 515 516 if (!statbuf) 517 { 518 if (stat (file_name, &buf) != 0) 519 return XDG_MIME_TYPE_UNKNOWN; 520 521 statbuf = &buf; 522 } 523 524 if (!S_ISREG (statbuf->st_mode)) 525 return XDG_MIME_TYPE_UNKNOWN; 526 527 /* FIXME: Need to make sure that max_extent isn't totally broken. This could 528 * be large and need getting from a stream instead of just reading it all 529 * in. */ 530 max_extent = _xdg_mime_magic_get_buffer_extents (global_magic); 531 data = malloc (max_extent); 532 if (data == NULL) 533 return XDG_MIME_TYPE_UNKNOWN; 534 535 file = fopen (file_name, "r"); 536 if (file == NULL) 537 { 538 free (data); 539 return XDG_MIME_TYPE_UNKNOWN; 540 } 541 542 bytes_read = fread (data, 1, max_extent, file); 543 if (ferror (file)) 544 { 545 free (data); 546 fclose (file); 547 return XDG_MIME_TYPE_UNKNOWN; 548 } 549 550 mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL, 551 mime_types, n); 552 553 free (data); 554 fclose (file); 555 556 if (mime_type) 557 return mime_type; 558 559 return XDG_MIME_TYPE_UNKNOWN; 560 } 561 562 const char * 563 xdg_mime_get_mime_type_from_file_name (const char *file_name) 564 { 565 const char *mime_type; 566 567 xdg_mime_init (); 568 569 if (_caches) 570 return _xdg_mime_cache_get_mime_type_from_file_name (file_name); 571 572 if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1)) 573 return mime_type; 574 else 575 return XDG_MIME_TYPE_UNKNOWN; 576 } 577 578 int 579 xdg_mime_get_mime_types_from_file_name (const char *file_name, 580 const char *mime_types[], 581 int n_mime_types) 582 { 583 xdg_mime_init (); 584 585 if (_caches) 586 return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types, n_mime_types); 587 588 return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types); 589 } 590 591 int 592 xdg_mime_is_valid_mime_type (const char *mime_type) 593 { 594 /* FIXME: We should make this a better test 595 */ 596 return _xdg_utf8_validate (mime_type); 597 } 598 599 void 600 xdg_mime_shutdown (void) 601 { 602 XdgCallbackList *list; 603 604 /* FIXME: Need to make this (and the whole library) thread safe */ 605 if (dir_time_list) 606 { 607 xdg_dir_time_list_free (dir_time_list); 608 dir_time_list = NULL; 609 } 610 611 if (global_hash) 612 { 613 _xdg_glob_hash_free (global_hash); 614 global_hash = NULL; 615 } 616 if (global_magic) 617 { 618 _xdg_mime_magic_free (global_magic); 619 global_magic = NULL; 620 } 621 622 if (alias_list) 623 { 624 _xdg_mime_alias_list_free (alias_list); 625 alias_list = NULL; 626 } 627 628 if (parent_list) 629 { 630 _xdg_mime_parent_list_free (parent_list); 631 parent_list = NULL; 632 } 633 634 if (icon_list) 635 { 636 _xdg_mime_icon_list_free (icon_list); 637 icon_list = NULL; 638 } 639 640 if (generic_icon_list) 641 { 642 _xdg_mime_icon_list_free (generic_icon_list); 643 generic_icon_list = NULL; 644 } 645 646 if (_caches) 647 { 648 int i; 649 650 for (i = 0; i < n_caches; i++) 651 _xdg_mime_cache_unref (_caches[i]); 652 free (_caches); 653 _caches = NULL; 654 n_caches = 0; 655 } 656 657 for (list = callback_list; list; list = list->next) 658 (list->callback) (list->data); 659 660 need_reread = TRUE; 661 } 662 663 int 664 xdg_mime_get_max_buffer_extents (void) 665 { 666 xdg_mime_init (); 667 668 if (_caches) 669 return _xdg_mime_cache_get_max_buffer_extents (); 670 671 return _xdg_mime_magic_get_buffer_extents (global_magic); 672 } 673 674 const char * 675 _xdg_mime_unalias_mime_type (const char *mime_type) 676 { 677 const char *lookup; 678 679 if (_caches) 680 return _xdg_mime_cache_unalias_mime_type (mime_type); 681 682 if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL) 683 return lookup; 684 685 return mime_type; 686 } 687 688 const char * 689 xdg_mime_unalias_mime_type (const char *mime_type) 690 { 691 xdg_mime_init (); 692 693 return _xdg_mime_unalias_mime_type (mime_type); 694 } 695 696 int 697 _xdg_mime_mime_type_equal (const char *mime_a, 698 const char *mime_b) 699 { 700 const char *unalias_a, *unalias_b; 701 702 unalias_a = _xdg_mime_unalias_mime_type (mime_a); 703 unalias_b = _xdg_mime_unalias_mime_type (mime_b); 704 705 if (strcmp (unalias_a, unalias_b) == 0) 706 return 1; 707 708 return 0; 709 } 710 711 int 712 xdg_mime_mime_type_equal (const char *mime_a, 713 const char *mime_b) 714 { 715 xdg_mime_init (); 716 717 return _xdg_mime_mime_type_equal (mime_a, mime_b); 718 } 719 720 int 721 xdg_mime_media_type_equal (const char *mime_a, 722 const char *mime_b) 723 { 724 char *sep; 725 726 sep = strchr (mime_a, '/'); 727 728 if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0) 729 return 1; 730 731 return 0; 732 } 733 734 #if 1 735 static int 736 xdg_mime_is_super_type (const char *mime) 737 { 738 int length; 739 const char *type; 740 741 length = strlen (mime); 742 type = &(mime[length - 2]); 743 744 if (strcmp (type, "/*") == 0) 745 return 1; 746 747 return 0; 748 } 749 #endif 750 751 int 752 _xdg_mime_mime_type_subclass (const char *mime, 753 const char *base) 754 { 755 const char *umime, *ubase; 756 const char **parents; 757 758 if (_caches) 759 return _xdg_mime_cache_mime_type_subclass (mime, base); 760 761 umime = _xdg_mime_unalias_mime_type (mime); 762 ubase = _xdg_mime_unalias_mime_type (base); 763 764 if (strcmp (umime, ubase) == 0) 765 return 1; 766 767 #if 1 768 /* Handle supertypes */ 769 if (xdg_mime_is_super_type (ubase) && 770 xdg_mime_media_type_equal (umime, ubase)) 771 return 1; 772 #endif 773 774 /* Handle special cases text/plain and application/octet-stream */ 775 if (strcmp (ubase, "text/plain") == 0 && 776 strncmp (umime, "text/", 5) == 0) 777 return 1; 778 779 if (strcmp (ubase, "application/octet-stream") == 0) 780 return 1; 781 782 parents = _xdg_mime_parent_list_lookup (parent_list, umime); 783 for (; parents && *parents; parents++) 784 { 785 if (_xdg_mime_mime_type_subclass (*parents, ubase)) 786 return 1; 787 } 788 789 return 0; 790 } 791 792 int 793 xdg_mime_mime_type_subclass (const char *mime, 794 const char *base) 795 { 796 xdg_mime_init (); 797 798 return _xdg_mime_mime_type_subclass (mime, base); 799 } 800 801 char ** 802 xdg_mime_list_mime_parents (const char *mime) 803 { 804 const char **parents; 805 char **result; 806 int i, n; 807 808 if (_caches) 809 return _xdg_mime_cache_list_mime_parents (mime); 810 811 parents = xdg_mime_get_mime_parents (mime); 812 813 if (!parents) 814 return NULL; 815 816 for (i = 0; parents[i]; i++) ; 817 818 n = (i + 1) * sizeof (char *); 819 result = (char **) malloc (n); 820 memcpy (result, parents, n); 821 822 return result; 823 } 824 825 const char ** 826 xdg_mime_get_mime_parents (const char *mime) 827 { 828 const char *umime; 829 830 xdg_mime_init (); 831 832 umime = _xdg_mime_unalias_mime_type (mime); 833 834 return _xdg_mime_parent_list_lookup (parent_list, umime); 835 } 836 837 void 838 xdg_mime_dump (void) 839 { 840 xdg_mime_init(); 841 842 printf ("*** ALIASES ***\n\n"); 843 _xdg_mime_alias_list_dump (alias_list); 844 printf ("\n*** PARENTS ***\n\n"); 845 _xdg_mime_parent_list_dump (parent_list); 846 printf ("\n*** CACHE ***\n\n"); 847 _xdg_glob_hash_dump (global_hash); 848 printf ("\n*** GLOBS ***\n\n"); 849 _xdg_glob_hash_dump (global_hash); 850 printf ("\n*** GLOBS REVERSE TREE ***\n\n"); 851 _xdg_mime_cache_glob_dump (); 852 } 853 854 855 /* Registers a function to be called every time the mime database reloads its files 856 */ 857 int 858 xdg_mime_register_reload_callback (XdgMimeCallback callback, 859 void *data, 860 XdgMimeDestroy destroy) 861 { 862 XdgCallbackList *list_el; 863 static int callback_id = 1; 864 865 /* Make a new list element */ 866 list_el = calloc (1, sizeof (XdgCallbackList)); 867 list_el->callback_id = callback_id; 868 list_el->callback = callback; 869 list_el->data = data; 870 list_el->destroy = destroy; 871 list_el->next = callback_list; 872 if (list_el->next) 873 list_el->next->prev = list_el; 874 875 callback_list = list_el; 876 callback_id ++; 877 878 return callback_id - 1; 879 } 880 881 void 882 xdg_mime_remove_callback (int callback_id) 883 { 884 XdgCallbackList *list; 885 886 for (list = callback_list; list; list = list->next) 887 { 888 if (list->callback_id == callback_id) 889 { 890 if (list->next) 891 list->next = list->prev; 892 893 if (list->prev) 894 list->prev->next = list->next; 895 else 896 callback_list = list->next; 897 898 /* invoke the destroy handler */ 899 (list->destroy) (list->data); 900 free (list); 901 return; 902 } 903 } 904 } 905 906 const char * 907 xdg_mime_get_icon (const char *mime) 908 { 909 xdg_mime_init (); 910 911 if (_caches) 912 return _xdg_mime_cache_get_icon (mime); 913 914 return _xdg_mime_icon_list_lookup (icon_list, mime); 915 } 916 917 const char * 918 xdg_mime_get_generic_icon (const char *mime) 919 { 920 xdg_mime_init (); 921 922 if (_caches) 923 return _xdg_mime_cache_get_generic_icon (mime); 924 925 return _xdg_mime_icon_list_lookup (generic_icon_list, mime); 926 } 927