1 /* -*- mode: C; c-file-style: "gnu" -*- */ 2 /* xdgmimemagic.: Private file. Datastructure for storing magic files. 3 * 4 * More info can be found at http://www.freedesktop.org/standards/ 5 * 6 * Copyright (C) 2003 Red Hat, Inc. 7 * Copyright (C) 2003 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 <assert.h> 33 #include "xdgmimemagic.h" 34 #include "xdgmimeint.h" 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <ctype.h> 39 #include <errno.h> 40 #include <limits.h> 41 42 #ifndef FALSE 43 #define FALSE (0) 44 #endif 45 46 #ifndef TRUE 47 #define TRUE (!FALSE) 48 #endif 49 50 #if !defined getc_unlocked && !defined HAVE_GETC_UNLOCKED 51 # define getc_unlocked(fp) getc (fp) 52 #endif 53 54 typedef struct XdgMimeMagicMatch XdgMimeMagicMatch; 55 typedef struct XdgMimeMagicMatchlet XdgMimeMagicMatchlet; 56 57 typedef enum 58 { 59 XDG_MIME_MAGIC_SECTION, 60 XDG_MIME_MAGIC_MAGIC, 61 XDG_MIME_MAGIC_ERROR, 62 XDG_MIME_MAGIC_EOF 63 } XdgMimeMagicState; 64 65 struct XdgMimeMagicMatch 66 { 67 const char *mime_type; 68 int priority; 69 XdgMimeMagicMatchlet *matchlet; 70 XdgMimeMagicMatch *next; 71 }; 72 73 74 struct XdgMimeMagicMatchlet 75 { 76 int indent; 77 int offset; 78 unsigned int value_length; 79 unsigned char *value; 80 unsigned char *mask; 81 unsigned int range_length; 82 unsigned int word_size; 83 XdgMimeMagicMatchlet *next; 84 }; 85 86 87 struct XdgMimeMagic 88 { 89 XdgMimeMagicMatch *match_list; 90 int max_extent; 91 }; 92 93 static XdgMimeMagicMatch * 94 _xdg_mime_magic_match_new (void) 95 { 96 return calloc (1, sizeof (XdgMimeMagicMatch)); 97 } 98 99 100 static XdgMimeMagicMatchlet * 101 _xdg_mime_magic_matchlet_new (void) 102 { 103 XdgMimeMagicMatchlet *matchlet; 104 105 matchlet = malloc (sizeof (XdgMimeMagicMatchlet)); 106 107 matchlet->indent = 0; 108 matchlet->offset = 0; 109 matchlet->value_length = 0; 110 matchlet->value = NULL; 111 matchlet->mask = NULL; 112 matchlet->range_length = 1; 113 matchlet->word_size = 1; 114 matchlet->next = NULL; 115 116 return matchlet; 117 } 118 119 120 static void 121 _xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet) 122 { 123 if (mime_magic_matchlet) 124 { 125 if (mime_magic_matchlet->next) 126 _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next); 127 if (mime_magic_matchlet->value) 128 free (mime_magic_matchlet->value); 129 if (mime_magic_matchlet->mask) 130 free (mime_magic_matchlet->mask); 131 free (mime_magic_matchlet); 132 } 133 } 134 135 136 /* Frees mime_magic_match and the remainder of its list 137 */ 138 static void 139 _xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match) 140 { 141 XdgMimeMagicMatch *ptr, *next; 142 143 ptr = mime_magic_match; 144 while (ptr) 145 { 146 next = ptr->next; 147 148 if (ptr->mime_type) 149 free ((void *) ptr->mime_type); 150 if (ptr->matchlet) 151 _xdg_mime_magic_matchlet_free (ptr->matchlet); 152 free (ptr); 153 154 ptr = next; 155 } 156 } 157 158 /* Reads in a hunk of data until a newline character or a '\000' is hit. The 159 * returned string is null terminated, and doesn't include the newline. 160 */ 161 static unsigned char * 162 _xdg_mime_magic_read_to_newline (FILE *magic_file, 163 int *end_of_file) 164 { 165 unsigned char *retval; 166 int c; 167 int len, pos; 168 169 len = 128; 170 pos = 0; 171 retval = malloc (len); 172 *end_of_file = FALSE; 173 174 while (TRUE) 175 { 176 c = getc_unlocked (magic_file); 177 if (c == EOF) 178 { 179 *end_of_file = TRUE; 180 break; 181 } 182 if (c == '\n' || c == '\000') 183 break; 184 retval[pos++] = (unsigned char) c; 185 if (pos % 128 == 127) 186 { 187 len = len + 128; 188 retval = realloc (retval, len); 189 } 190 } 191 192 retval[pos] = '\000'; 193 return retval; 194 } 195 196 /* Returns the number read from the file, or -1 if no number could be read. 197 */ 198 static int 199 _xdg_mime_magic_read_a_number (FILE *magic_file, 200 int *end_of_file) 201 { 202 /* LONG_MAX is about 20 characters on my system */ 203 #define MAX_NUMBER_SIZE 30 204 char number_string[MAX_NUMBER_SIZE + 1]; 205 int pos = 0; 206 int c; 207 long retval = -1; 208 209 while (TRUE) 210 { 211 c = getc_unlocked (magic_file); 212 213 if (c == EOF) 214 { 215 *end_of_file = TRUE; 216 break; 217 } 218 if (! isdigit (c)) 219 { 220 ungetc (c, magic_file); 221 break; 222 } 223 number_string[pos] = (char) c; 224 pos++; 225 if (pos == MAX_NUMBER_SIZE) 226 break; 227 } 228 if (pos > 0) 229 { 230 number_string[pos] = '\000'; 231 errno = 0; 232 retval = strtol (number_string, NULL, 10); 233 234 if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0)) 235 return -1; 236 } 237 238 return retval; 239 } 240 241 /* Headers are of the format: 242 * [<priority>:<mime-type>] 243 */ 244 static XdgMimeMagicState 245 _xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match) 246 { 247 int c; 248 char *buffer; 249 char *end_ptr; 250 int end_of_file = 0; 251 252 assert (magic_file != NULL); 253 assert (match != NULL); 254 255 c = getc_unlocked (magic_file); 256 if (c == EOF) 257 return XDG_MIME_MAGIC_EOF; 258 if (c != '[') 259 return XDG_MIME_MAGIC_ERROR; 260 261 match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); 262 if (end_of_file) 263 return XDG_MIME_MAGIC_EOF; 264 if (match->priority == -1) 265 return XDG_MIME_MAGIC_ERROR; 266 267 c = getc_unlocked (magic_file); 268 if (c == EOF) 269 return XDG_MIME_MAGIC_EOF; 270 if (c != ':') 271 return XDG_MIME_MAGIC_ERROR; 272 273 buffer = (char *)_xdg_mime_magic_read_to_newline (magic_file, &end_of_file); 274 if (end_of_file) 275 return XDG_MIME_MAGIC_EOF; 276 277 end_ptr = buffer; 278 while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n') 279 end_ptr++; 280 if (*end_ptr != ']') 281 { 282 free (buffer); 283 return XDG_MIME_MAGIC_ERROR; 284 } 285 *end_ptr = '\000'; 286 287 match->mime_type = strdup (buffer); 288 free (buffer); 289 290 return XDG_MIME_MAGIC_MAGIC; 291 } 292 293 static XdgMimeMagicState 294 _xdg_mime_magic_parse_error (FILE *magic_file) 295 { 296 int c; 297 298 while (1) 299 { 300 c = getc_unlocked (magic_file); 301 if (c == EOF) 302 return XDG_MIME_MAGIC_EOF; 303 if (c == '\n') 304 return XDG_MIME_MAGIC_SECTION; 305 } 306 } 307 308 /* Headers are of the format: 309 * [ indent ] ">" start-offset "=" value 310 * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n" 311 */ 312 static XdgMimeMagicState 313 _xdg_mime_magic_parse_magic_line (FILE *magic_file, 314 XdgMimeMagicMatch *match) 315 { 316 XdgMimeMagicMatchlet *matchlet; 317 int c; 318 int end_of_file; 319 int indent = 0; 320 int bytes_read; 321 322 assert (magic_file != NULL); 323 324 /* Sniff the buffer to make sure it's a valid line */ 325 c = getc_unlocked (magic_file); 326 if (c == EOF) 327 return XDG_MIME_MAGIC_EOF; 328 else if (c == '[') 329 { 330 ungetc (c, magic_file); 331 return XDG_MIME_MAGIC_SECTION; 332 } 333 else if (c == '\n') 334 return XDG_MIME_MAGIC_MAGIC; 335 336 /* At this point, it must be a digit or a '>' */ 337 end_of_file = FALSE; 338 if (isdigit (c)) 339 { 340 ungetc (c, magic_file); 341 indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); 342 if (end_of_file) 343 return XDG_MIME_MAGIC_EOF; 344 if (indent == -1) 345 return XDG_MIME_MAGIC_ERROR; 346 c = getc_unlocked (magic_file); 347 if (c == EOF) 348 return XDG_MIME_MAGIC_EOF; 349 } 350 351 if (c != '>') 352 return XDG_MIME_MAGIC_ERROR; 353 354 matchlet = _xdg_mime_magic_matchlet_new (); 355 matchlet->indent = indent; 356 matchlet->offset = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); 357 if (end_of_file) 358 { 359 _xdg_mime_magic_matchlet_free (matchlet); 360 return XDG_MIME_MAGIC_EOF; 361 } 362 if (matchlet->offset == -1) 363 { 364 _xdg_mime_magic_matchlet_free (matchlet); 365 return XDG_MIME_MAGIC_ERROR; 366 } 367 c = getc_unlocked (magic_file); 368 if (c == EOF) 369 { 370 _xdg_mime_magic_matchlet_free (matchlet); 371 return XDG_MIME_MAGIC_EOF; 372 } 373 else if (c != '=') 374 { 375 _xdg_mime_magic_matchlet_free (matchlet); 376 return XDG_MIME_MAGIC_ERROR; 377 } 378 379 /* Next two bytes determine how long the value is */ 380 matchlet->value_length = 0; 381 c = getc_unlocked (magic_file); 382 if (c == EOF) 383 { 384 _xdg_mime_magic_matchlet_free (matchlet); 385 return XDG_MIME_MAGIC_EOF; 386 } 387 matchlet->value_length = c & 0xFF; 388 matchlet->value_length = matchlet->value_length << 8; 389 390 c = getc_unlocked (magic_file); 391 if (c == EOF) 392 { 393 _xdg_mime_magic_matchlet_free (matchlet); 394 return XDG_MIME_MAGIC_EOF; 395 } 396 matchlet->value_length = matchlet->value_length + (c & 0xFF); 397 398 matchlet->value = malloc (matchlet->value_length); 399 400 /* OOM */ 401 if (matchlet->value == NULL) 402 { 403 _xdg_mime_magic_matchlet_free (matchlet); 404 return XDG_MIME_MAGIC_ERROR; 405 } 406 bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file); 407 if (bytes_read != matchlet->value_length) 408 { 409 _xdg_mime_magic_matchlet_free (matchlet); 410 if (feof (magic_file)) 411 return XDG_MIME_MAGIC_EOF; 412 else 413 return XDG_MIME_MAGIC_ERROR; 414 } 415 416 c = getc_unlocked (magic_file); 417 if (c == '&') 418 { 419 matchlet->mask = malloc (matchlet->value_length); 420 /* OOM */ 421 if (matchlet->mask == NULL) 422 { 423 _xdg_mime_magic_matchlet_free (matchlet); 424 return XDG_MIME_MAGIC_ERROR; 425 } 426 bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file); 427 if (bytes_read != matchlet->value_length) 428 { 429 _xdg_mime_magic_matchlet_free (matchlet); 430 if (feof (magic_file)) 431 return XDG_MIME_MAGIC_EOF; 432 else 433 return XDG_MIME_MAGIC_ERROR; 434 } 435 c = getc_unlocked (magic_file); 436 } 437 438 if (c == '~') 439 { 440 matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); 441 if (end_of_file) 442 { 443 _xdg_mime_magic_matchlet_free (matchlet); 444 return XDG_MIME_MAGIC_EOF; 445 } 446 if (matchlet->word_size != 0 && 447 matchlet->word_size != 1 && 448 matchlet->word_size != 2 && 449 matchlet->word_size != 4) 450 { 451 _xdg_mime_magic_matchlet_free (matchlet); 452 return XDG_MIME_MAGIC_ERROR; 453 } 454 c = getc_unlocked (magic_file); 455 } 456 457 if (c == '+') 458 { 459 matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); 460 if (end_of_file) 461 { 462 _xdg_mime_magic_matchlet_free (matchlet); 463 return XDG_MIME_MAGIC_EOF; 464 } 465 if (matchlet->range_length == -1) 466 { 467 _xdg_mime_magic_matchlet_free (matchlet); 468 return XDG_MIME_MAGIC_ERROR; 469 } 470 c = getc_unlocked (magic_file); 471 } 472 473 474 if (c == '\n') 475 { 476 /* We clean up the matchlet, byte swapping if needed */ 477 if (matchlet->word_size > 1) 478 { 479 int i; 480 if (matchlet->value_length % matchlet->word_size != 0) 481 { 482 _xdg_mime_magic_matchlet_free (matchlet); 483 return XDG_MIME_MAGIC_ERROR; 484 } 485 /* FIXME: need to get this defined in a <config.h> style file */ 486 #if LITTLE_ENDIAN 487 for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size) 488 { 489 if (matchlet->word_size == 2) 490 *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i))); 491 else if (matchlet->word_size == 4) 492 *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i))); 493 if (matchlet->mask) 494 { 495 if (matchlet->word_size == 2) 496 *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i))); 497 else if (matchlet->word_size == 4) 498 *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i))); 499 500 } 501 } 502 #endif 503 } 504 505 matchlet->next = match->matchlet; 506 match->matchlet = matchlet; 507 508 509 return XDG_MIME_MAGIC_MAGIC; 510 } 511 512 _xdg_mime_magic_matchlet_free (matchlet); 513 if (c == EOF) 514 return XDG_MIME_MAGIC_EOF; 515 516 return XDG_MIME_MAGIC_ERROR; 517 } 518 519 static int 520 _xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet, 521 const void *data, 522 size_t len) 523 { 524 int i, j; 525 for (i = matchlet->offset; i < matchlet->offset + matchlet->range_length; i++) 526 { 527 int valid_matchlet = TRUE; 528 529 if (i + matchlet->value_length > len) 530 return FALSE; 531 532 if (matchlet->mask) 533 { 534 for (j = 0; j < matchlet->value_length; j++) 535 { 536 if ((matchlet->value[j] & matchlet->mask[j]) != 537 ((((unsigned char *) data)[j + i]) & matchlet->mask[j])) 538 { 539 valid_matchlet = FALSE; 540 break; 541 } 542 } 543 } 544 else 545 { 546 for (j = 0; j < matchlet->value_length; j++) 547 { 548 if (matchlet->value[j] != ((unsigned char *) data)[j + i]) 549 { 550 valid_matchlet = FALSE; 551 break; 552 } 553 } 554 } 555 if (valid_matchlet) 556 return TRUE; 557 } 558 return FALSE; 559 } 560 561 static int 562 _xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet, 563 const void *data, 564 size_t len, 565 int indent) 566 { 567 while ((matchlet != NULL) && (matchlet->indent == indent)) 568 { 569 if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len)) 570 { 571 if ((matchlet->next == NULL) || 572 (matchlet->next->indent <= indent)) 573 return TRUE; 574 575 if (_xdg_mime_magic_matchlet_compare_level (matchlet->next, 576 data, 577 len, 578 indent + 1)) 579 return TRUE; 580 } 581 582 do 583 { 584 matchlet = matchlet->next; 585 } 586 while (matchlet && matchlet->indent > indent); 587 } 588 589 return FALSE; 590 } 591 592 static int 593 _xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match, 594 const void *data, 595 size_t len) 596 { 597 return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0); 598 } 599 600 static void 601 _xdg_mime_magic_insert_match (XdgMimeMagic *mime_magic, 602 XdgMimeMagicMatch *match) 603 { 604 XdgMimeMagicMatch *list; 605 606 if (mime_magic->match_list == NULL) 607 { 608 mime_magic->match_list = match; 609 return; 610 } 611 612 if (match->priority > mime_magic->match_list->priority) 613 { 614 match->next = mime_magic->match_list; 615 mime_magic->match_list = match; 616 return; 617 } 618 619 list = mime_magic->match_list; 620 while (list->next != NULL) 621 { 622 if (list->next->priority < match->priority) 623 { 624 match->next = list->next; 625 list->next = match; 626 return; 627 } 628 list = list->next; 629 } 630 list->next = match; 631 match->next = NULL; 632 } 633 634 XdgMimeMagic * 635 _xdg_mime_magic_new (void) 636 { 637 return calloc (1, sizeof (XdgMimeMagic)); 638 } 639 640 void 641 _xdg_mime_magic_free (XdgMimeMagic *mime_magic) 642 { 643 if (mime_magic) { 644 _xdg_mime_magic_match_free (mime_magic->match_list); 645 free (mime_magic); 646 } 647 } 648 649 int 650 _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic) 651 { 652 return mime_magic->max_extent; 653 } 654 655 const char * 656 _xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic, 657 const void *data, 658 size_t len, 659 int *result_prio, 660 const char *mime_types[], 661 int n_mime_types) 662 { 663 XdgMimeMagicMatch *match; 664 const char *mime_type; 665 int n; 666 int prio; 667 668 prio = 0; 669 mime_type = NULL; 670 for (match = mime_magic->match_list; match; match = match->next) 671 { 672 if (_xdg_mime_magic_match_compare_to_data (match, data, len)) 673 { 674 prio = match->priority; 675 mime_type = match->mime_type; 676 break; 677 } 678 else 679 { 680 for (n = 0; n < n_mime_types; n++) 681 { 682 if (mime_types[n] && 683 _xdg_mime_mime_type_equal (mime_types[n], match->mime_type)) 684 mime_types[n] = NULL; 685 } 686 } 687 } 688 689 if (mime_type == NULL) 690 { 691 for (n = 0; n < n_mime_types; n++) 692 { 693 if (mime_types[n]) 694 mime_type = mime_types[n]; 695 } 696 } 697 698 if (result_prio) 699 *result_prio = prio; 700 701 return mime_type; 702 } 703 704 static void 705 _xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic) 706 { 707 XdgMimeMagicMatch *match; 708 int max_extent = 0; 709 710 for (match = mime_magic->match_list; match; match = match->next) 711 { 712 XdgMimeMagicMatchlet *matchlet; 713 714 for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next) 715 { 716 int extent; 717 718 extent = matchlet->value_length + matchlet->offset + matchlet->range_length; 719 if (max_extent < extent) 720 max_extent = extent; 721 } 722 } 723 724 mime_magic->max_extent = max_extent; 725 } 726 727 static XdgMimeMagicMatchlet * 728 _xdg_mime_magic_matchlet_mirror (XdgMimeMagicMatchlet *matchlets) 729 { 730 XdgMimeMagicMatchlet *new_list; 731 XdgMimeMagicMatchlet *tmp; 732 733 if ((matchlets == NULL) || (matchlets->next == NULL)) 734 return matchlets; 735 736 new_list = NULL; 737 tmp = matchlets; 738 while (tmp != NULL) 739 { 740 XdgMimeMagicMatchlet *matchlet; 741 742 matchlet = tmp; 743 tmp = tmp->next; 744 matchlet->next = new_list; 745 new_list = matchlet; 746 } 747 748 return new_list; 749 750 } 751 752 static void 753 _xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic, 754 FILE *magic_file) 755 { 756 XdgMimeMagicState state; 757 XdgMimeMagicMatch *match = NULL; /* Quiet compiler */ 758 759 state = XDG_MIME_MAGIC_SECTION; 760 761 while (state != XDG_MIME_MAGIC_EOF) 762 { 763 switch (state) 764 { 765 case XDG_MIME_MAGIC_SECTION: 766 match = _xdg_mime_magic_match_new (); 767 state = _xdg_mime_magic_parse_header (magic_file, match); 768 if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR) 769 _xdg_mime_magic_match_free (match); 770 break; 771 case XDG_MIME_MAGIC_MAGIC: 772 state = _xdg_mime_magic_parse_magic_line (magic_file, match); 773 if (state == XDG_MIME_MAGIC_SECTION || 774 (state == XDG_MIME_MAGIC_EOF && match->mime_type)) 775 { 776 match->matchlet = _xdg_mime_magic_matchlet_mirror (match->matchlet); 777 _xdg_mime_magic_insert_match (mime_magic, match); 778 } 779 else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR) 780 _xdg_mime_magic_match_free (match); 781 break; 782 case XDG_MIME_MAGIC_ERROR: 783 state = _xdg_mime_magic_parse_error (magic_file); 784 break; 785 case XDG_MIME_MAGIC_EOF: 786 default: 787 /* Make the compiler happy */ 788 assert (0); 789 } 790 } 791 _xdg_mime_update_mime_magic_extents (mime_magic); 792 } 793 794 void 795 _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic, 796 const char *file_name) 797 { 798 FILE *magic_file; 799 char header[12]; 800 801 magic_file = fopen (file_name, "r"); 802 803 if (magic_file == NULL) 804 return; 805 806 if (fread (header, 1, 12, magic_file) == 12) 807 { 808 if (memcmp ("MIME-Magic\0\n", header, 12) == 0) 809 _xdg_mime_magic_read_magic_file (mime_magic, magic_file); 810 } 811 812 fclose (magic_file); 813 } 814