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.1 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, see <http://www.gnu.org/licenses/>.
28 #include "xdgmimeglob.h"
29 #include "xdgmimeint.h"
37 #define MAX(a,b) ((a) > (b) ? (a) : (b))
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
;
161 #ifdef NOT_USED_IN_GIO
164 _xdg_glob_hash_node_dump (XdgGlobHashNode
*glob_hash_node
,
168 for (i
= 0; i
< depth
; i
++)
171 printf ("%c", (char)glob_hash_node
->character
);
172 if (glob_hash_node
->mime_type
)
173 printf (" - %s %d\n", glob_hash_node
->mime_type
, glob_hash_node
->weight
);
176 if (glob_hash_node
->child
)
177 _xdg_glob_hash_node_dump (glob_hash_node
->child
, depth
+ 1);
178 if (glob_hash_node
->next
)
179 _xdg_glob_hash_node_dump (glob_hash_node
->next
, depth
);
184 static XdgGlobHashNode
*
185 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode
*glob_hash_node
,
187 const char *mime_type
,
191 XdgGlobHashNode
*node
;
192 xdg_unichar_t character
;
196 if ((glob_hash_node
== NULL
) ||
197 (character
< glob_hash_node
->character
))
199 node
= _xdg_glob_hash_node_new ();
200 node
->character
= character
;
201 node
->next
= glob_hash_node
;
202 glob_hash_node
= node
;
204 else if (character
== glob_hash_node
->character
)
206 node
= glob_hash_node
;
210 XdgGlobHashNode
*prev_node
;
211 int found_node
= FALSE
;
213 /* Look for the first character of text in glob_hash_node, and insert it if we
215 prev_node
= glob_hash_node
;
216 node
= prev_node
->next
;
220 if (character
< node
->character
)
222 node
= _xdg_glob_hash_node_new ();
223 node
->character
= character
;
224 node
->next
= prev_node
->next
;
225 prev_node
->next
= node
;
230 else if (character
== node
->character
)
241 node
= _xdg_glob_hash_node_new ();
242 node
->character
= character
;
243 node
->next
= prev_node
->next
;
244 prev_node
->next
= node
;
253 if (strcmp (node
->mime_type
, mime_type
) != 0)
255 XdgGlobHashNode
*child
;
256 int found_node
= FALSE
;
259 while (child
&& child
->character
== 0)
261 if (strcmp (child
->mime_type
, mime_type
) == 0)
271 child
= _xdg_glob_hash_node_new ();
272 child
->character
= 0;
273 child
->mime_type
= strdup (mime_type
);
274 child
->weight
= weight
;
275 child
->case_sensitive
= case_sensitive
;
277 child
->next
= node
->child
;
284 node
->mime_type
= strdup (mime_type
);
285 node
->weight
= weight
;
286 node
->case_sensitive
= case_sensitive
;
291 node
->child
= _xdg_glob_hash_insert_ucs4 (node
->child
, text
, mime_type
, weight
, case_sensitive
);
293 return glob_hash_node
;
296 /* glob must be valid UTF-8 */
297 static XdgGlobHashNode
*
298 _xdg_glob_hash_insert_text (XdgGlobHashNode
*glob_hash_node
,
300 const char *mime_type
,
304 XdgGlobHashNode
*node
;
305 xdg_unichar_t
*unitext
;
308 unitext
= _xdg_convert_to_ucs4 (text
, &len
);
309 _xdg_reverse_ucs4 (unitext
, len
);
310 node
= _xdg_glob_hash_insert_ucs4 (glob_hash_node
, unitext
, mime_type
, weight
, case_sensitive
);
321 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode
*glob_hash_node
,
322 const char *file_name
,
324 int case_sensitive_check
,
325 MimeWeight mime_types
[],
329 XdgGlobHashNode
*node
;
330 xdg_unichar_t character
;
332 if (glob_hash_node
== NULL
)
335 character
= file_name
[len
- 1];
337 for (node
= glob_hash_node
; node
&& character
>= node
->character
; node
= node
->next
)
339 if (character
== node
->character
)
345 n
= _xdg_glob_hash_node_lookup_file_name (node
->child
,
348 case_sensitive_check
,
354 if (node
->mime_type
&&
355 (case_sensitive_check
||
356 !node
->case_sensitive
))
358 mime_types
[n
].mime
= node
->mime_type
;
359 mime_types
[n
].weight
= node
->weight
;
363 while (n
< n_mime_types
&& node
&& node
->character
== 0)
365 if (node
->mime_type
&&
366 (case_sensitive_check
||
367 !node
->case_sensitive
))
369 mime_types
[n
].mime
= node
->mime_type
;
370 mime_types
[n
].weight
= node
->weight
;
383 static int compare_mime_weight (const void *a
, const void *b
)
385 const MimeWeight
*aa
= (const MimeWeight
*)a
;
386 const MimeWeight
*bb
= (const MimeWeight
*)b
;
388 return bb
->weight
- aa
->weight
;
391 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
393 ascii_tolower (const char *str
)
397 lower
= strdup (str
);
402 *p
++ = ISUPPER (c
) ? c
- 'A' + 'a' : c
;
408 filter_out_dupes (MimeWeight mimes
[], int n_mimes
)
415 for (i
= 0; i
< last
; i
++)
420 if (strcmp (mimes
[i
].mime
, mimes
[j
].mime
) == 0)
422 mimes
[i
].weight
= MAX (mimes
[i
].weight
, mimes
[j
].weight
);
424 mimes
[j
].mime
= mimes
[last
].mime
;
425 mimes
[j
].weight
= mimes
[last
].weight
;
436 _xdg_glob_hash_lookup_file_name (XdgGlobHash
*glob_hash
,
437 const char *file_name
,
438 const char *mime_types
[],
443 MimeWeight mimes
[10];
448 /* First, check the literals */
450 assert (file_name
!= NULL
&& n_mime_types
> 0);
454 lower_case
= ascii_tolower (file_name
);
456 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
458 if (strcmp ((const char *)list
->data
, file_name
) == 0)
460 mime_types
[0] = list
->mime_type
;
466 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
468 if (!list
->case_sensitive
&&
469 strcmp ((const char *)list
->data
, lower_case
) == 0)
471 mime_types
[0] = list
->mime_type
;
478 len
= strlen (file_name
);
479 n
= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, lower_case
, len
, FALSE
,
482 n
+= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, file_name
, len
, TRUE
,
483 mimes
+ n
, n_mimes
- n
);
487 for (list
= glob_hash
->full_list
; list
&& n
< n_mime_types
; list
= list
->next
)
489 if (fnmatch ((const char *)list
->data
, file_name
, 0) == 0)
491 mimes
[n
].mime
= list
->mime_type
;
492 mimes
[n
].weight
= list
->weight
;
499 n
= filter_out_dupes (mimes
, n
);
501 qsort (mimes
, n
, sizeof (MimeWeight
), compare_mime_weight
);
503 if (n_mime_types
< n
)
506 for (i
= 0; i
< n
; i
++)
507 mime_types
[i
] = mimes
[i
].mime
;
518 _xdg_glob_hash_new (void)
520 XdgGlobHash
*glob_hash
;
522 glob_hash
= calloc (1, sizeof (XdgGlobHash
));
529 _xdg_glob_hash_free_nodes (XdgGlobHashNode
*node
)
534 _xdg_glob_hash_free_nodes (node
->child
);
536 _xdg_glob_hash_free_nodes (node
->next
);
538 free ((void *) node
->mime_type
);
544 _xdg_glob_hash_free (XdgGlobHash
*glob_hash
)
546 _xdg_glob_list_free (glob_hash
->literal_list
);
547 _xdg_glob_list_free (glob_hash
->full_list
);
548 _xdg_glob_hash_free_nodes (glob_hash
->simple_node
);
553 _xdg_glob_determine_type (const char *glob
)
556 int maybe_in_simple_glob
= FALSE
;
557 int first_char
= TRUE
;
563 if (*ptr
== '*' && first_char
)
564 maybe_in_simple_glob
= TRUE
;
565 else if (*ptr
== '\\' || *ptr
== '[' || *ptr
== '?' || *ptr
== '*')
566 return XDG_GLOB_FULL
;
569 ptr
= _xdg_utf8_next_char (ptr
);
571 if (maybe_in_simple_glob
)
572 return XDG_GLOB_SIMPLE
;
574 return XDG_GLOB_LITERAL
;
577 /* glob must be valid UTF-8 */
579 _xdg_glob_hash_append_glob (XdgGlobHash
*glob_hash
,
581 const char *mime_type
,
587 assert (glob_hash
!= NULL
);
588 assert (glob
!= NULL
);
590 type
= _xdg_glob_determine_type (glob
);
594 case XDG_GLOB_LITERAL
:
595 glob_hash
->literal_list
= _xdg_glob_list_append (glob_hash
->literal_list
, strdup (glob
), strdup (mime_type
), weight
, case_sensitive
);
597 case XDG_GLOB_SIMPLE
:
598 glob_hash
->simple_node
= _xdg_glob_hash_insert_text (glob_hash
->simple_node
, glob
+ 1, mime_type
, weight
, case_sensitive
);
601 glob_hash
->full_list
= _xdg_glob_list_append (glob_hash
->full_list
, strdup (glob
), strdup (mime_type
), weight
, case_sensitive
);
606 #ifdef NOT_USED_IN_GIO
609 _xdg_glob_hash_dump (XdgGlobHash
*glob_hash
)
612 printf ("LITERAL STRINGS\n");
613 if (!glob_hash
|| glob_hash
->literal_list
== NULL
)
619 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
620 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
622 printf ("\nSIMPLE GLOBS\n");
623 if (!glob_hash
|| glob_hash
->simple_node
== NULL
)
629 _xdg_glob_hash_node_dump (glob_hash
->simple_node
, 4);
632 printf ("\nFULL GLOBS\n");
633 if (!glob_hash
|| glob_hash
->full_list
== NULL
)
639 for (list
= glob_hash
->full_list
; list
; list
= list
->next
)
640 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
647 _xdg_mime_glob_read_from_file (XdgGlobHash
*glob_hash
,
648 const char *file_name
,
655 glob_file
= fopen (file_name
, "r");
657 if (glob_file
== NULL
)
660 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
662 while (fgets (line
, 255, glob_file
) != NULL
)
665 char *mimetype
, *glob
, *end
;
669 if (line
[0] == '#' || line
[0] == 0)
672 end
= line
+ strlen(line
) - 1;
679 colon
= strchr (p
, ':');
689 colon
= strchr (p
, ':');
697 case_sensitive
= FALSE
;
699 colon
= strchr (p
, ':');
700 if (version_two
&& colon
!= NULL
)
708 /* Flags end at next colon */
709 colon
= strchr (p
, ':');
713 flag
= strstr (p
, "cs");
715 /* Start or after comma */
718 /* ends with comma or end of string */
721 case_sensitive
= TRUE
;
724 _xdg_glob_hash_append_glob (glob_hash
, glob
, mimetype
, weight
, case_sensitive
);