]>
Commit | Line | Data |
---|---|---|
cf31d981 SI |
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 | * | |
73d5a30a | 9 | * SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0 |
cf31d981 SI |
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)); | |
73d5a30a SI |
90 | if (matchlet == NULL) |
91 | return NULL; | |
cf31d981 SI |
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 (); | |
73d5a30a SI |
344 | |
345 | /* OOM */ | |
346 | if (matchlet == NULL) | |
347 | return XDG_MIME_MAGIC_ERROR; | |
348 | ||
cf31d981 SI |
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 | { | |
73d5a30a | 473 | #if LITTLE_ENDIAN |
cf31d981 | 474 | unsigned int i; |
73d5a30a | 475 | #endif |
cf31d981 SI |
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 (); | |
73d5a30a SI |
763 | |
764 | /* OOM */ | |
765 | if (match == NULL) | |
766 | return; | |
767 | ||
cf31d981 SI |
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); | |
73d5a30a SI |
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 | ||
cf31d981 SI |
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 | } |