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, see <http://www.gnu.org/licenses/>.
30 #include "xdgmimeglob.h"
31 #include "xdgmimeint.h"
39 #define MAX(a,b) ((a) > (b) ? (a) : (b))
50 typedef struct XdgGlobHashNode XdgGlobHashNode
;
51 typedef struct XdgGlobList XdgGlobList
;
53 struct XdgGlobHashNode
55 xdg_unichar_t character
;
56 const char *mime_type
;
59 XdgGlobHashNode
*next
;
60 XdgGlobHashNode
*child
;
65 const char *mime_type
;
73 XdgGlobList
*literal_list
;
74 XdgGlobHashNode
*simple_node
;
75 XdgGlobList
*full_list
;
82 _xdg_glob_list_new (void)
84 XdgGlobList
*new_element
;
86 new_element
= calloc (1, sizeof (XdgGlobList
));
91 /* Frees glob_list and all of it's children */
93 _xdg_glob_list_free (XdgGlobList
*glob_list
)
95 XdgGlobList
*ptr
, *next
;
104 free ((void *) ptr
->data
);
106 free ((void *) ptr
->mime_type
);
114 _xdg_glob_list_append (XdgGlobList
*glob_list
,
116 const char *mime_type
,
120 XdgGlobList
*new_element
;
121 XdgGlobList
*tmp_element
;
123 tmp_element
= glob_list
;
124 while (tmp_element
!= NULL
)
126 if (strcmp (tmp_element
->data
, data
) == 0 &&
127 strcmp (tmp_element
->mime_type
, mime_type
) == 0)
130 tmp_element
= tmp_element
->next
;
133 new_element
= _xdg_glob_list_new ();
134 new_element
->data
= data
;
135 new_element
->mime_type
= mime_type
;
136 new_element
->weight
= weight
;
137 new_element
->case_sensitive
= case_sensitive
;
138 if (glob_list
== NULL
)
141 tmp_element
= glob_list
;
142 while (tmp_element
->next
!= NULL
)
143 tmp_element
= tmp_element
->next
;
145 tmp_element
->next
= new_element
;
153 static XdgGlobHashNode
*
154 _xdg_glob_hash_node_new (void)
156 XdgGlobHashNode
*glob_hash_node
;
158 glob_hash_node
= calloc (1, sizeof (XdgGlobHashNode
));
160 return glob_hash_node
;
163 #ifdef NOT_USED_IN_GIO
166 _xdg_glob_hash_node_dump (XdgGlobHashNode
*glob_hash_node
,
170 for (i
= 0; i
< depth
; i
++)
173 printf ("%c", (char)glob_hash_node
->character
);
174 if (glob_hash_node
->mime_type
)
175 printf (" - %s %d\n", glob_hash_node
->mime_type
, glob_hash_node
->weight
);
178 if (glob_hash_node
->child
)
179 _xdg_glob_hash_node_dump (glob_hash_node
->child
, depth
+ 1);
180 if (glob_hash_node
->next
)
181 _xdg_glob_hash_node_dump (glob_hash_node
->next
, depth
);
186 static XdgGlobHashNode
*
187 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode
*glob_hash_node
,
189 const char *mime_type
,
193 XdgGlobHashNode
*node
;
194 xdg_unichar_t character
;
198 if ((glob_hash_node
== NULL
) ||
199 (character
< glob_hash_node
->character
))
201 node
= _xdg_glob_hash_node_new ();
202 node
->character
= character
;
203 node
->next
= glob_hash_node
;
204 glob_hash_node
= node
;
206 else if (character
== glob_hash_node
->character
)
208 node
= glob_hash_node
;
212 XdgGlobHashNode
*prev_node
;
213 int found_node
= FALSE
;
215 /* Look for the first character of text in glob_hash_node, and insert it if we
217 prev_node
= glob_hash_node
;
218 node
= prev_node
->next
;
222 if (character
< node
->character
)
224 node
= _xdg_glob_hash_node_new ();
225 node
->character
= character
;
226 node
->next
= prev_node
->next
;
227 prev_node
->next
= node
;
232 else if (character
== node
->character
)
243 node
= _xdg_glob_hash_node_new ();
244 node
->character
= character
;
245 node
->next
= prev_node
->next
;
246 prev_node
->next
= node
;
255 if (strcmp (node
->mime_type
, mime_type
) != 0)
257 XdgGlobHashNode
*child
;
258 int found_node
= FALSE
;
261 while (child
&& child
->character
== 0)
263 if (strcmp (child
->mime_type
, mime_type
) == 0)
273 child
= _xdg_glob_hash_node_new ();
274 child
->character
= 0;
275 child
->mime_type
= strdup (mime_type
);
276 child
->weight
= weight
;
277 child
->case_sensitive
= case_sensitive
;
279 child
->next
= node
->child
;
286 node
->mime_type
= strdup (mime_type
);
287 node
->weight
= weight
;
288 node
->case_sensitive
= case_sensitive
;
293 node
->child
= _xdg_glob_hash_insert_ucs4 (node
->child
, text
, mime_type
, weight
, case_sensitive
);
295 return glob_hash_node
;
298 /* glob must be valid UTF-8 */
299 static XdgGlobHashNode
*
300 _xdg_glob_hash_insert_text (XdgGlobHashNode
*glob_hash_node
,
302 const char *mime_type
,
306 XdgGlobHashNode
*node
;
307 xdg_unichar_t
*unitext
;
310 unitext
= _xdg_convert_to_ucs4 (text
, &len
);
311 _xdg_reverse_ucs4 (unitext
, len
);
312 node
= _xdg_glob_hash_insert_ucs4 (glob_hash_node
, unitext
, mime_type
, weight
, case_sensitive
);
323 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode
*glob_hash_node
,
324 const char *file_name
,
326 int case_sensitive_check
,
327 MimeWeight mime_types
[],
331 XdgGlobHashNode
*node
;
332 xdg_unichar_t character
;
334 if (glob_hash_node
== NULL
)
337 character
= file_name
[len
- 1];
339 for (node
= glob_hash_node
; node
&& character
>= node
->character
; node
= node
->next
)
341 if (character
== node
->character
)
347 n
= _xdg_glob_hash_node_lookup_file_name (node
->child
,
350 case_sensitive_check
,
356 if (node
->mime_type
&&
357 (case_sensitive_check
||
358 !node
->case_sensitive
))
360 mime_types
[n
].mime
= node
->mime_type
;
361 mime_types
[n
].weight
= node
->weight
;
365 while (n
< n_mime_types
&& node
&& node
->character
== 0)
367 if (node
->mime_type
&&
368 (case_sensitive_check
||
369 !node
->case_sensitive
))
371 mime_types
[n
].mime
= node
->mime_type
;
372 mime_types
[n
].weight
= node
->weight
;
385 static int compare_mime_weight (const void *a
, const void *b
)
387 const MimeWeight
*aa
= (const MimeWeight
*)a
;
388 const MimeWeight
*bb
= (const MimeWeight
*)b
;
390 return bb
->weight
- aa
->weight
;
393 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
395 ascii_tolower (const char *str
)
399 lower
= strdup (str
);
404 *p
++ = ISUPPER (c
) ? c
- 'A' + 'a' : c
;
410 filter_out_dupes (MimeWeight mimes
[], int n_mimes
)
417 for (i
= 0; i
< last
; i
++)
422 if (strcmp (mimes
[i
].mime
, mimes
[j
].mime
) == 0)
424 mimes
[i
].weight
= MAX (mimes
[i
].weight
, mimes
[j
].weight
);
426 mimes
[j
].mime
= mimes
[last
].mime
;
427 mimes
[j
].weight
= mimes
[last
].weight
;
438 _xdg_glob_hash_lookup_file_name (XdgGlobHash
*glob_hash
,
439 const char *file_name
,
440 const char *mime_types
[],
445 MimeWeight mimes
[10];
450 /* First, check the literals */
452 assert (file_name
!= NULL
&& n_mime_types
> 0);
456 lower_case
= ascii_tolower (file_name
);
458 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
460 if (strcmp ((const char *)list
->data
, file_name
) == 0)
462 mime_types
[0] = list
->mime_type
;
468 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
470 if (!list
->case_sensitive
&&
471 strcmp ((const char *)list
->data
, lower_case
) == 0)
473 mime_types
[0] = list
->mime_type
;
480 len
= strlen (file_name
);
481 n
= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, lower_case
, len
, FALSE
,
484 n
+= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, file_name
, len
, TRUE
,
485 mimes
+ n
, n_mimes
- n
);
489 for (list
= glob_hash
->full_list
; list
&& n
< n_mime_types
; list
= list
->next
)
491 if (fnmatch ((const char *)list
->data
, file_name
, 0) == 0)
493 mimes
[n
].mime
= list
->mime_type
;
494 mimes
[n
].weight
= list
->weight
;
501 n
= filter_out_dupes (mimes
, n
);
503 qsort (mimes
, n
, sizeof (MimeWeight
), compare_mime_weight
);
505 if (n_mime_types
< n
)
508 for (i
= 0; i
< n
; i
++)
509 mime_types
[i
] = mimes
[i
].mime
;
520 _xdg_glob_hash_new (void)
522 XdgGlobHash
*glob_hash
;
524 glob_hash
= calloc (1, sizeof (XdgGlobHash
));
531 _xdg_glob_hash_free_nodes (XdgGlobHashNode
*node
)
536 _xdg_glob_hash_free_nodes (node
->child
);
538 _xdg_glob_hash_free_nodes (node
->next
);
540 free ((void *) node
->mime_type
);
546 _xdg_glob_hash_free (XdgGlobHash
*glob_hash
)
548 _xdg_glob_list_free (glob_hash
->literal_list
);
549 _xdg_glob_list_free (glob_hash
->full_list
);
550 _xdg_glob_hash_free_nodes (glob_hash
->simple_node
);
555 _xdg_glob_determine_type (const char *glob
)
558 int maybe_in_simple_glob
= FALSE
;
559 int first_char
= TRUE
;
565 if (*ptr
== '*' && first_char
)
566 maybe_in_simple_glob
= TRUE
;
567 else if (*ptr
== '\\' || *ptr
== '[' || *ptr
== '?' || *ptr
== '*')
568 return XDG_GLOB_FULL
;
571 ptr
= _xdg_utf8_next_char (ptr
);
573 if (maybe_in_simple_glob
)
574 return XDG_GLOB_SIMPLE
;
576 return XDG_GLOB_LITERAL
;
579 /* glob must be valid UTF-8 */
581 _xdg_glob_hash_append_glob (XdgGlobHash
*glob_hash
,
583 const char *mime_type
,
589 assert (glob_hash
!= NULL
);
590 assert (glob
!= NULL
);
592 type
= _xdg_glob_determine_type (glob
);
596 case XDG_GLOB_LITERAL
:
597 glob_hash
->literal_list
= _xdg_glob_list_append (glob_hash
->literal_list
, strdup (glob
), strdup (mime_type
), weight
, case_sensitive
);
599 case XDG_GLOB_SIMPLE
:
600 glob_hash
->simple_node
= _xdg_glob_hash_insert_text (glob_hash
->simple_node
, glob
+ 1, mime_type
, weight
, case_sensitive
);
603 glob_hash
->full_list
= _xdg_glob_list_append (glob_hash
->full_list
, strdup (glob
), strdup (mime_type
), weight
, case_sensitive
);
608 #ifdef NOT_USED_IN_GIO
611 _xdg_glob_hash_dump (XdgGlobHash
*glob_hash
)
614 printf ("LITERAL STRINGS\n");
615 if (!glob_hash
|| glob_hash
->literal_list
== NULL
)
621 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
622 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
624 printf ("\nSIMPLE GLOBS\n");
625 if (!glob_hash
|| glob_hash
->simple_node
== NULL
)
631 _xdg_glob_hash_node_dump (glob_hash
->simple_node
, 4);
634 printf ("\nFULL GLOBS\n");
635 if (!glob_hash
|| glob_hash
->full_list
== NULL
)
641 for (list
= glob_hash
->full_list
; list
; list
= list
->next
)
642 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
649 _xdg_mime_glob_read_from_file (XdgGlobHash
*glob_hash
,
650 const char *file_name
,
657 glob_file
= fopen (file_name
, "r");
659 if (glob_file
== NULL
)
662 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
664 while (fgets (line
, 255, glob_file
) != NULL
)
667 char *mimetype
, *glob
, *end
;
671 if (line
[0] == '#' || line
[0] == 0)
674 end
= line
+ strlen(line
) - 1;
681 colon
= strchr (p
, ':');
691 colon
= strchr (p
, ':');
699 case_sensitive
= FALSE
;
701 colon
= strchr (p
, ':');
702 if (version_two
&& colon
!= NULL
)
710 /* Flags end at next colon */
711 colon
= strchr (p
, ':');
715 flag
= strstr (p
, "cs");
717 /* Start or after comma */
720 /* ends with comma or end of string */
723 case_sensitive
= TRUE
;
726 _xdg_glob_hash_append_glob (glob_hash
, glob
, mimetype
, weight
, case_sensitive
);