1 /* -*- mode: C; c-file-style: "gnu" -*- */ 2 /* xdgmimealias.c: Private file. mmappable caches for mime data 3 * 4 * More info can be found at http://www.freedesktop.org/standards/ 5 * 6 * Copyright (C) 2005 Matthias Clasen <mclasen (at) redhat.com> 7 * 8 * Licensed under the Academic Free License version 2.0 9 * Or under the following terms: 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the 23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 24 * Boston, MA 02111-1307, USA. 25 */ 26 27 #ifdef HAVE_CONFIG_H 28 #include "config.h" 29 #endif 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <ctype.h> 35 36 #include <fcntl.h> 37 #include <unistd.h> 38 #include <fnmatch.h> 39 #include <assert.h> 40 41 #include <netinet/in.h> /* for ntohl/ntohs */ 42 43 #ifdef HAVE_MMAP 44 #include <sys/mman.h> 45 #else 46 #warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used. 47 #endif 48 49 #include <sys/stat.h> 50 #include <sys/types.h> 51 52 #include "xdgmimecache.h" 53 #include "xdgmimeint.h" 54 55 #ifndef MAX 56 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 57 #endif 58 59 #ifndef FALSE 60 #define FALSE (0) 61 #endif 62 63 #ifndef TRUE 64 #define TRUE (!FALSE) 65 #endif 66 67 #ifndef _O_BINARY 68 #define _O_BINARY 0 69 #endif 70 71 #ifndef MAP_FAILED 72 #define MAP_FAILED ((void *) -1) 73 #endif 74 75 #define MAJOR_VERSION 1 76 #define MINOR_VERSION 1 77 78 struct _XdgMimeCache 79 { 80 int ref_count; 81 82 size_t size; 83 char *buffer; 84 }; 85 86 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset)))) 87 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset)))) 88 89 XdgMimeCache * 90 _xdg_mime_cache_ref (XdgMimeCache *cache) 91 { 92 cache->ref_count++; 93 return cache; 94 } 95 96 void 97 _xdg_mime_cache_unref (XdgMimeCache *cache) 98 { 99 cache->ref_count--; 100 101 if (cache->ref_count == 0) 102 { 103 #ifdef HAVE_MMAP 104 munmap (cache->buffer, cache->size); 105 #endif 106 free (cache); 107 } 108 } 109 110 XdgMimeCache * 111 _xdg_mime_cache_new_from_file (const char *file_name) 112 { 113 XdgMimeCache *cache = NULL; 114 115 #ifdef HAVE_MMAP 116 int fd = -1; 117 struct stat st; 118 char *buffer = NULL; 119 120 /* Open the file and map it into memory */ 121 fd = open (file_name, O_RDONLY|_O_BINARY, 0); 122 123 if (fd < 0) 124 return NULL; 125 126 if (fstat (fd, &st) < 0 || st.st_size < 4) 127 goto done; 128 129 buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 130 131 if (buffer == MAP_FAILED) 132 goto done; 133 134 /* Verify version */ 135 if (GET_UINT16 (buffer, 0) != MAJOR_VERSION || 136 GET_UINT16 (buffer, 2) != MINOR_VERSION) 137 { 138 munmap (buffer, st.st_size); 139 140 goto done; 141 } 142 143 cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache)); 144 cache->ref_count = 1; 145 cache->buffer = buffer; 146 cache->size = st.st_size; 147 148 done: 149 if (fd != -1) 150 close (fd); 151 152 #endif /* HAVE_MMAP */ 153 154 return cache; 155 } 156 157 static int 158 cache_magic_matchlet_compare_to_data (XdgMimeCache *cache, 159 xdg_uint32_t offset, 160 const void *data, 161 size_t len) 162 { 163 xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset); 164 xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4); 165 xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12); 166 xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16); 167 xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20); 168 169 int i, j; 170 171 for (i = range_start; i <= range_start + range_length; i++) 172 { 173 int valid_matchlet = TRUE; 174 175 if (i + data_length > len) 176 return FALSE; 177 178 if (mask_offset) 179 { 180 for (j = 0; j < data_length; j++) 181 { 182 if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) != 183 ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j])) 184 { 185 valid_matchlet = FALSE; 186 break; 187 } 188 } 189 } 190 else 191 { 192 for (j = 0; j < data_length; j++) 193 { 194 if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i]) 195 { 196 valid_matchlet = FALSE; 197 break; 198 } 199 } 200 } 201 202 if (valid_matchlet) 203 return TRUE; 204 } 205 206 return FALSE; 207 } 208 209 static int 210 cache_magic_matchlet_compare (XdgMimeCache *cache, 211 xdg_uint32_t offset, 212 const void *data, 213 size_t len) 214 { 215 xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24); 216 xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28); 217 218 int i; 219 220 if (cache_magic_matchlet_compare_to_data (cache, offset, data, len)) 221 { 222 if (n_children == 0) 223 return TRUE; 224 225 for (i = 0; i < n_children; i++) 226 { 227 if (cache_magic_matchlet_compare (cache, child_offset + 32 * i, 228 data, len)) 229 return TRUE; 230 } 231 } 232 233 return FALSE; 234 } 235 236 static const char * 237 cache_magic_compare_to_data (XdgMimeCache *cache, 238 xdg_uint32_t offset, 239 const void *data, 240 size_t len, 241 int *prio) 242 { 243 xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset); 244 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4); 245 xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8); 246 xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12); 247 248 int i; 249 250 for (i = 0; i < n_matchlets; i++) 251 { 252 if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32, 253 data, len)) 254 { 255 *prio = priority; 256 257 return cache->buffer + mimetype_offset; 258 } 259 } 260 261 return NULL; 262 } 263 264 static const char * 265 cache_magic_lookup_data (XdgMimeCache *cache, 266 const void *data, 267 size_t len, 268 int *prio, 269 const char *mime_types[], 270 int n_mime_types) 271 { 272 xdg_uint32_t list_offset; 273 xdg_uint32_t n_entries; 274 xdg_uint32_t offset; 275 276 int j, n; 277 278 *prio = 0; 279 280 list_offset = GET_UINT32 (cache->buffer, 24); 281 n_entries = GET_UINT32 (cache->buffer, list_offset); 282 offset = GET_UINT32 (cache->buffer, list_offset + 8); 283 284 for (j = 0; j < n_entries; j++) 285 { 286 const char *match; 287 288 match = cache_magic_compare_to_data (cache, offset + 16 * j, 289 data, len, prio); 290 if (match) 291 return match; 292 else 293 { 294 xdg_uint32_t mimetype_offset; 295 const char *non_match; 296 297 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4); 298 non_match = cache->buffer + mimetype_offset; 299 300 for (n = 0; n < n_mime_types; n++) 301 { 302 if (mime_types[n] && 303 _xdg_mime_mime_type_equal (mime_types[n], non_match)) 304 mime_types[n] = NULL; 305 } 306 } 307 } 308 309 return NULL; 310 } 311 312 static const char * 313 cache_alias_lookup (const char *alias) 314 { 315 const char *ptr; 316 int i, min, max, mid, cmp; 317 318 for (i = 0; _caches[i]; i++) 319 { 320 XdgMimeCache *cache = _caches[i]; 321 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4); 322 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); 323 xdg_uint32_t offset; 324 325 min = 0; 326 max = n_entries - 1; 327 while (max >= min) 328 { 329 mid = (min + max) / 2; 330 331 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid); 332 ptr = cache->buffer + offset; 333 cmp = strcmp (ptr, alias); 334 335 if (cmp < 0) 336 min = mid + 1; 337 else if (cmp > 0) 338 max = mid - 1; 339 else 340 { 341 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4); 342 return cache->buffer + offset; 343 } 344 } 345 } 346 347 return NULL; 348 } 349 350 typedef struct { 351 const char *mime; 352 int weight; 353 } MimeWeight; 354 355 static int 356 cache_glob_lookup_literal (const char *file_name, 357 const char *mime_types[], 358 int n_mime_types) 359 { 360 const char *ptr; 361 int i, min, max, mid, cmp; 362 363 for (i = 0; _caches[i]; i++) 364 { 365 XdgMimeCache *cache = _caches[i]; 366 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12); 367 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); 368 xdg_uint32_t offset; 369 370 min = 0; 371 max = n_entries - 1; 372 while (max >= min) 373 { 374 mid = (min + max) / 2; 375 376 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid); 377 ptr = cache->buffer + offset; 378 cmp = strcmp (ptr, file_name); 379 380 if (cmp < 0) 381 min = mid + 1; 382 else if (cmp > 0) 383 max = mid - 1; 384 else 385 { 386 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4); 387 mime_types[0] = (const char *)(cache->buffer + offset); 388 389 return 1; 390 } 391 } 392 } 393 394 return 0; 395 } 396 397 static int 398 cache_glob_lookup_fnmatch (const char *file_name, 399 MimeWeight mime_types[], 400 int n_mime_types) 401 { 402 const char *mime_type; 403 const char *ptr; 404 405 int i, j, n; 406 407 n = 0; 408 for (i = 0; _caches[i]; i++) 409 { 410 XdgMimeCache *cache = _caches[i]; 411 412 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20); 413 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); 414 415 for (j = 0; j < n_entries && n < n_mime_types; j++) 416 { 417 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j); 418 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4); 419 int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8); 420 ptr = cache->buffer + offset; 421 mime_type = cache->buffer + mimetype_offset; 422 423 /* FIXME: Not UTF-8 safe */ 424 if (fnmatch (ptr, file_name, 0) == 0) 425 { 426 mime_types[n].mime = mime_type; 427 mime_types[n].weight = weight; 428 n++; 429 } 430 } 431 432 if (n > 0) 433 return n; 434 } 435 436 return 0; 437 } 438 439 static int 440 cache_glob_node_lookup_suffix (XdgMimeCache *cache, 441 xdg_uint32_t n_entries, 442 xdg_uint32_t offset, 443 const char *file_name, 444 int len, 445 int ignore_case, 446 MimeWeight mime_types[], 447 int n_mime_types) 448 { 449 xdg_unichar_t character; 450 xdg_unichar_t match_char; 451 xdg_uint32_t mimetype_offset; 452 xdg_uint32_t n_children; 453 xdg_uint32_t child_offset; 454 int weight; 455 456 int min, max, mid, n, i; 457 458 character = file_name[len - 1]; 459 if (ignore_case) 460 character = tolower (character); 461 462 assert (character != 0); 463 464 min = 0; 465 max = n_entries - 1; 466 while (max >= min) 467 { 468 mid = (min + max) / 2; 469 match_char = GET_UINT32 (cache->buffer, offset + 12 * mid); 470 if (match_char < character) 471 min = mid + 1; 472 else if (match_char > character) 473 max = mid - 1; 474 else 475 { 476 len--; 477 n = 0; 478 n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4); 479 child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8); 480 481 if (len > 0) 482 { 483 n = cache_glob_node_lookup_suffix (cache, 484 n_children, child_offset, 485 file_name, len, 486 ignore_case, 487 mime_types, 488 n_mime_types); 489 } 490 if (n == 0) 491 { 492 i = 0; 493 while (n < n_mime_types && i < n_children) 494 { 495 match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i); 496 if (match_char != 0) 497 break; 498 499 mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4); 500 weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8); 501 502 mime_types[n].mime = cache->buffer + mimetype_offset; 503 mime_types[n].weight = weight; 504 n++; 505 i++; 506 } 507 } 508 return n; 509 } 510 } 511 return 0; 512 } 513 514 static int 515 cache_glob_lookup_suffix (const char *file_name, 516 int len, 517 int ignore_case, 518 MimeWeight mime_types[], 519 int n_mime_types) 520 { 521 int i, n; 522 523 for (i = 0; _caches[i]; i++) 524 { 525 XdgMimeCache *cache = _caches[i]; 526 527 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16); 528 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); 529 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4); 530 531 n = cache_glob_node_lookup_suffix (cache, 532 n_entries, offset, 533 file_name, len, 534 ignore_case, 535 mime_types, 536 n_mime_types); 537 if (n > 0) 538 return n; 539 } 540 541 return 0; 542 } 543 544 static int compare_mime_weight (const void *a, const void *b) 545 { 546 const MimeWeight *aa = (const MimeWeight *)a; 547 const MimeWeight *bb = (const MimeWeight *)b; 548 549 return aa->weight - bb->weight; 550 } 551 552 static int 553 cache_glob_lookup_file_name (const char *file_name, 554 const char *mime_types[], 555 int n_mime_types) 556 { 557 int n; 558 MimeWeight mimes[10]; 559 int n_mimes = 10; 560 int i; 561 int len; 562 563 assert (file_name != NULL && n_mime_types > 0); 564 565 /* First, check the literals */ 566 n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types); 567 if (n > 0) 568 return n; 569 570 len = strlen (file_name); 571 n = cache_glob_lookup_suffix (file_name, len, FALSE, mimes, n_mimes); 572 573 if (n == 0) 574 n = cache_glob_lookup_suffix (file_name, len, TRUE, mimes, n_mimes); 575 576 /* Last, try fnmatch */ 577 if (n == 0) 578 n = cache_glob_lookup_fnmatch (file_name, mimes, n_mimes); 579 580 qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight); 581 582 if (n_mime_types < n) 583 n = n_mime_types; 584 585 for (i = 0; i < n; i++) 586 mime_types[i] = mimes[i].mime; 587 588 return n; 589 } 590 591 int 592 _xdg_mime_cache_get_max_buffer_extents (void) 593 { 594 xdg_uint32_t offset; 595 xdg_uint32_t max_extent; 596 int i; 597 598 max_extent = 0; 599 for (i = 0; _caches[i]; i++) 600 { 601 XdgMimeCache *cache = _caches[i]; 602 603 offset = GET_UINT32 (cache->buffer, 24); 604 max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4)); 605 } 606 607 return max_extent; 608 } 609 610 static const char * 611 cache_get_mime_type_for_data (const void *data, 612 size_t len, 613 int *result_prio, 614 const char *mime_types[], 615 int n_mime_types) 616 { 617 const char *mime_type; 618 int i, n, priority; 619 620 priority = 0; 621 mime_type = NULL; 622 for (i = 0; _caches[i]; i++) 623 { 624 XdgMimeCache *cache = _caches[i]; 625 626 int prio; 627 const char *match; 628 629 match = cache_magic_lookup_data (cache, data, len, &prio, 630 mime_types, n_mime_types); 631 if (prio > priority) 632 { 633 priority = prio; 634 mime_type = match; 635 } 636 } 637 638 if (result_prio) 639 *result_prio = priority; 640 641 if (priority > 0) 642 return mime_type; 643 644 for (n = 0; n < n_mime_types; n++) 645 { 646 647 if (mime_types[n]) 648 return mime_types[n]; 649 } 650 651 return XDG_MIME_TYPE_UNKNOWN; 652 } 653 654 const char * 655 _xdg_mime_cache_get_mime_type_for_data (const void *data, 656 size_t len, 657 int *result_prio) 658 { 659 return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0); 660 } 661 662 const char * 663 _xdg_mime_cache_get_mime_type_for_file (const char *file_name, 664 struct stat *statbuf) 665 { 666 const char *mime_type; 667 const char *mime_types[10]; 668 FILE *file; 669 unsigned char *data; 670 int max_extent; 671 int bytes_read; 672 struct stat buf; 673 const char *base_name; 674 int n; 675 676 if (file_name == NULL) 677 return NULL; 678 679 if (! _xdg_utf8_validate (file_name)) 680 return NULL; 681 682 base_name = _xdg_get_base_name (file_name); 683 n = cache_glob_lookup_file_name (base_name, mime_types, 10); 684 685 if (n == 1) 686 return mime_types[0]; 687 688 if (!statbuf) 689 { 690 if (stat (file_name, &buf) != 0) 691 return XDG_MIME_TYPE_UNKNOWN; 692 693 statbuf = &buf; 694 } 695 696 if (!S_ISREG (statbuf->st_mode)) 697 return XDG_MIME_TYPE_UNKNOWN; 698 699 /* FIXME: Need to make sure that max_extent isn't totally broken. This could 700 * be large and need getting from a stream instead of just reading it all 701 * in. */ 702 max_extent = _xdg_mime_cache_get_max_buffer_extents (); 703 data = malloc (max_extent); 704 if (data == NULL) 705 return XDG_MIME_TYPE_UNKNOWN; 706 707 file = fopen (file_name, "r"); 708 if (file == NULL) 709 { 710 free (data); 711 return XDG_MIME_TYPE_UNKNOWN; 712 } 713 714 bytes_read = fread (data, 1, max_extent, file); 715 if (ferror (file)) 716 { 717 free (data); 718 fclose (file); 719 return XDG_MIME_TYPE_UNKNOWN; 720 } 721 722 mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL, 723 mime_types, n); 724 725 free (data); 726 fclose (file); 727 728 return mime_type; 729 } 730 731 const char * 732 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name) 733 { 734 const char *mime_type; 735 736 if (cache_glob_lookup_file_name (file_name, &mime_type, 1)) 737 return mime_type; 738 else 739 return XDG_MIME_TYPE_UNKNOWN; 740 } 741 742 int 743 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name, 744 const char *mime_types[], 745 int n_mime_types) 746 { 747 return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types); 748 } 749 750 #if 1 751 static int 752 is_super_type (const char *mime) 753 { 754 int length; 755 const char *type; 756 757 length = strlen (mime); 758 type = &(mime[length - 2]); 759 760 if (strcmp (type, "/*") == 0) 761 return 1; 762 763 return 0; 764 } 765 #endif 766 767 int 768 _xdg_mime_cache_mime_type_subclass (const char *mime, 769 const char *base) 770 { 771 const char *umime, *ubase; 772 773 int i, j, min, max, med, cmp; 774 775 umime = _xdg_mime_cache_unalias_mime_type (mime); 776 ubase = _xdg_mime_cache_unalias_mime_type (base); 777 778 if (strcmp (umime, ubase) == 0) 779 return 1; 780 781 /* We really want to handle text/ * in GtkFileFilter, so we just 782 * turn on the supertype matching 783 */ 784 #if 1 785 /* Handle supertypes */ 786 if (is_super_type (ubase) && 787 xdg_mime_media_type_equal (umime, ubase)) 788 return 1; 789 #endif 790 791 /* Handle special cases text/plain and application/octet-stream */ 792 if (strcmp (ubase, "text/plain") == 0 && 793 strncmp (umime, "text/", 5) == 0) 794 return 1; 795 796 if (strcmp (ubase, "application/octet-stream") == 0) 797 return 1; 798 799 for (i = 0; _caches[i]; i++) 800 { 801 XdgMimeCache *cache = _caches[i]; 802 803 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8); 804 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); 805 xdg_uint32_t offset, n_parents, parent_offset; 806 807 min = 0; 808 max = n_entries - 1; 809 while (max >= min) 810 { 811 med = (min + max)/2; 812 813 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med); 814 cmp = strcmp (cache->buffer + offset, umime); 815 if (cmp < 0) 816 min = med + 1; 817 else if (cmp > 0) 818 max = med - 1; 819 else 820 { 821 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4); 822 n_parents = GET_UINT32 (cache->buffer, offset); 823 824 for (j = 0; j < n_parents; j++) 825 { 826 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j); 827 if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase)) 828 return 1; 829 } 830 831 break; 832 } 833 } 834 } 835 836 return 0; 837 } 838 839 const char * 840 _xdg_mime_cache_unalias_mime_type (const char *mime) 841 { 842 const char *lookup; 843 844 lookup = cache_alias_lookup (mime); 845 846 if (lookup) 847 return lookup; 848 849 return mime; 850 } 851 852 char ** 853 _xdg_mime_cache_list_mime_parents (const char *mime) 854 { 855 int i, j, k, l, p; 856 char *all_parents[128]; /* we'll stop at 128 */ 857 char **result; 858 859 mime = xdg_mime_unalias_mime_type (mime); 860 861 p = 0; 862 for (i = 0; _caches[i]; i++) 863 { 864 XdgMimeCache *cache = _caches[i]; 865 866 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8); 867 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); 868 869 for (j = 0; j < n_entries; j++) 870 { 871 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j); 872 xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4); 873 874 if (strcmp (cache->buffer + mimetype_offset, mime) == 0) 875 { 876 xdg_uint32_t parent_mime_offset; 877 xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset); 878 879 for (k = 0; k < n_parents && p < 127; k++) 880 { 881 parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k); 882 883 /* Don't add same parent multiple times. 884 * This can happen for instance if the same type is listed in multiple directories 885 */ 886 for (l = 0; l < p; l++) 887 { 888 if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0) 889 break; 890 } 891 892 if (l == p) 893 all_parents[p++] = cache->buffer + parent_mime_offset; 894 } 895 896 break; 897 } 898 } 899 } 900 all_parents[p++] = NULL; 901 902 result = (char **) malloc (p * sizeof (char *)); 903 memcpy (result, all_parents, p * sizeof (char *)); 904 905 return result; 906 } 907 908 static const char * 909 cache_lookup_icon (const char *mime, int header) 910 { 911 const char *ptr; 912 int i, min, max, mid, cmp; 913 914 for (i = 0; _caches[i]; i++) 915 { 916 XdgMimeCache *cache = _caches[i]; 917 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, header); 918 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); 919 xdg_uint32_t offset; 920 921 min = 0; 922 max = n_entries - 1; 923 while (max >= min) 924 { 925 mid = (min + max) / 2; 926 927 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid); 928 ptr = cache->buffer + offset; 929 cmp = strcmp (ptr, mime); 930 931 if (cmp < 0) 932 min = mid + 1; 933 else if (cmp > 0) 934 max = mid - 1; 935 else 936 { 937 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4); 938 return cache->buffer + offset; 939 } 940 } 941 } 942 943 return NULL; 944 } 945 946 const char * 947 _xdg_mime_cache_get_generic_icon (const char *mime) 948 { 949 return cache_lookup_icon (mime, 36); 950 } 951 952 const char * 953 _xdg_mime_cache_get_icon (const char *mime) 954 { 955 return cache_lookup_icon (mime, 32); 956 } 957 958 static void 959 dump_glob_node (XdgMimeCache *cache, 960 xdg_uint32_t offset, 961 int depth) 962 { 963 xdg_unichar_t character; 964 xdg_uint32_t mime_offset; 965 xdg_uint32_t n_children; 966 xdg_uint32_t child_offset; 967 int i; 968 969 character = GET_UINT32 (cache->buffer, offset); 970 mime_offset = GET_UINT32 (cache->buffer, offset + 4); 971 n_children = GET_UINT32 (cache->buffer, offset + 8); 972 child_offset = GET_UINT32 (cache->buffer, offset + 12); 973 for (i = 0; i < depth; i++) 974 printf (" "); 975 printf ("%c", character); 976 if (mime_offset) 977 printf (" - %s", cache->buffer + mime_offset); 978 printf ("\n"); 979 if (child_offset) 980 { 981 for (i = 0; i < n_children; i++) 982 dump_glob_node (cache, child_offset + 20 * i, depth + 1); 983 } 984 } 985 986 void 987 _xdg_mime_cache_glob_dump (void) 988 { 989 int i, j; 990 for (i = 0; _caches[i]; i++) 991 { 992 XdgMimeCache *cache = _caches[i]; 993 xdg_uint32_t list_offset; 994 xdg_uint32_t n_entries; 995 xdg_uint32_t offset; 996 list_offset = GET_UINT32 (cache->buffer, 16); 997 n_entries = GET_UINT32 (cache->buffer, list_offset); 998 offset = GET_UINT32 (cache->buffer, list_offset + 4); 999 for (j = 0; j < n_entries; j++) 1000 dump_glob_node (cache, offset + 20 * j, 0); 1001 } 1002 } 1003 1004 1005