fbc03afc5f33cce5bcd0b39793cf87cb2ef3f3aa
[platform/core/appfw/xdgmime.git] / xdgmime / src / 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  * 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   if (retval == NULL)
173     return NULL;
174
175   *end_of_file = FALSE;
176
177   while (TRUE)
178     {
179       c = getc_unlocked (magic_file);
180       if (c == EOF)
181         {
182           *end_of_file = TRUE;
183           break;
184         }
185       if (c == '\n' || c == '\000')
186         break;
187       retval[pos++] = (unsigned char) c;
188       if (pos % 128 == 127)
189         {
190           len = len + 128;
191           unsigned char *tmp = realloc (retval, len);
192           if (tmp == NULL)
193             {
194               free(retval);
195               return NULL;
196             }
197           retval = tmp;
198         }
199     }
200
201   retval[pos] = '\000';
202   return retval;
203 }
204
205 /* Returns the number read from the file, or -1 if no number could be read.
206  */
207 static int
208 _xdg_mime_magic_read_a_number (FILE *magic_file,
209                                int  *end_of_file)
210 {
211   /* LONG_MAX is about 20 characters on my system */
212 #define MAX_NUMBER_SIZE 30
213   char number_string[MAX_NUMBER_SIZE + 1];
214   int pos = 0;
215   int c;
216   long retval = -1;
217
218   while (TRUE)
219     {
220       c = getc_unlocked (magic_file);
221
222       if (c == EOF)
223         {
224           *end_of_file = TRUE;
225           break;
226         }
227       if (! isdigit (c))
228         {
229           ungetc (c, magic_file);
230           break;
231         }
232       number_string[pos] = (char) c;
233       pos++;
234       if (pos == MAX_NUMBER_SIZE)
235         break;
236     }
237   if (pos > 0)
238     {
239       number_string[pos] = '\000';
240       errno = 0;
241       retval = strtol (number_string, NULL, 10);
242
243       if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0))
244         return -1;
245     }
246
247   return retval;
248 }
249
250 /* Headers are of the format:
251  * [<priority>:<mime-type>]
252  */
253 static XdgMimeMagicState
254 _xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match)
255 {
256   int c;
257   char *buffer;
258   char *end_ptr;
259   int end_of_file = 0;
260
261   assert (magic_file != NULL);
262   assert (match != NULL);
263
264   c = getc_unlocked (magic_file);
265   if (c == EOF)
266     return XDG_MIME_MAGIC_EOF;
267   if (c != '[')
268     return XDG_MIME_MAGIC_ERROR;
269
270   match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
271   if (end_of_file)
272     return XDG_MIME_MAGIC_EOF;
273   if (match->priority == -1)
274     return XDG_MIME_MAGIC_ERROR;
275
276   c = getc_unlocked (magic_file);
277   if (c == EOF)
278     return XDG_MIME_MAGIC_EOF;
279   if (c != ':')
280     return XDG_MIME_MAGIC_ERROR;
281
282   buffer = (char *)_xdg_mime_magic_read_to_newline (magic_file, &end_of_file);
283   if (end_of_file)
284     return XDG_MIME_MAGIC_EOF;
285
286   end_ptr = buffer;
287   while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n')
288     end_ptr++;
289   if (*end_ptr != ']')
290     {
291       free (buffer);
292       return XDG_MIME_MAGIC_ERROR;
293     }
294   *end_ptr = '\000';
295
296   match->mime_type = strdup (buffer);
297   free (buffer);
298
299   return XDG_MIME_MAGIC_MAGIC;
300 }
301
302 static XdgMimeMagicState
303 _xdg_mime_magic_parse_error (FILE *magic_file)
304 {
305   int c;
306
307   while (1)
308     {
309       c = getc_unlocked (magic_file);
310       if (c == EOF)
311         return XDG_MIME_MAGIC_EOF;
312       if (c == '\n')
313         return XDG_MIME_MAGIC_SECTION;
314     }
315 }
316
317 /* Headers are of the format:
318  * [ indent ] ">" start-offset "=" value
319  * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
320  */
321 static XdgMimeMagicState
322 _xdg_mime_magic_parse_magic_line (FILE              *magic_file,
323                                   XdgMimeMagicMatch *match)
324 {
325   XdgMimeMagicMatchlet *matchlet;
326   int c;
327   int end_of_file;
328   int indent = 0;
329   int bytes_read;
330
331   assert (magic_file != NULL);
332
333   /* Sniff the buffer to make sure it's a valid line */
334   c = getc_unlocked (magic_file);
335   if (c == EOF)
336     return XDG_MIME_MAGIC_EOF;
337   else if (c == '[')
338     {
339       ungetc (c, magic_file);
340       return XDG_MIME_MAGIC_SECTION;
341     }
342   else if (c == '\n')
343     return XDG_MIME_MAGIC_MAGIC;
344
345   /* At this point, it must be a digit or a '>' */
346   end_of_file = FALSE;
347   if (isdigit (c))
348     {
349       ungetc (c, magic_file);
350       indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
351       if (end_of_file)
352         return XDG_MIME_MAGIC_EOF;
353       if (indent == -1)
354         return XDG_MIME_MAGIC_ERROR;
355       c = getc_unlocked (magic_file);
356       if (c == EOF)
357         return XDG_MIME_MAGIC_EOF;
358     }
359
360   if (c != '>')
361     return XDG_MIME_MAGIC_ERROR;
362
363   matchlet = _xdg_mime_magic_matchlet_new ();
364   matchlet->indent = indent;
365   matchlet->offset = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
366   if (end_of_file)
367     {
368       _xdg_mime_magic_matchlet_free (matchlet);
369       return XDG_MIME_MAGIC_EOF;
370     }
371   if (matchlet->offset == -1)
372     {
373       _xdg_mime_magic_matchlet_free (matchlet);
374       return XDG_MIME_MAGIC_ERROR;
375     }
376   c = getc_unlocked (magic_file);
377   if (c == EOF)
378     {
379       _xdg_mime_magic_matchlet_free (matchlet);
380       return XDG_MIME_MAGIC_EOF;
381     }
382   else if (c != '=')
383     {
384       _xdg_mime_magic_matchlet_free (matchlet);
385       return XDG_MIME_MAGIC_ERROR;
386     }
387
388   /* Next two bytes determine how long the value is */
389   matchlet->value_length = 0;
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 = c & 0xFF;
397   matchlet->value_length = matchlet->value_length << 8;
398
399   c = getc_unlocked (magic_file);
400   if (c == EOF)
401     {
402       _xdg_mime_magic_matchlet_free (matchlet);
403       return XDG_MIME_MAGIC_EOF;
404     }
405   matchlet->value_length = matchlet->value_length + (c & 0xFF);
406
407   matchlet->value = malloc (matchlet->value_length);
408
409   /* OOM */
410   if (matchlet->value == NULL)
411     {
412       _xdg_mime_magic_matchlet_free (matchlet);
413       return XDG_MIME_MAGIC_ERROR;
414     }
415   bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file);
416   if (bytes_read != matchlet->value_length)
417     {
418       _xdg_mime_magic_matchlet_free (matchlet);
419       if (feof (magic_file))
420         return XDG_MIME_MAGIC_EOF;
421       else
422         return XDG_MIME_MAGIC_ERROR;
423     }
424
425   c = getc_unlocked (magic_file);
426   if (c == '&')
427     {
428       matchlet->mask = malloc (matchlet->value_length);
429       /* OOM */
430       if (matchlet->mask == NULL)
431         {
432           _xdg_mime_magic_matchlet_free (matchlet);
433           return XDG_MIME_MAGIC_ERROR;
434         }
435       bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file);
436       if (bytes_read != matchlet->value_length)
437         {
438           _xdg_mime_magic_matchlet_free (matchlet);
439           if (feof (magic_file))
440             return XDG_MIME_MAGIC_EOF;
441           else
442             return XDG_MIME_MAGIC_ERROR;
443         }
444       c = getc_unlocked (magic_file);
445     }
446
447   if (c == '~')
448     {
449       matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
450       if (end_of_file)
451         {
452           _xdg_mime_magic_matchlet_free (matchlet);
453           return XDG_MIME_MAGIC_EOF;
454         }
455       if (matchlet->word_size != 0 &&
456           matchlet->word_size != 1 &&
457           matchlet->word_size != 2 &&
458           matchlet->word_size != 4)
459         {
460           _xdg_mime_magic_matchlet_free (matchlet);
461           return XDG_MIME_MAGIC_ERROR;
462         }
463       c = getc_unlocked (magic_file);
464     }
465
466   if (c == '+')
467     {
468       matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
469       if (end_of_file)
470         {
471           _xdg_mime_magic_matchlet_free (matchlet);
472           return XDG_MIME_MAGIC_EOF;
473         }
474       if (matchlet->range_length == -1)
475         {
476           _xdg_mime_magic_matchlet_free (matchlet);
477           return XDG_MIME_MAGIC_ERROR;
478         }
479       c = getc_unlocked (magic_file);
480     }
481
482
483   if (c == '\n')
484     {
485       /* We clean up the matchlet, byte swapping if needed */
486       if (matchlet->word_size > 1)
487         {
488           int i;
489           if (matchlet->value_length % matchlet->word_size != 0)
490             {
491               _xdg_mime_magic_matchlet_free (matchlet);
492               return XDG_MIME_MAGIC_ERROR;
493             }
494           /* FIXME: need to get this defined in a <config.h> style file */
495 #if LITTLE_ENDIAN
496           for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size)
497             {
498               if (matchlet->word_size == 2)
499                 *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i)));
500               else if (matchlet->word_size == 4)
501                 *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i)));
502               if (matchlet->mask)
503                 {
504                   if (matchlet->word_size == 2)
505                     *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i)));
506                   else if (matchlet->word_size == 4)
507                     *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i)));
508
509                 }
510             }
511 #endif
512         }
513
514       matchlet->next = match->matchlet;
515       match->matchlet = matchlet;
516
517
518       return XDG_MIME_MAGIC_MAGIC;
519     }
520
521   _xdg_mime_magic_matchlet_free (matchlet);
522   if (c == EOF)
523     return XDG_MIME_MAGIC_EOF;
524
525   return XDG_MIME_MAGIC_ERROR;
526 }
527
528 static int
529 _xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet,
530                                           const void           *data,
531                                           size_t                len)
532 {
533   int i, j;
534   for (i = matchlet->offset; i < matchlet->offset + matchlet->range_length; i++)
535     {
536       int valid_matchlet = TRUE;
537
538       if (i + matchlet->value_length > len)
539         return FALSE;
540
541       if (matchlet->mask)
542         {
543           for (j = 0; j < matchlet->value_length; j++)
544             {
545               if ((matchlet->value[j] & matchlet->mask[j]) !=
546                   ((((unsigned char *) data)[j + i]) & matchlet->mask[j]))
547                 {
548                   valid_matchlet = FALSE;
549                   break;
550                 }
551             }
552         }
553       else
554         {
555           for (j = 0; j <  matchlet->value_length; j++)
556             {
557               if (matchlet->value[j] != ((unsigned char *) data)[j + i])
558                 {
559                   valid_matchlet = FALSE;
560                   break;
561                 }
562             }
563         }
564       if (valid_matchlet)
565         return TRUE;
566     }
567   return FALSE;
568 }
569
570 static int
571 _xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet,
572                                         const void           *data,
573                                         size_t                len,
574                                         int                   indent)
575 {
576   while ((matchlet != NULL) && (matchlet->indent == indent))
577     {
578       if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len))
579         {
580           if ((matchlet->next == NULL) ||
581               (matchlet->next->indent <= indent))
582             return TRUE;
583
584           if (_xdg_mime_magic_matchlet_compare_level (matchlet->next,
585                                                       data,
586                                                       len,
587                                                       indent + 1))
588             return TRUE;
589         }
590
591       do
592         {
593           matchlet = matchlet->next;
594         }
595       while (matchlet && matchlet->indent > indent);
596     }
597
598   return FALSE;
599 }
600
601 static int
602 _xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match,
603                                        const void        *data,
604                                        size_t             len)
605 {
606   return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0);
607 }
608
609 static void
610 _xdg_mime_magic_insert_match (XdgMimeMagic      *mime_magic,
611                               XdgMimeMagicMatch *match)
612 {
613   XdgMimeMagicMatch *list;
614
615   if (mime_magic->match_list == NULL)
616     {
617       mime_magic->match_list = match;
618       return;
619     }
620
621   if (match->priority > mime_magic->match_list->priority)
622     {
623       match->next = mime_magic->match_list;
624       mime_magic->match_list = match;
625       return;
626     }
627
628   list = mime_magic->match_list;
629   while (list->next != NULL)
630     {
631       if (list->next->priority < match->priority)
632         {
633           match->next = list->next;
634           list->next = match;
635           return;
636         }
637       list = list->next;
638     }
639   list->next = match;
640   match->next = NULL;
641 }
642
643 XdgMimeMagic *
644 _xdg_mime_magic_new (void)
645 {
646   return calloc (1, sizeof (XdgMimeMagic));
647 }
648
649 void
650 _xdg_mime_magic_free (XdgMimeMagic *mime_magic)
651 {
652   if (mime_magic) {
653     _xdg_mime_magic_match_free (mime_magic->match_list);
654     free (mime_magic);
655   }
656 }
657
658 int
659 _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic)
660 {
661   return mime_magic->max_extent;
662 }
663
664 const char *
665 _xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic,
666                              const void   *data,
667                              size_t        len,
668                              int           *result_prio,
669                              const char   *mime_types[],
670                              int           n_mime_types)
671 {
672   XdgMimeMagicMatch *match;
673   const char *mime_type;
674   int n;
675   int prio;
676
677   prio = 0;
678   mime_type = NULL;
679   for (match = mime_magic->match_list; match; match = match->next)
680     {
681       if (_xdg_mime_magic_match_compare_to_data (match, data, len))
682         {
683           prio = match->priority;
684           mime_type = match->mime_type;
685           break;
686         }
687       else 
688         {
689           for (n = 0; n < n_mime_types; n++)
690             {
691               if (mime_types[n] && 
692                   _xdg_mime_mime_type_equal (mime_types[n], match->mime_type))
693                 mime_types[n] = NULL;
694             }
695         }
696     }
697
698   if (mime_type == NULL)
699     {
700       for (n = 0; n < n_mime_types; n++)
701         {
702           if (mime_types[n])
703             mime_type = mime_types[n];
704         }
705     }
706   
707   if (result_prio)
708     *result_prio = prio;
709
710   return mime_type;
711 }
712
713 static void
714 _xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic)
715 {
716   XdgMimeMagicMatch *match;
717   int max_extent = 0;
718
719   for (match = mime_magic->match_list; match; match = match->next)
720     {
721       XdgMimeMagicMatchlet *matchlet;
722
723       for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next)
724         {
725           int extent;
726
727           extent = matchlet->value_length + matchlet->offset + matchlet->range_length;
728           if (max_extent < extent)
729             max_extent = extent;
730         }
731     }
732
733   mime_magic->max_extent = max_extent;
734 }
735
736 static XdgMimeMagicMatchlet *
737 _xdg_mime_magic_matchlet_mirror (XdgMimeMagicMatchlet *matchlets)
738 {
739   XdgMimeMagicMatchlet *new_list;
740   XdgMimeMagicMatchlet *tmp;
741
742   if ((matchlets == NULL) || (matchlets->next == NULL))
743     return matchlets;
744
745   new_list = NULL;
746   tmp = matchlets;
747   while (tmp != NULL)
748     {
749       XdgMimeMagicMatchlet *matchlet;
750
751       matchlet = tmp;
752       tmp = tmp->next;
753       matchlet->next = new_list;
754       new_list = matchlet;
755     }
756
757   return new_list;
758
759 }
760
761 static void
762 _xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic,
763                                  FILE         *magic_file)
764 {
765   XdgMimeMagicState state;
766   XdgMimeMagicMatch *match = NULL; /* Quiet compiler */
767
768   state = XDG_MIME_MAGIC_SECTION;
769
770   while (state != XDG_MIME_MAGIC_EOF)
771     {
772       switch (state)
773         {
774         case XDG_MIME_MAGIC_SECTION:
775           match = _xdg_mime_magic_match_new ();
776           state = _xdg_mime_magic_parse_header (magic_file, match);
777           if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
778             _xdg_mime_magic_match_free (match);
779           break;
780         case XDG_MIME_MAGIC_MAGIC:
781           state = _xdg_mime_magic_parse_magic_line (magic_file, match);
782           if (state == XDG_MIME_MAGIC_SECTION ||
783               (state == XDG_MIME_MAGIC_EOF && match->mime_type))
784             {
785               match->matchlet = _xdg_mime_magic_matchlet_mirror (match->matchlet);
786               _xdg_mime_magic_insert_match (mime_magic, match);
787             }
788           else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
789             _xdg_mime_magic_match_free (match);
790           break;
791         case XDG_MIME_MAGIC_ERROR:
792           state = _xdg_mime_magic_parse_error (magic_file);
793           break;
794         case XDG_MIME_MAGIC_EOF:
795         default:
796           /* Make the compiler happy */
797           assert (0);
798         }
799     }
800   _xdg_mime_update_mime_magic_extents (mime_magic);
801 }
802
803 void
804 _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic,
805                                 const char   *file_name)
806 {
807   FILE *magic_file;
808   char header[12];
809
810   magic_file = fopen (file_name, "r");
811
812   if (magic_file == NULL)
813     return;
814
815   if (fread (header, 1, 12, magic_file) == 12)
816     {
817       if (memcmp ("MIME-Magic\0\n", header, 12) == 0)
818         _xdg_mime_magic_read_magic_file (mime_magic, magic_file);
819     }
820
821   fclose (magic_file);
822 }