Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu" -*- */
2 : : /* xdgmimeglob.c: Private file. Datastructure for storing the globs.
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 "xdgmimeglob.h"
33 : : #include "xdgmimeint.h"
34 : : #include <stdlib.h>
35 : : #include <stdio.h>
36 : : #include <assert.h>
37 : : #include <string.h>
38 : : #include <fnmatch.h>
39 : :
40 : : #ifndef FALSE
41 : : #define FALSE (0)
42 : : #endif
43 : :
44 : : #ifndef TRUE
45 : : #define TRUE (!FALSE)
46 : : #endif
47 : :
48 : : typedef struct XdgGlobHashNode XdgGlobHashNode;
49 : : typedef struct XdgGlobList XdgGlobList;
50 : :
51 : : struct XdgGlobHashNode
52 : : {
53 : : xdg_unichar_t character;
54 : : const char *mime_type;
55 : : XdgGlobHashNode *next;
56 : : XdgGlobHashNode *child;
57 : : };
58 : : struct XdgGlobList
59 : : {
60 : : const char *data;
61 : : const char *mime_type;
62 : : XdgGlobList *next;
63 : : };
64 : :
65 : : struct XdgGlobHash
66 : : {
67 : : XdgGlobList *literal_list;
68 : : XdgGlobHashNode *simple_node;
69 : : XdgGlobList *full_list;
70 : : };
71 : :
72 : :
73 : : /* XdgGlobList
74 : : */
75 : : static XdgGlobList *
76 : 102 : _xdg_glob_list_new (void)
77 : : {
78 : : XdgGlobList *new_element;
79 : :
80 : 102 : new_element = calloc (1, sizeof (XdgGlobList));
81 : :
82 : 102 : return new_element;
83 : : }
84 : :
85 : : /* Frees glob_list and all of it's children */
86 : : static void
87 : 12 : _xdg_glob_list_free (XdgGlobList *glob_list)
88 : : {
89 : : XdgGlobList *ptr, *next;
90 : :
91 : 12 : ptr = glob_list;
92 : :
93 [ + + ]: 114 : while (ptr != NULL)
94 : : {
95 : 102 : next = ptr->next;
96 : :
97 [ + - ]: 102 : if (ptr->data)
98 : 102 : free ((void *) ptr->data);
99 [ + - ]: 102 : if (ptr->mime_type)
100 : 102 : free ((void *) ptr->mime_type);
101 : 102 : free (ptr);
102 : :
103 : 102 : ptr = next;
104 : : }
105 : 12 : }
106 : :
107 : : static XdgGlobList *
108 : 102 : _xdg_glob_list_append (XdgGlobList *glob_list,
109 : : void *data,
110 : : const char *mime_type)
111 : : {
112 : : XdgGlobList *new_element;
113 : : XdgGlobList *tmp_element;
114 : :
115 : 102 : new_element = _xdg_glob_list_new ();
116 : 102 : new_element->data = data;
117 : 102 : new_element->mime_type = mime_type;
118 [ + + ]: 102 : if (glob_list == NULL)
119 : 12 : return new_element;
120 : :
121 : 90 : tmp_element = glob_list;
122 [ + + ]: 564 : while (tmp_element->next != NULL)
123 : 474 : tmp_element = tmp_element->next;
124 : :
125 : 90 : tmp_element->next = new_element;
126 : :
127 : 102 : return glob_list;
128 : : }
129 : :
130 : : #if 0
131 : : static XdgGlobList *
132 : : _xdg_glob_list_prepend (XdgGlobList *glob_list,
133 : : void *data,
134 : : const char *mime_type)
135 : : {
136 : : XdgGlobList *new_element;
137 : :
138 : : new_element = _xdg_glob_list_new ();
139 : : new_element->data = data;
140 : : new_element->next = glob_list;
141 : : new_element->mime_type = mime_type;
142 : :
143 : : return new_element;
144 : : }
145 : : #endif
146 : :
147 : : /* XdgGlobHashNode
148 : : */
149 : :
150 : : static XdgGlobHashNode *
151 : 7110 : _xdg_glob_hash_node_new (void)
152 : : {
153 : : XdgGlobHashNode *glob_hash_node;
154 : :
155 : 7110 : glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
156 : :
157 : 7110 : return glob_hash_node;
158 : : }
159 : :
160 : : static void
161 : 0 : _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
162 : : int depth)
163 : : {
164 : : int i;
165 [ # # ]: 0 : for (i = 0; i < depth; i++)
166 : 0 : printf (" ");
167 : :
168 : 0 : printf ("%c", (char)glob_hash_node->character);
169 [ # # ]: 0 : if (glob_hash_node->mime_type)
170 : 0 : printf (" - %s\n", glob_hash_node->mime_type);
171 : : else
172 : 0 : printf ("\n");
173 [ # # ]: 0 : if (glob_hash_node->child)
174 : 0 : _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
175 [ # # ]: 0 : if (glob_hash_node->next)
176 : 0 : _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
177 : 0 : }
178 : :
179 : : static XdgGlobHashNode *
180 : 18924 : _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
181 : : const char *text,
182 : : const char *mime_type)
183 : : {
184 : : XdgGlobHashNode *node;
185 : : xdg_unichar_t character;
186 : :
187 : 18924 : character = _xdg_utf8_to_ucs4 (text);
188 : :
189 [ + + + + ]: 22896 : if ((glob_hash_node == NULL) ||
190 : 15564 : (character < glob_hash_node->character))
191 : : {
192 : 3972 : node = _xdg_glob_hash_node_new ();
193 : 3972 : node->character = character;
194 : 3972 : node->next = glob_hash_node;
195 : 3972 : glob_hash_node = node;
196 : : }
197 [ + + ]: 14952 : else if (character == glob_hash_node->character)
198 : : {
199 : 2388 : node = glob_hash_node;
200 : : }
201 : : else
202 : : {
203 : : XdgGlobHashNode *prev_node;
204 : 12564 : int found_node = FALSE;
205 : :
206 : : /* Look for the first character of text in glob_hash_node, and insert it if we
207 : : * have to.*/
208 : 12564 : prev_node = glob_hash_node;
209 : 12564 : node = prev_node->next;
210 : :
211 [ + + ]: 91278 : while (node != NULL)
212 : : {
213 [ + + ]: 89514 : if (character < node->character)
214 : : {
215 : 1218 : node = _xdg_glob_hash_node_new ();
216 : 1218 : node->character = character;
217 : 1218 : node->next = prev_node->next;
218 : 1218 : prev_node->next = node;
219 : :
220 : 1218 : found_node = TRUE;
221 : 1218 : break;
222 : : }
223 [ + + ]: 88296 : else if (character == node->character)
224 : : {
225 : 9582 : found_node = TRUE;
226 : 9582 : break;
227 : : }
228 : 78714 : prev_node = node;
229 : 78714 : node = node->next;
230 : : }
231 : :
232 [ + + ]: 12564 : if (! found_node)
233 : : {
234 : 1764 : node = _xdg_glob_hash_node_new ();
235 : 1764 : node->character = character;
236 : 1764 : node->next = prev_node->next;
237 : 1764 : prev_node->next = node;
238 : : }
239 : : }
240 : :
241 : 18924 : text = _xdg_utf8_next_char (text);
242 [ + + ]: 18924 : if (*text == '\000')
243 : : {
244 [ + + ]: 4404 : if (node->mime_type)
245 : : {
246 [ + - ]: 156 : if (strcmp (node->mime_type, mime_type))
247 : : {
248 : : XdgGlobHashNode *child;
249 : 156 : int found_node = FALSE;
250 : :
251 : 156 : child = node->child;
252 [ + + ][ + + ]: 204 : while (child && child->character == '\0')
253 : : {
254 [ - + ]: 48 : if (strcmp (child->mime_type, mime_type) == 0)
255 : : {
256 : 0 : found_node = TRUE;
257 : 0 : break;
258 : : }
259 : 48 : child = child->next;
260 : : }
261 : :
262 [ + - ]: 156 : if (!found_node)
263 : : {
264 : 156 : child = _xdg_glob_hash_node_new ();
265 : 156 : child->character = '\000';
266 : 156 : child->mime_type = strdup (mime_type);
267 : 156 : child->child = NULL;
268 : 156 : child->next = node->child;
269 : 156 : node->child = child;
270 : : }
271 : : }
272 : : }
273 : : else
274 : : {
275 : 4404 : node->mime_type = strdup (mime_type);
276 : : }
277 : : }
278 : : else
279 : : {
280 : 14520 : node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type);
281 : : }
282 : 18924 : return glob_hash_node;
283 : : }
284 : :
285 : : static int
286 : 18 : _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
287 : : const char *file_name,
288 : : int ignore_case,
289 : : const char *mime_types[],
290 : : int n_mime_types)
291 : : {
292 : : int n;
293 : : XdgGlobHashNode *node;
294 : : xdg_unichar_t character;
295 : :
296 [ - + ]: 18 : if (glob_hash_node == NULL)
297 : 0 : return 0;
298 : :
299 : 18 : character = _xdg_utf8_to_ucs4 (file_name);
300 [ + + ]: 18 : if (ignore_case)
301 : 8 : character = _xdg_ucs4_to_lower(character);
302 : :
303 [ + - ][ + + ]: 68 : for (node = glob_hash_node; node && character >= node->character; node = node->next)
304 : : {
305 [ + + ]: 64 : if (character == node->character)
306 : : {
307 : 14 : file_name = _xdg_utf8_next_char (file_name);
308 [ + + ]: 14 : if (*file_name == '\000')
309 : : {
310 : 3 : n = 0;
311 : :
312 [ + + ]: 3 : if (node->mime_type != NULL)
313 : 1 : mime_types[n++] = node->mime_type;
314 : :
315 : 3 : node = node->child;
316 [ + - ][ + - ]: 3 : while (n < n_mime_types && node && node->character == 0)
[ - + ]
317 : : {
318 [ # # ]: 0 : if (node->mime_type != NULL)
319 : 0 : mime_types[n++] = node->mime_type;
320 : :
321 : 0 : node = node->next;
322 : : }
323 : : }
324 : : else
325 : : {
326 : 11 : n = _xdg_glob_hash_node_lookup_file_name (node->child,
327 : : file_name,
328 : : ignore_case,
329 : : mime_types,
330 : : n_mime_types);
331 : : }
332 : 14 : return n;
333 : : }
334 : : }
335 : :
336 : 18 : return 0;
337 : : }
338 : :
339 : : int
340 : 4 : _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
341 : : const char *file_name,
342 : : const char *mime_types[],
343 : : int n_mime_types)
344 : : {
345 : : XdgGlobList *list;
346 : : const char *ptr;
347 : : char stopchars[128];
348 : : int i, n;
349 : : XdgGlobHashNode *node;
350 : :
351 : : /* First, check the literals */
352 : :
353 [ + - ][ - + ]: 4 : assert (file_name != NULL && n_mime_types > 0);
354 : :
355 [ + + ]: 60 : for (list = glob_hash->literal_list; list; list = list->next)
356 : : {
357 [ - + ]: 56 : if (strcmp ((const char *)list->data, file_name) == 0)
358 : : {
359 : 0 : mime_types[0] = list->mime_type;
360 : 0 : return 1;
361 : : }
362 : : }
363 : :
364 : 4 : i = 0;
365 [ + + ]: 20 : for (node = glob_hash->simple_node; node; node = node->next)
366 : : {
367 [ + - ]: 16 : if (node->character < 128)
368 : 16 : stopchars[i++] = (char)node->character;
369 : : }
370 : 4 : stopchars[i] = '\0';
371 : :
372 : 4 : ptr = strpbrk (file_name, stopchars);
373 [ + + ]: 7 : while (ptr)
374 : : {
375 : 4 : n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE,
376 : : mime_types, n_mime_types);
377 [ + + ]: 4 : if (n > 0)
378 : 1 : return n;
379 : :
380 : 3 : n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE,
381 : : mime_types, n_mime_types);
382 [ - + ]: 3 : if (n > 0)
383 : 0 : return n;
384 : :
385 : 3 : ptr = strpbrk (ptr + 1, stopchars);
386 : : }
387 : :
388 : : /* FIXME: Not UTF-8 safe */
389 : 3 : n = 0;
390 [ + + ][ + - ]: 12 : for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
391 : : {
392 [ - + ]: 9 : if (fnmatch ((const char *)list->data, file_name, 0) == 0)
393 : 0 : mime_types[n++] = list->mime_type;
394 : : }
395 : :
396 : 4 : return n;
397 : : }
398 : :
399 : :
400 : :
401 : : /* XdgGlobHash
402 : : */
403 : :
404 : : XdgGlobHash *
405 : 6 : _xdg_glob_hash_new (void)
406 : : {
407 : : XdgGlobHash *glob_hash;
408 : :
409 : 6 : glob_hash = calloc (1, sizeof (XdgGlobHash));
410 : :
411 : 6 : return glob_hash;
412 : : }
413 : :
414 : :
415 : : static void
416 : 7110 : _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
417 : : {
418 [ + - ]: 7110 : if (node)
419 : : {
420 [ + + ]: 7110 : if (node->child)
421 : 3444 : _xdg_glob_hash_free_nodes (node->child);
422 [ + + ]: 7110 : if (node->next)
423 : 3660 : _xdg_glob_hash_free_nodes (node->next);
424 [ + + ]: 7110 : if (node->mime_type)
425 : 4404 : free ((void *) node->mime_type);
426 : 7110 : free (node);
427 : : }
428 : 7110 : }
429 : :
430 : : void
431 : 6 : _xdg_glob_hash_free (XdgGlobHash *glob_hash)
432 : : {
433 : 6 : _xdg_glob_list_free (glob_hash->literal_list);
434 : 6 : _xdg_glob_list_free (glob_hash->full_list);
435 : 6 : _xdg_glob_hash_free_nodes (glob_hash->simple_node);
436 : 6 : free (glob_hash);
437 : 6 : }
438 : :
439 : : XdgGlobType
440 : 4506 : _xdg_glob_determine_type (const char *glob)
441 : : {
442 : : const char *ptr;
443 : 4506 : int maybe_in_simple_glob = FALSE;
444 : 4506 : int first_char = TRUE;
445 : :
446 : 4506 : ptr = glob;
447 : :
448 [ + + ]: 28638 : while (*ptr != '\000')
449 : : {
450 [ + + ][ + + ]: 24150 : if (*ptr == '*' && first_char)
451 : 4410 : maybe_in_simple_glob = TRUE;
452 [ + - ][ + + ]: 19740 : else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
[ + - ][ + + ]
453 : 18 : return XDG_GLOB_FULL;
454 : :
455 : 24132 : first_char = FALSE;
456 : 24132 : ptr = _xdg_utf8_next_char (ptr);
457 : : }
458 [ + + ]: 4488 : if (maybe_in_simple_glob)
459 : 4404 : return XDG_GLOB_SIMPLE;
460 : : else
461 : 4506 : return XDG_GLOB_LITERAL;
462 : : }
463 : :
464 : : /* glob must be valid UTF-8 */
465 : : void
466 : 4506 : _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
467 : : const char *glob,
468 : : const char *mime_type)
469 : : {
470 : : XdgGlobType type;
471 : :
472 [ - + ]: 4506 : assert (glob_hash != NULL);
473 [ - + ]: 4506 : assert (glob != NULL);
474 : :
475 : 4506 : type = _xdg_glob_determine_type (glob);
476 : :
477 [ + + + - ]: 4506 : switch (type)
478 : : {
479 : : case XDG_GLOB_LITERAL:
480 : 84 : glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type));
481 : 84 : break;
482 : : case XDG_GLOB_SIMPLE:
483 : 4404 : glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type);
484 : 4404 : break;
485 : : case XDG_GLOB_FULL:
486 : 18 : glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type));
487 : : break;
488 : : }
489 : 4506 : }
490 : :
491 : : void
492 : 0 : _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
493 : : {
494 : : XdgGlobList *list;
495 : 0 : printf ("LITERAL STRINGS\n");
496 [ # # ]: 0 : if (glob_hash->literal_list == NULL)
497 : : {
498 : 0 : printf (" None\n");
499 : : }
500 : : else
501 : : {
502 [ # # ]: 0 : for (list = glob_hash->literal_list; list; list = list->next)
503 : 0 : printf (" %s - %s\n", (char *)list->data, list->mime_type);
504 : : }
505 : 0 : printf ("\nSIMPLE GLOBS\n");
506 : 0 : _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
507 : :
508 : 0 : printf ("\nFULL GLOBS\n");
509 [ # # ]: 0 : if (glob_hash->full_list == NULL)
510 : : {
511 : 0 : printf (" None\n");
512 : : }
513 : : else
514 : : {
515 [ # # ]: 0 : for (list = glob_hash->full_list; list; list = list->next)
516 : 0 : printf (" %s - %s\n", (char *)list->data, list->mime_type);
517 : : }
518 : 0 : }
519 : :
520 : :
521 : : void
522 : 6 : _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
523 : : const char *file_name)
524 : : {
525 : : FILE *glob_file;
526 : : char line[255];
527 : :
528 : 6 : glob_file = fopen (file_name, "r");
529 : :
530 [ - + ]: 6 : if (glob_file == NULL)
531 : 0 : return;
532 : :
533 : : /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
534 : : * Blah */
535 [ + + ]: 4524 : while (fgets (line, 255, glob_file) != NULL)
536 : : {
537 : : char *colon;
538 [ + + ]: 4518 : if (line[0] == '#')
539 : 12 : continue;
540 : :
541 : 4506 : colon = strchr (line, ':');
542 [ - + ]: 4506 : if (colon == NULL)
543 : 0 : continue;
544 : 4506 : *(colon++) = '\000';
545 : 4506 : colon[strlen (colon) -1] = '\000';
546 : 4506 : _xdg_glob_hash_append_glob (glob_hash, colon, line);
547 : : }
548 : :
549 : 6 : fclose (glob_file);
550 : : }
|