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;
57 XdgGlobHashNode *next;
58 XdgGlobHashNode *child;
63 const char *mime_type;
70 XdgGlobList *literal_list;
71 XdgGlobHashNode *simple_node;
72 XdgGlobList *full_list;
79 _xdg_glob_list_new (void)
81 XdgGlobList *new_element;
83 new_element = calloc (1, sizeof (XdgGlobList));
88 /* Frees glob_list and all of it's children */
90 _xdg_glob_list_free (XdgGlobList *glob_list)
92 XdgGlobList *ptr, *next;
101 free ((void *) ptr->data);
103 free ((void *) ptr->mime_type);
111 _xdg_glob_list_append (XdgGlobList *glob_list,
113 const char *mime_type,
116 XdgGlobList *new_element;
117 XdgGlobList *tmp_element;
119 new_element = _xdg_glob_list_new ();
120 new_element->data = data;
121 new_element->mime_type = mime_type;
122 new_element->weight = weight;
123 if (glob_list == NULL)
126 tmp_element = glob_list;
127 while (tmp_element->next != NULL)
128 tmp_element = tmp_element->next;
130 tmp_element->next = new_element;
138 static XdgGlobHashNode *
139 _xdg_glob_hash_node_new (void)
141 XdgGlobHashNode *glob_hash_node;
143 glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
145 return glob_hash_node;
149 _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
153 for (i = 0; i < depth; i++)
156 printf ("%c", (char)glob_hash_node->character);
157 if (glob_hash_node->mime_type)
158 printf (" - %s %d\n", glob_hash_node->mime_type, glob_hash_node->weight);
161 if (glob_hash_node->child)
162 _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
163 if (glob_hash_node->next)
164 _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
167 static XdgGlobHashNode *
168 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
170 const char *mime_type,
173 XdgGlobHashNode *node;
174 xdg_unichar_t character;
178 if ((glob_hash_node == NULL) ||
179 (character < glob_hash_node->character))
181 node = _xdg_glob_hash_node_new ();
182 node->character = character;
183 node->next = glob_hash_node;
184 glob_hash_node = node;
186 else if (character == glob_hash_node->character)
188 node = glob_hash_node;
192 XdgGlobHashNode *prev_node;
193 int found_node = FALSE;
195 /* Look for the first character of text in glob_hash_node, and insert it if we
197 prev_node = glob_hash_node;
198 node = prev_node->next;
202 if (character < node->character)
204 node = _xdg_glob_hash_node_new ();
205 node->character = character;
206 node->next = prev_node->next;
207 prev_node->next = node;
212 else if (character == node->character)
223 node = _xdg_glob_hash_node_new ();
224 node->character = character;
225 node->next = prev_node->next;
226 prev_node->next = node;
235 if (strcmp (node->mime_type, mime_type))
237 XdgGlobHashNode *child;
238 int found_node = FALSE;
241 while (child && child->character == 0)
243 if (strcmp (child->mime_type, mime_type) == 0)
253 child = _xdg_glob_hash_node_new ();
254 child->character = 0;
255 child->mime_type = strdup (mime_type);
256 child->weight = weight;
258 child->next = node->child;
265 node->mime_type = strdup (mime_type);
266 node->weight = weight;
271 node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight);
273 return glob_hash_node;
276 /* glob must be valid UTF-8 */
277 static XdgGlobHashNode *
278 _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
280 const char *mime_type,
283 XdgGlobHashNode *node;
284 xdg_unichar_t *unitext;
287 unitext = _xdg_convert_to_ucs4 (text, &len);
288 _xdg_reverse_ucs4 (unitext, len);
289 node = _xdg_glob_hash_insert_ucs4 (glob_hash_node, unitext, mime_type, weight);
300 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
301 const char *file_name,
304 MimeWeight mime_types[],
308 XdgGlobHashNode *node;
309 xdg_unichar_t character;
311 if (glob_hash_node == NULL)
314 character = file_name[len - 1];
316 character = tolower(character);
318 for (node = glob_hash_node; node && character >= node->character; node = node->next)
320 if (character == node->character)
326 n = _xdg_glob_hash_node_lookup_file_name (node->child,
337 mime_types[n].mime = node->mime_type;
338 mime_types[n].weight = node->weight;
342 while (n < n_mime_types && node && node->character == 0)
346 mime_types[n].mime = node->mime_type;
347 mime_types[n].weight = node->weight;
360 static int compare_mime_weight (const void *a, const void *b)
362 const MimeWeight *aa = (const MimeWeight *)a;
363 const MimeWeight *bb = (const MimeWeight *)b;
365 return aa->weight - bb->weight;
369 _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
370 const char *file_name,
371 const char *mime_types[],
376 MimeWeight mimes[10];
380 /* First, check the literals */
382 assert (file_name != NULL && n_mime_types > 0);
386 for (list = glob_hash->literal_list; list; list = list->next)
388 if (strcmp ((const char *)list->data, file_name) == 0)
390 mime_types[0] = list->mime_type;
395 len = strlen (file_name);
396 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, file_name, len, FALSE,
399 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, file_name, len, TRUE,
404 for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
406 if (fnmatch ((const char *)list->data, file_name, 0) == 0)
408 mimes[n].mime = list->mime_type;
409 mimes[n].weight = list->weight;
415 qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
417 if (n_mime_types < n)
420 for (i = 0; i < n; i++)
421 mime_types[i] = mimes[i].mime;
432 _xdg_glob_hash_new (void)
434 XdgGlobHash *glob_hash;
436 glob_hash = calloc (1, sizeof (XdgGlobHash));
443 _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
448 _xdg_glob_hash_free_nodes (node->child);
450 _xdg_glob_hash_free_nodes (node->next);
452 free ((void *) node->mime_type);
458 _xdg_glob_hash_free (XdgGlobHash *glob_hash)
460 _xdg_glob_list_free (glob_hash->literal_list);
461 _xdg_glob_list_free (glob_hash->full_list);
462 _xdg_glob_hash_free_nodes (glob_hash->simple_node);
467 _xdg_glob_determine_type (const char *glob)
470 int maybe_in_simple_glob = FALSE;
471 int first_char = TRUE;
477 if (*ptr == '*' && first_char)
478 maybe_in_simple_glob = TRUE;
479 else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
480 return XDG_GLOB_FULL;
483 ptr = _xdg_utf8_next_char (ptr);
485 if (maybe_in_simple_glob)
486 return XDG_GLOB_SIMPLE;
488 return XDG_GLOB_LITERAL;
491 /* glob must be valid UTF-8 */
493 _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
495 const char *mime_type,
501 assert (glob_hash != NULL);
502 assert (glob != NULL);
504 type = _xdg_glob_determine_type (glob);
508 case XDG_GLOB_LITERAL:
509 glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type), weight);
511 case XDG_GLOB_SIMPLE:
512 glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type, weight);
515 glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight);
521 _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
524 printf ("LITERAL STRINGS\n");
525 if (!glob_hash || glob_hash->literal_list == NULL)
531 for (list = glob_hash->literal_list; list; list = list->next)
532 printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
534 printf ("\nSIMPLE GLOBS\n");
535 if (!glob_hash || glob_hash->simple_node == NULL)
541 _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
544 printf ("\nFULL GLOBS\n");
545 if (!glob_hash || glob_hash->full_list == NULL)
551 for (list = glob_hash->full_list; list; list = list->next)
552 printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
558 _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
559 const char *file_name,
566 glob_file = fopen (file_name, "r");
568 if (glob_file == NULL)
571 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
573 while (fgets (line, 255, glob_file) != NULL)
576 char *mimetype, *glob, *end;
580 if (line[0] == '#' || line[0] == 0)
583 end = line + strlen(line) - 1;
590 colon = strchr (p, ':');
600 colon = strchr (p, ':');
608 case_sensitive = FALSE;
610 colon = strchr (p, ':');
611 if (version_two && colon != NULL)
619 /* Flags end at next colon */
620 colon = strchr (p, ':');
624 flag = strstr (p, "cs");
626 /* Start or after comma */
629 /* ends with comma or end of string */
632 case_sensitive = TRUE;
635 _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight, case_sensitive);