1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* xdgmimemagic.: Private file. Datastructure for storing magic files.
4 * More info can be found at http://www.freedesktop.org/standards/
6 * Copyright (C) 2003 Red Hat, Inc.
7 * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
9 * Licensed under the Academic Free License version 2.0
10 * Or under the following terms:
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.
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.
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.
30 #include "xdgmimemagic.h"
31 #include "xdgmimeint.h"
49 typedef struct XdgMimeMagicMatch XdgMimeMagicMatch;
50 typedef struct XdgMimeMagicMatchlet XdgMimeMagicMatchlet;
54 XDG_MIME_MAGIC_SECTION,
60 struct XdgMimeMagicMatch
62 const char *mime_type;
64 XdgMimeMagicMatchlet *matchlet;
65 XdgMimeMagicMatch *next;
69 struct XdgMimeMagicMatchlet
73 unsigned int value_length;
76 unsigned int range_length;
77 unsigned int word_size;
78 XdgMimeMagicMatchlet *next;
84 XdgMimeMagicMatch *match_list;
88 static XdgMimeMagicMatch *
89 _xdg_mime_magic_match_new (void)
91 return calloc (1, sizeof (XdgMimeMagicMatch));
95 static XdgMimeMagicMatchlet *
96 _xdg_mime_magic_matchlet_new (void)
98 XdgMimeMagicMatchlet *matchlet;
100 matchlet = malloc (sizeof (XdgMimeMagicMatchlet));
102 matchlet->indent = 0;
103 matchlet->offset = 0;
104 matchlet->value_length = 0;
105 matchlet->value = NULL;
106 matchlet->mask = NULL;
107 matchlet->range_length = 1;
108 matchlet->word_size = 1;
109 matchlet->next = NULL;
116 _xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet)
118 if (mime_magic_matchlet)
120 if (mime_magic_matchlet->next)
121 _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next);
122 if (mime_magic_matchlet->value)
123 free (mime_magic_matchlet->value);
124 if (mime_magic_matchlet->mask)
125 free (mime_magic_matchlet->mask);
126 free (mime_magic_matchlet);
131 /* Frees mime_magic_match and the remainder of its list
134 _xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match)
136 XdgMimeMagicMatch *ptr, *next;
138 ptr = mime_magic_match;
144 free ((void *) ptr->mime_type);
146 _xdg_mime_magic_matchlet_free (ptr->matchlet);
153 /* Reads in a hunk of data until a newline character or a '\000' is hit. The
154 * returned string is null terminated, and doesn't include the newline.
156 static unsigned char *
157 _xdg_mime_magic_read_to_newline (FILE *magic_file,
160 unsigned char *retval;
166 retval = malloc (len);
167 *end_of_file = FALSE;
171 c = fgetc (magic_file);
177 if (c == '\n' || c == '\000')
179 retval[pos++] = (unsigned char) c;
180 if (pos % 128 == 127)
183 retval = realloc (retval, len);
187 retval[pos] = '\000';
191 /* Returns the number read from the file, or -1 if no number could be read.
194 _xdg_mime_magic_read_a_number (FILE *magic_file,
197 /* LONG_MAX is about 20 characters on my system */
198 #define MAX_NUMBER_SIZE 30
199 char number_string[MAX_NUMBER_SIZE];
206 c = fgetc (magic_file);
213 if (! isdigit ((char) c))
215 ungetc (c, magic_file);
218 number_string[pos] = (char) c;
220 if (pos == MAX_NUMBER_SIZE)
225 number_string[pos] = '\000';
227 retval = strtol (number_string, NULL, 10);
229 if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0))
236 /* Headers are of the format:
237 * [<priority>:<mime-type>]
239 static XdgMimeMagicState
240 _xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match)
247 assert (magic_file != NULL);
248 assert (match != NULL);
250 c = fgetc (magic_file);
252 return XDG_MIME_MAGIC_EOF;
254 return XDG_MIME_MAGIC_ERROR;
256 match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
258 return XDG_MIME_MAGIC_EOF;
259 if (match->priority == -1)
260 return XDG_MIME_MAGIC_ERROR;
262 c = fgetc (magic_file);
264 return XDG_MIME_MAGIC_EOF;
266 return XDG_MIME_MAGIC_ERROR;
268 buffer = _xdg_mime_magic_read_to_newline (magic_file, &end_of_file);
270 return XDG_MIME_MAGIC_EOF;
273 while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n')
278 return XDG_MIME_MAGIC_ERROR;
282 match->mime_type = strdup (buffer);
285 return XDG_MIME_MAGIC_MAGIC;
288 static XdgMimeMagicState
289 _xdg_mime_magic_parse_error (FILE *magic_file)
295 c = fgetc (magic_file);
297 return XDG_MIME_MAGIC_EOF;
299 return XDG_MIME_MAGIC_SECTION;
303 /* Headers are of the format:
304 * [ indent ] ">" start-offset "=" value
305 * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
307 static XdgMimeMagicState
308 _xdg_mime_magic_parse_magic_line (FILE *magic_file,
309 XdgMimeMagicMatch *match)
311 XdgMimeMagicMatchlet *matchlet;
317 assert (magic_file != NULL);
319 /* Sniff the buffer to make sure it's a valid line */
320 c = fgetc (magic_file);
322 return XDG_MIME_MAGIC_EOF;
325 ungetc (c, magic_file);
326 return XDG_MIME_MAGIC_SECTION;
329 return XDG_MIME_MAGIC_MAGIC;
331 /* At this point, it must be a digit or a '>' */
335 ungetc (c, magic_file);
336 indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
338 return XDG_MIME_MAGIC_EOF;
340 return XDG_MIME_MAGIC_ERROR;
341 c = fgetc (magic_file);
343 return XDG_MIME_MAGIC_EOF;
347 return XDG_MIME_MAGIC_ERROR;
349 matchlet = _xdg_mime_magic_matchlet_new ();
350 matchlet->indent = indent;
351 matchlet->offset = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
354 _xdg_mime_magic_matchlet_free (matchlet);
355 return XDG_MIME_MAGIC_EOF;
357 if (matchlet->offset == -1)
359 _xdg_mime_magic_matchlet_free (matchlet);
360 return XDG_MIME_MAGIC_ERROR;
362 c = fgetc (magic_file);
365 _xdg_mime_magic_matchlet_free (matchlet);
366 return XDG_MIME_MAGIC_EOF;
370 _xdg_mime_magic_matchlet_free (matchlet);
371 return XDG_MIME_MAGIC_ERROR;
374 /* Next two bytes determine how long the value is */
375 matchlet->value_length = 0;
376 c = fgetc (magic_file);
379 _xdg_mime_magic_matchlet_free (matchlet);
380 return XDG_MIME_MAGIC_EOF;
382 matchlet->value_length = c & 0xFF;
383 matchlet->value_length = matchlet->value_length << 8;
385 c = fgetc (magic_file);
388 _xdg_mime_magic_matchlet_free (matchlet);
389 return XDG_MIME_MAGIC_EOF;
391 matchlet->value_length = matchlet->value_length + (c & 0xFF);
393 matchlet->value = malloc (matchlet->value_length);
396 if (matchlet->value == NULL)
398 _xdg_mime_magic_matchlet_free (matchlet);
399 return XDG_MIME_MAGIC_ERROR;
401 bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file);
402 if (bytes_read != matchlet->value_length)
404 _xdg_mime_magic_matchlet_free (matchlet);
405 if (feof (magic_file))
406 return XDG_MIME_MAGIC_EOF;
408 return XDG_MIME_MAGIC_ERROR;
411 c = fgetc (magic_file);
414 matchlet->mask = malloc (matchlet->value_length);
416 if (matchlet->mask == NULL)
418 _xdg_mime_magic_matchlet_free (matchlet);
419 return XDG_MIME_MAGIC_ERROR;
421 bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file);
422 if (bytes_read != matchlet->value_length)
424 _xdg_mime_magic_matchlet_free (matchlet);
425 if (feof (magic_file))
426 return XDG_MIME_MAGIC_EOF;
428 return XDG_MIME_MAGIC_ERROR;
430 c = fgetc (magic_file);
435 matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
438 _xdg_mime_magic_matchlet_free (matchlet);
439 return XDG_MIME_MAGIC_EOF;
441 if (matchlet->word_size != 0 &&
442 matchlet->word_size != 1 &&
443 matchlet->word_size != 2 &&
444 matchlet->word_size != 4)
446 _xdg_mime_magic_matchlet_free (matchlet);
447 return XDG_MIME_MAGIC_ERROR;
449 c = fgetc (magic_file);
454 matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
457 _xdg_mime_magic_matchlet_free (matchlet);
458 return XDG_MIME_MAGIC_EOF;
460 if (matchlet->range_length == -1)
462 _xdg_mime_magic_matchlet_free (matchlet);
463 return XDG_MIME_MAGIC_ERROR;
465 c = fgetc (magic_file);
471 /* We clean up the matchlet, byte swapping if needed */
472 if (matchlet->word_size > 1)
475 if (matchlet->value_length % matchlet->word_size != 0)
477 _xdg_mime_magic_matchlet_free (matchlet);
478 return XDG_MIME_MAGIC_ERROR;
480 /* FIXME: need to get this defined in a <config.h> style file */
482 for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size)
484 if (matchlet->word_size == 2)
485 *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i)));
486 else if (matchlet->word_size == 4)
487 *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i)));
490 if (matchlet->word_size == 2)
491 *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i)));
492 else if (matchlet->word_size == 4)
493 *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i)));
500 matchlet->next = match->matchlet;
501 match->matchlet = matchlet;
504 return XDG_MIME_MAGIC_MAGIC;
507 _xdg_mime_magic_matchlet_free (matchlet);
509 return XDG_MIME_MAGIC_EOF;
511 return XDG_MIME_MAGIC_ERROR;
515 _xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet,
521 for (i = matchlet->offset; i <= matchlet->offset + matchlet->range_length; i++)
523 int valid_matchlet = TRUE;
525 if (i + matchlet->value_length > len)
530 for (j = 0; j < matchlet->value_length; j++)
532 if ((matchlet->value[j] & matchlet->mask[j]) !=
533 ((((unsigned char *) data)[j + i]) & matchlet->mask[j]))
535 valid_matchlet = FALSE;
542 for (j = 0; j < matchlet->value_length; j++)
544 if (matchlet->value[j] != ((unsigned char *) data)[j + i])
546 valid_matchlet = FALSE;
558 _xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet,
563 while (matchlet != NULL && matchlet->indent == indent)
565 if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len))
567 if (matchlet->next == NULL ||
568 matchlet->next->indent <= indent)
571 if (_xdg_mime_magic_matchlet_compare_level (matchlet->next,
580 matchlet = matchlet->next;
582 while (matchlet && matchlet->indent > indent);
589 _xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match,
593 return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0);
597 _xdg_mime_magic_insert_match (XdgMimeMagic *mime_magic,
598 XdgMimeMagicMatch *match)
600 XdgMimeMagicMatch *list;
602 if (mime_magic->match_list == NULL)
604 mime_magic->match_list = match;
608 if (match->priority > mime_magic->match_list->priority)
610 match->next = mime_magic->match_list;
611 mime_magic->match_list = match;
615 list = mime_magic->match_list;
616 while (list->next != NULL)
618 if (list->next->priority < match->priority)
620 match->next = list->next;
631 _xdg_mime_magic_new (void)
633 return calloc (1, sizeof (XdgMimeMagic));
637 _xdg_mime_magic_free (XdgMimeMagic *mime_magic)
644 _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic)
646 return mime_magic->max_extent;
650 _xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic,
654 XdgMimeMagicMatch *match;
656 for (match = mime_magic->match_list; match; match = match->next)
658 if (_xdg_mime_magic_match_compare_to_data (match, data, len))
660 return match->mime_type;
668 _xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic)
670 XdgMimeMagicMatch *match;
673 for (match = mime_magic->match_list; match; match = match->next)
675 XdgMimeMagicMatchlet *matchlet;
677 for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next)
681 extent = matchlet->value_length + matchlet->offset + matchlet->range_length;
682 if (max_extent < extent)
687 mime_magic->max_extent = max_extent;
691 _xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic,
694 XdgMimeMagicState state;
695 XdgMimeMagicMatch *match = NULL; /* Quiet compiler */
697 state = XDG_MIME_MAGIC_SECTION;
699 while (state != XDG_MIME_MAGIC_EOF)
703 case XDG_MIME_MAGIC_SECTION:
704 match = _xdg_mime_magic_match_new ();
705 state = _xdg_mime_magic_parse_header (magic_file, match);
706 if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
707 _xdg_mime_magic_match_free (match);
709 case XDG_MIME_MAGIC_MAGIC:
710 state = _xdg_mime_magic_parse_magic_line (magic_file, match);
711 if (state == XDG_MIME_MAGIC_SECTION)
712 _xdg_mime_magic_insert_match (mime_magic, match);
713 else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
714 _xdg_mime_magic_match_free (match);
716 case XDG_MIME_MAGIC_ERROR:
717 state = _xdg_mime_magic_parse_error (magic_file);
719 case XDG_MIME_MAGIC_EOF:
721 /* Make the compiler happy */
725 _xdg_mime_update_mime_magic_extents (mime_magic);
729 _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic,
730 const char *file_name)
735 magic_file = fopen (file_name, "r");
737 if (magic_file == NULL)
740 fread (header, 1, 12, magic_file);
742 if (memcmp ("MIME-Magic\0\n", header, 12) == 0)
743 _xdg_mime_magic_read_magic_file (mime_magic, magic_file);