Home | History | Annotate | Download | only in xdgmime
      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