1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* xdgmimeglob.c: Private file. Datastructure for storing the globs.
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.
32 #include "xdgmimeglob.h"
33 #include "xdgmimeint.h"
49 typedef struct XdgGlobHashNode XdgGlobHashNode;
50 typedef struct XdgGlobList XdgGlobList;
52 struct XdgGlobHashNode
54 xdg_unichar_t character;
55 const char *mime_type;
58 XdgGlobHashNode *next;
59 XdgGlobHashNode *child;
64 const char *mime_type;
72 XdgGlobList *literal_list;
73 XdgGlobHashNode *simple_node;
74 XdgGlobList *full_list;
81 _xdg_glob_list_new (void)
83 XdgGlobList *new_element;
85 new_element = calloc (1, sizeof (XdgGlobList));
90 /* Frees glob_list and all of it's children */
92 _xdg_glob_list_free (XdgGlobList *glob_list)
94 XdgGlobList *ptr, *next;
103 free ((void *) ptr->data);
105 free ((void *) ptr->mime_type);
113 _xdg_glob_list_append (XdgGlobList *glob_list,
115 const char *mime_type,
119 XdgGlobList *new_element;
120 XdgGlobList *tmp_element;
122 tmp_element = glob_list;
123 while (tmp_element != NULL)
125 if (strcmp (tmp_element->data, data) == 0 &&
126 strcmp (tmp_element->mime_type, mime_type) == 0)
129 tmp_element = tmp_element->next;
132 new_element = _xdg_glob_list_new ();
133 new_element->data = data;
134 new_element->mime_type = mime_type;
135 new_element->weight = weight;
136 new_element->case_sensitive = case_sensitive;
137 if (glob_list == NULL)
140 tmp_element = glob_list;
141 while (tmp_element->next != NULL)
142 tmp_element = tmp_element->next;
144 tmp_element->next = new_element;
152 static XdgGlobHashNode *
153 _xdg_glob_hash_node_new (void)
155 XdgGlobHashNode *glob_hash_node;
157 glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
159 return glob_hash_node;
163 _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
167 for (i = 0; i < depth; i++)
170 printf ("%c", (char)glob_hash_node->character);
171 if (glob_hash_node->mime_type)
172 printf (" - %s %d\n", glob_hash_node->mime_type, glob_hash_node->weight);
175 if (glob_hash_node->child)
176 _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
177 if (glob_hash_node->next)
178 _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
181 static XdgGlobHashNode *
182 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
184 const char *mime_type,
188 XdgGlobHashNode *node;
189 xdg_unichar_t character;
193 if ((glob_hash_node == NULL) ||
194 (character < glob_hash_node->character))
196 node = _xdg_glob_hash_node_new ();
197 node->character = character;
198 node->next = glob_hash_node;
199 glob_hash_node = node;
201 else if (character == glob_hash_node->character)
203 node = glob_hash_node;
207 XdgGlobHashNode *prev_node;
208 int found_node = FALSE;
210 /* Look for the first character of text in glob_hash_node, and insert it if we
212 prev_node = glob_hash_node;
213 node = prev_node->next;
217 if (character < node->character)
219 node = _xdg_glob_hash_node_new ();
220 node->character = character;
221 node->next = prev_node->next;
222 prev_node->next = node;
227 else if (character == node->character)
238 node = _xdg_glob_hash_node_new ();
239 node->character = character;
240 node->next = prev_node->next;
241 prev_node->next = node;
250 if (strcmp (node->mime_type, mime_type) != 0)
252 XdgGlobHashNode *child;
253 int found_node = FALSE;
256 while (child && child->character == 0)
258 if (strcmp (child->mime_type, mime_type) == 0)
268 child = _xdg_glob_hash_node_new ();
269 child->character = 0;
270 child->mime_type = strdup (mime_type);
271 child->weight = weight;
272 child->case_sensitive = case_sensitive;
274 child->next = node->child;
281 node->mime_type = strdup (mime_type);
282 node->weight = weight;
283 node->case_sensitive = case_sensitive;
288 node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight, case_sensitive);
290 return glob_hash_node;
293 /* glob must be valid UTF-8 */
294 static XdgGlobHashNode *
295 _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
297 const char *mime_type,
301 XdgGlobHashNode *node;
302 xdg_unichar_t *unitext;
305 unitext = _xdg_convert_to_ucs4 (text, &len);
306 _xdg_reverse_ucs4 (unitext, len);
307 node = _xdg_glob_hash_insert_ucs4 (glob_hash_node, unitext, mime_type, weight, case_sensitive);
318 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
319 const char *file_name,
321 int case_sensitive_check,
322 MimeWeight mime_types[],
326 XdgGlobHashNode *node;
327 xdg_unichar_t character;
329 if (glob_hash_node == NULL)
332 character = file_name[len - 1];
334 for (node = glob_hash_node; node && character >= node->character; node = node->next)
336 if (character == node->character)
342 n = _xdg_glob_hash_node_lookup_file_name (node->child,
345 case_sensitive_check,
351 if (node->mime_type &&
352 (case_sensitive_check ||
353 !node->case_sensitive))
355 mime_types[n].mime = node->mime_type;
356 mime_types[n].weight = node->weight;
360 while (n < n_mime_types && node && node->character == 0)
362 if (node->mime_type &&
363 (case_sensitive_check ||
364 !node->case_sensitive))
366 mime_types[n].mime = node->mime_type;
367 mime_types[n].weight = node->weight;
380 static int compare_mime_weight (const void *a, const void *b)
382 const MimeWeight *aa = (const MimeWeight *)a;
383 const MimeWeight *bb = (const MimeWeight *)b;
385 return aa->weight - bb->weight;
388 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
390 ascii_tolower (const char *str)
394 lower = strdup (str);
399 *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
405 _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
406 const char *file_name,
407 const char *mime_types[],
412 MimeWeight mimes[10];
418 /* First, check the literals */
420 assert (file_name != NULL && n_mime_types > 0);
424 lower_case = ascii_tolower (file_name);
426 for (list = glob_hash->literal_list; list; list = list->next)
428 if (strcmp ((const char *)list->data, file_name) == 0)
430 mime_types[0] = list->mime_type;
436 for (list = glob_hash->literal_list; list; list = list->next)
438 if (!list->case_sensitive &&
439 strcmp ((const char *)list->data, lower_case) == 0)
441 mime_types[0] = list->mime_type;
448 len = strlen (file_name);
449 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, lower_case, len, FALSE,
452 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, file_name, len, TRUE,
457 for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
459 if (fnmatch ((const char *)list->data, file_name, 0) == 0)
461 mimes[n].mime = list->mime_type;
462 mimes[n].weight = list->weight;
469 qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
471 if (n_mime_types < n)
474 for (i = 0; i < n; i++)
475 mime_types[i] = mimes[i].mime;
486 _xdg_glob_hash_new (void)
488 XdgGlobHash *glob_hash;
490 glob_hash = calloc (1, sizeof (XdgGlobHash));
497 _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
502 _xdg_glob_hash_free_nodes (node->child);
504 _xdg_glob_hash_free_nodes (node->next);
506 free ((void *) node->mime_type);
512 _xdg_glob_hash_free (XdgGlobHash *glob_hash)
514 _xdg_glob_list_free (glob_hash->literal_list);
515 _xdg_glob_list_free (glob_hash->full_list);
516 _xdg_glob_hash_free_nodes (glob_hash->simple_node);
521 _xdg_glob_determine_type (const char *glob)
524 int maybe_in_simple_glob = FALSE;
525 int first_char = TRUE;
531 if (*ptr == '*' && first_char)
532 maybe_in_simple_glob = TRUE;
533 else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
534 return XDG_GLOB_FULL;
537 ptr = _xdg_utf8_next_char (ptr);
539 if (maybe_in_simple_glob)
540 return XDG_GLOB_SIMPLE;
542 return XDG_GLOB_LITERAL;
545 /* glob must be valid UTF-8 */
547 _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
549 const char *mime_type,
555 assert (glob_hash != NULL);
556 assert (glob != NULL);
558 type = _xdg_glob_determine_type (glob);
562 case XDG_GLOB_LITERAL:
563 glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
565 case XDG_GLOB_SIMPLE:
566 glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type, weight, case_sensitive);
569 glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
575 _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
578 printf ("LITERAL STRINGS\n");
579 if (!glob_hash || glob_hash->literal_list == NULL)
585 for (list = glob_hash->literal_list; list; list = list->next)
586 printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
588 printf ("\nSIMPLE GLOBS\n");
589 if (!glob_hash || glob_hash->simple_node == NULL)
595 _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
598 printf ("\nFULL GLOBS\n");
599 if (!glob_hash || glob_hash->full_list == NULL)
605 for (list = glob_hash->full_list; list; list = list->next)
606 printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
612 _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
613 const char *file_name,
620 glob_file = fopen (file_name, "r");
622 if (glob_file == NULL)
625 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
627 while (fgets (line, 255, glob_file) != NULL)
630 char *mimetype, *glob, *end;
634 if (line[0] == '#' || line[0] == 0)
637 end = line + strlen(line) - 1;
644 colon = strchr (p, ':');
654 colon = strchr (p, ':');
662 case_sensitive = FALSE;
664 colon = strchr (p, ':');
665 if (version_two && colon != NULL)
673 /* Flags end at next colon */
674 colon = strchr (p, ':');
678 flag = strstr (p, "cs");
680 /* Start or after comma */
683 /* ends with comma or end of string */
686 case_sensitive = TRUE;
689 _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight, case_sensitive);