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