]>
Commit | Line | Data |
---|---|---|
a1d229d1 DM |
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 | *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 | } |