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"
48 typedef struct XdgGlobHashNode XdgGlobHashNode
;
49 typedef struct XdgGlobList XdgGlobList
;
51 struct XdgGlobHashNode
53 xdg_unichar_t character
;
54 const char *mime_type
;
57 XdgGlobHashNode
*next
;
58 XdgGlobHashNode
*child
;
63 const char *mime_type
;
71 XdgGlobList
*literal_list
;
72 XdgGlobHashNode
*simple_node
;
73 XdgGlobList
*full_list
;
80 _xdg_glob_list_new (void)
82 XdgGlobList
*new_element
;
84 new_element
= calloc (1, sizeof (XdgGlobList
));
89 /* Frees glob_list and all of it's children */
91 _xdg_glob_list_free (XdgGlobList
*glob_list
)
93 XdgGlobList
*ptr
, *next
;
102 free ((void *) ptr
->data
);
104 free ((void *) ptr
->mime_type
);
112 _xdg_glob_list_append (XdgGlobList
*glob_list
,
114 const char *mime_type
,
118 XdgGlobList
*new_element
;
119 XdgGlobList
*tmp_element
;
121 tmp_element
= glob_list
;
122 while (tmp_element
!= NULL
)
124 if (strcmp (tmp_element
->data
, data
) == 0 &&
125 strcmp (tmp_element
->mime_type
, mime_type
) == 0)
128 tmp_element
= tmp_element
->next
;
131 new_element
= _xdg_glob_list_new ();
132 new_element
->data
= data
;
133 new_element
->mime_type
= mime_type
;
134 new_element
->weight
= weight
;
135 new_element
->case_sensitive
= case_sensitive
;
136 if (glob_list
== NULL
)
139 tmp_element
= glob_list
;
140 while (tmp_element
->next
!= NULL
)
141 tmp_element
= tmp_element
->next
;
143 tmp_element
->next
= new_element
;
151 static XdgGlobHashNode
*
152 _xdg_glob_hash_node_new (void)
154 XdgGlobHashNode
*glob_hash_node
;
156 glob_hash_node
= calloc (1, sizeof (XdgGlobHashNode
));
158 return glob_hash_node
;
162 _xdg_glob_hash_node_dump (XdgGlobHashNode
*glob_hash_node
,
166 for (i
= 0; i
< depth
; i
++)
169 printf ("%c", (char)glob_hash_node
->character
);
170 if (glob_hash_node
->mime_type
)
171 printf (" - %s %d\n", glob_hash_node
->mime_type
, glob_hash_node
->weight
);
174 if (glob_hash_node
->child
)
175 _xdg_glob_hash_node_dump (glob_hash_node
->child
, depth
+ 1);
176 if (glob_hash_node
->next
)
177 _xdg_glob_hash_node_dump (glob_hash_node
->next
, depth
);
180 static XdgGlobHashNode
*
181 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode
*glob_hash_node
,
183 const char *mime_type
,
187 XdgGlobHashNode
*node
;
188 xdg_unichar_t character
;
192 if ((glob_hash_node
== NULL
) ||
193 (character
< glob_hash_node
->character
))
195 node
= _xdg_glob_hash_node_new ();
196 node
->character
= character
;
197 node
->next
= glob_hash_node
;
198 glob_hash_node
= node
;
200 else if (character
== glob_hash_node
->character
)
202 node
= glob_hash_node
;
206 XdgGlobHashNode
*prev_node
;
207 int found_node
= FALSE
;
209 /* Look for the first character of text in glob_hash_node, and insert it if we
211 prev_node
= glob_hash_node
;
212 node
= prev_node
->next
;
216 if (character
< node
->character
)
218 node
= _xdg_glob_hash_node_new ();
219 node
->character
= character
;
220 node
->next
= prev_node
->next
;
221 prev_node
->next
= node
;
226 else if (character
== node
->character
)
237 node
= _xdg_glob_hash_node_new ();
238 node
->character
= character
;
239 node
->next
= prev_node
->next
;
240 prev_node
->next
= node
;
249 if (strcmp (node
->mime_type
, mime_type
) != 0)
251 XdgGlobHashNode
*child
;
252 int found_node
= FALSE
;
255 while (child
&& child
->character
== 0)
257 if (strcmp (child
->mime_type
, mime_type
) == 0)
267 child
= _xdg_glob_hash_node_new ();
268 child
->character
= 0;
269 child
->mime_type
= strdup (mime_type
);
270 child
->weight
= weight
;
271 child
->case_sensitive
= case_sensitive
;
273 child
->next
= node
->child
;
280 node
->mime_type
= strdup (mime_type
);
281 node
->weight
= weight
;
282 node
->case_sensitive
= case_sensitive
;
287 node
->child
= _xdg_glob_hash_insert_ucs4 (node
->child
, text
, mime_type
, weight
, case_sensitive
);
289 return glob_hash_node
;
292 /* glob must be valid UTF-8 */
293 static XdgGlobHashNode
*
294 _xdg_glob_hash_insert_text (XdgGlobHashNode
*glob_hash_node
,
296 const char *mime_type
,
300 XdgGlobHashNode
*node
;
301 xdg_unichar_t
*unitext
;
304 unitext
= _xdg_convert_to_ucs4 (text
, &len
);
305 _xdg_reverse_ucs4 (unitext
, len
);
306 node
= _xdg_glob_hash_insert_ucs4 (glob_hash_node
, unitext
, mime_type
, weight
, case_sensitive
);
317 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode
*glob_hash_node
,
318 const char *file_name
,
320 int case_sensitive_check
,
321 MimeWeight mime_types
[],
325 XdgGlobHashNode
*node
;
326 xdg_unichar_t character
;
328 if (glob_hash_node
== NULL
)
331 character
= file_name
[len
- 1];
333 for (node
= glob_hash_node
; node
&& character
>= node
->character
; node
= node
->next
)
335 if (character
== node
->character
)
341 n
= _xdg_glob_hash_node_lookup_file_name (node
->child
,
344 case_sensitive_check
,
350 if (node
->mime_type
&&
351 (case_sensitive_check
||
352 !node
->case_sensitive
))
354 mime_types
[n
].mime
= node
->mime_type
;
355 mime_types
[n
].weight
= node
->weight
;
359 while (n
< n_mime_types
&& node
&& node
->character
== 0)
361 if (node
->mime_type
&&
362 (case_sensitive_check
||
363 !node
->case_sensitive
))
365 mime_types
[n
].mime
= node
->mime_type
;
366 mime_types
[n
].weight
= node
->weight
;
379 static int compare_mime_weight (const void *a
, const void *b
)
381 const MimeWeight
*aa
= (const MimeWeight
*)a
;
382 const MimeWeight
*bb
= (const MimeWeight
*)b
;
384 return bb
->weight
- aa
->weight
;
387 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
389 ascii_tolower (const char *str
)
393 lower
= strdup (str
);
398 *p
++ = ISUPPER (c
) ? c
- 'A' + 'a' : c
;
404 _xdg_glob_hash_lookup_file_name (XdgGlobHash
*glob_hash
,
405 const char *file_name
,
406 const char *mime_types
[],
411 MimeWeight mimes
[10];
416 /* First, check the literals */
418 assert (file_name
!= NULL
&& n_mime_types
> 0);
422 lower_case
= ascii_tolower (file_name
);
424 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
426 if (strcmp ((const char *)list
->data
, file_name
) == 0)
428 mime_types
[0] = list
->mime_type
;
434 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
436 if (!list
->case_sensitive
&&
437 strcmp ((const char *)list
->data
, lower_case
) == 0)
439 mime_types
[0] = list
->mime_type
;
446 len
= strlen (file_name
);
447 n
= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, lower_case
, len
, FALSE
,
450 n
= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, file_name
, len
, TRUE
,
455 for (list
= glob_hash
->full_list
; list
&& n
< n_mime_types
; list
= list
->next
)
457 if (fnmatch ((const char *)list
->data
, file_name
, 0) == 0)
459 mimes
[n
].mime
= list
->mime_type
;
460 mimes
[n
].weight
= list
->weight
;
467 qsort (mimes
, n
, sizeof (MimeWeight
), compare_mime_weight
);
469 if (n_mime_types
< n
)
472 for (i
= 0; i
< n
; i
++)
473 mime_types
[i
] = mimes
[i
].mime
;
484 _xdg_glob_hash_new (void)
486 XdgGlobHash
*glob_hash
;
488 glob_hash
= calloc (1, sizeof (XdgGlobHash
));
495 _xdg_glob_hash_free_nodes (XdgGlobHashNode
*node
)
500 _xdg_glob_hash_free_nodes (node
->child
);
502 _xdg_glob_hash_free_nodes (node
->next
);
504 free ((void *) node
->mime_type
);
510 _xdg_glob_hash_free (XdgGlobHash
*glob_hash
)
512 _xdg_glob_list_free (glob_hash
->literal_list
);
513 _xdg_glob_list_free (glob_hash
->full_list
);
514 _xdg_glob_hash_free_nodes (glob_hash
->simple_node
);
519 _xdg_glob_determine_type (const char *glob
)
522 int maybe_in_simple_glob
= FALSE
;
523 int first_char
= TRUE
;
529 if (*ptr
== '*' && first_char
)
530 maybe_in_simple_glob
= TRUE
;
531 else if (*ptr
== '\\' || *ptr
== '[' || *ptr
== '?' || *ptr
== '*')
532 return XDG_GLOB_FULL
;
535 ptr
= _xdg_utf8_next_char (ptr
);
537 if (maybe_in_simple_glob
)
538 return XDG_GLOB_SIMPLE
;
540 return XDG_GLOB_LITERAL
;
543 /* glob must be valid UTF-8 */
545 _xdg_glob_hash_append_glob (XdgGlobHash
*glob_hash
,
547 const char *mime_type
,
553 assert (glob_hash
!= NULL
);
554 assert (glob
!= NULL
);
556 type
= _xdg_glob_determine_type (glob
);
560 case XDG_GLOB_LITERAL
:
561 glob_hash
->literal_list
= _xdg_glob_list_append (glob_hash
->literal_list
, strdup (glob
), strdup (mime_type
), weight
, case_sensitive
);
563 case XDG_GLOB_SIMPLE
:
564 glob_hash
->simple_node
= _xdg_glob_hash_insert_text (glob_hash
->simple_node
, glob
+ 1, mime_type
, weight
, case_sensitive
);
567 glob_hash
->full_list
= _xdg_glob_list_append (glob_hash
->full_list
, strdup (glob
), strdup (mime_type
), weight
, case_sensitive
);
573 _xdg_glob_hash_dump (XdgGlobHash
*glob_hash
)
576 printf ("LITERAL STRINGS\n");
577 if (!glob_hash
|| glob_hash
->literal_list
== NULL
)
583 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
584 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
586 printf ("\nSIMPLE GLOBS\n");
587 if (!glob_hash
|| glob_hash
->simple_node
== NULL
)
593 _xdg_glob_hash_node_dump (glob_hash
->simple_node
, 4);
596 printf ("\nFULL GLOBS\n");
597 if (!glob_hash
|| glob_hash
->full_list
== NULL
)
603 for (list
= glob_hash
->full_list
; list
; list
= list
->next
)
604 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
610 _xdg_mime_glob_read_from_file (XdgGlobHash
*glob_hash
,
611 const char *file_name
,
618 glob_file
= fopen (file_name
, "r");
620 if (glob_file
== NULL
)
623 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
625 while (fgets (line
, 255, glob_file
) != NULL
)
628 char *mimetype
, *glob
, *end
;
632 if (line
[0] == '#' || line
[0] == 0)
635 end
= line
+ strlen(line
) - 1;
642 colon
= strchr (p
, ':');
652 colon
= strchr (p
, ':');
660 case_sensitive
= FALSE
;
662 colon
= strchr (p
, ':');
663 if (version_two
&& colon
!= NULL
)
671 /* Flags end at next colon */
672 colon
= strchr (p
, ':');
676 flag
= strstr (p
, "cs");
678 /* Start or after comma */
681 /* ends with comma or end of string */
684 case_sensitive
= TRUE
;
687 _xdg_glob_hash_append_glob (glob_hash
, glob
, mimetype
, weight
, case_sensitive
);