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 * SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0
16 #include "xdgmimeglob.h"
17 #include "xdgmimeint.h"
32 typedef struct XdgGlobHashNode XdgGlobHashNode
;
33 typedef struct XdgGlobList XdgGlobList
;
35 struct XdgGlobHashNode
37 xdg_unichar_t character
;
38 const char *mime_type
;
41 XdgGlobHashNode
*next
;
42 XdgGlobHashNode
*child
;
47 const char *mime_type
;
55 XdgGlobList
*literal_list
;
56 XdgGlobHashNode
*simple_node
;
57 XdgGlobList
*full_list
;
64 _xdg_glob_list_new (void)
66 XdgGlobList
*new_element
;
68 new_element
= calloc (1, sizeof (XdgGlobList
));
73 /* Frees glob_list and all of its children */
75 _xdg_glob_list_free (XdgGlobList
*glob_list
)
77 XdgGlobList
*ptr
, *next
;
86 free ((void *) ptr
->data
);
88 free ((void *) ptr
->mime_type
);
96 _xdg_glob_list_append (XdgGlobList
*glob_list
,
98 const char *mime_type
,
102 XdgGlobList
*new_element
;
103 XdgGlobList
*tmp_element
;
105 tmp_element
= glob_list
;
106 while (tmp_element
!= NULL
)
108 if (strcmp (tmp_element
->data
, data
) == 0 &&
109 strcmp (tmp_element
->mime_type
, mime_type
) == 0)
112 tmp_element
= tmp_element
->next
;
115 new_element
= _xdg_glob_list_new ();
116 new_element
->data
= data
;
117 new_element
->mime_type
= mime_type
;
118 new_element
->weight
= weight
;
119 new_element
->case_sensitive
= case_sensitive
;
120 if (glob_list
== NULL
)
123 tmp_element
= glob_list
;
124 while (tmp_element
->next
!= NULL
)
125 tmp_element
= tmp_element
->next
;
127 tmp_element
->next
= new_element
;
135 static XdgGlobHashNode
*
136 _xdg_glob_hash_node_new (void)
138 XdgGlobHashNode
*glob_hash_node
;
140 glob_hash_node
= calloc (1, sizeof (XdgGlobHashNode
));
142 return glob_hash_node
;
146 _xdg_glob_hash_node_dump (XdgGlobHashNode
*glob_hash_node
,
150 for (i
= 0; i
< depth
; i
++)
153 printf ("%c", (char)glob_hash_node
->character
);
154 if (glob_hash_node
->mime_type
)
155 printf (" - %s %d\n", glob_hash_node
->mime_type
, glob_hash_node
->weight
);
158 if (glob_hash_node
->child
)
159 _xdg_glob_hash_node_dump (glob_hash_node
->child
, depth
+ 1);
160 if (glob_hash_node
->next
)
161 _xdg_glob_hash_node_dump (glob_hash_node
->next
, depth
);
164 static XdgGlobHashNode
*
165 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode
*glob_hash_node
,
167 const char *mime_type
,
171 XdgGlobHashNode
*node
;
172 xdg_unichar_t character
;
176 if ((glob_hash_node
== NULL
) ||
177 (character
< glob_hash_node
->character
))
179 node
= _xdg_glob_hash_node_new ();
180 node
->character
= character
;
181 node
->next
= glob_hash_node
;
182 glob_hash_node
= node
;
184 else if (character
== glob_hash_node
->character
)
186 node
= glob_hash_node
;
190 XdgGlobHashNode
*prev_node
;
191 int found_node
= FALSE
;
193 /* Look for the first character of text in glob_hash_node, and insert it if we
195 prev_node
= glob_hash_node
;
196 node
= prev_node
->next
;
200 if (character
< node
->character
)
202 node
= _xdg_glob_hash_node_new ();
203 node
->character
= character
;
204 node
->next
= prev_node
->next
;
205 prev_node
->next
= node
;
210 else if (character
== node
->character
)
221 node
= _xdg_glob_hash_node_new ();
222 node
->character
= character
;
223 node
->next
= prev_node
->next
;
224 prev_node
->next
= node
;
233 if (strcmp (node
->mime_type
, mime_type
) != 0)
235 XdgGlobHashNode
*child
;
236 int found_node
= FALSE
;
239 while (child
&& child
->character
== 0)
241 if (strcmp (child
->mime_type
, mime_type
) == 0)
251 child
= _xdg_glob_hash_node_new ();
252 child
->character
= 0;
253 child
->mime_type
= strdup (mime_type
);
254 child
->weight
= weight
;
255 child
->case_sensitive
= case_sensitive
;
257 child
->next
= node
->child
;
264 node
->mime_type
= strdup (mime_type
);
265 node
->weight
= weight
;
266 node
->case_sensitive
= case_sensitive
;
271 node
->child
= _xdg_glob_hash_insert_ucs4 (node
->child
, text
, mime_type
, weight
, case_sensitive
);
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
,
284 XdgGlobHashNode
*node
;
285 xdg_unichar_t
*unitext
;
288 unitext
= _xdg_convert_to_ucs4 (text
, &len
);
289 _xdg_reverse_ucs4 (unitext
, len
);
290 node
= _xdg_glob_hash_insert_ucs4 (glob_hash_node
, unitext
, mime_type
, weight
, case_sensitive
);
301 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode
*glob_hash_node
,
302 const char *file_name
,
304 int case_sensitive_check
,
305 MimeWeight mime_types
[],
309 XdgGlobHashNode
*node
;
310 xdg_unichar_t character
;
312 if (glob_hash_node
== NULL
)
315 character
= file_name
[len
- 1];
317 for (node
= glob_hash_node
; node
&& character
>= node
->character
; node
= node
->next
)
319 if (character
== node
->character
)
325 n
= _xdg_glob_hash_node_lookup_file_name (node
->child
,
328 case_sensitive_check
,
334 if (node
->mime_type
&&
335 (case_sensitive_check
||
336 !node
->case_sensitive
))
338 mime_types
[n
].mime
= node
->mime_type
;
339 mime_types
[n
].weight
= node
->weight
;
343 while (n
< n_mime_types
&& node
&& node
->character
== 0)
345 if (node
->mime_type
&&
346 (case_sensitive_check
||
347 !node
->case_sensitive
))
349 mime_types
[n
].mime
= node
->mime_type
;
350 mime_types
[n
].weight
= node
->weight
;
363 static int compare_mime_weight (const void *a
, const void *b
)
365 const MimeWeight
*aa
= (const MimeWeight
*)a
;
366 const MimeWeight
*bb
= (const MimeWeight
*)b
;
368 return bb
->weight
- aa
->weight
;
371 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
373 ascii_tolower (const char *str
)
377 lower
= strdup (str
);
382 *p
++ = ISUPPER (c
) ? c
- 'A' + 'a' : c
;
388 _xdg_glob_hash_lookup_file_name (XdgGlobHash
*glob_hash
,
389 const char *file_name
,
390 const char *mime_types
[],
395 MimeWeight mimes
[10];
400 /* First, check the literals */
402 assert (file_name
!= NULL
&& n_mime_types
> 0);
406 lower_case
= ascii_tolower (file_name
);
408 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
410 if (strcmp ((const char *)list
->data
, file_name
) == 0)
412 mime_types
[0] = list
->mime_type
;
418 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
420 if (!list
->case_sensitive
&&
421 strcmp ((const char *)list
->data
, lower_case
) == 0)
423 mime_types
[0] = list
->mime_type
;
430 len
= strlen (file_name
);
431 n
= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, lower_case
, len
, FALSE
,
434 n
= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, file_name
, len
, TRUE
,
439 for (list
= glob_hash
->full_list
; list
&& n
< n_mime_types
; list
= list
->next
)
441 if (fnmatch ((const char *)list
->data
, file_name
, 0) == 0)
443 mimes
[n
].mime
= list
->mime_type
;
444 mimes
[n
].weight
= list
->weight
;
451 qsort (mimes
, n
, sizeof (MimeWeight
), compare_mime_weight
);
453 if (n_mime_types
< n
)
456 for (i
= 0; i
< n
; i
++)
457 mime_types
[i
] = mimes
[i
].mime
;
468 _xdg_glob_hash_new (void)
470 XdgGlobHash
*glob_hash
;
472 glob_hash
= calloc (1, sizeof (XdgGlobHash
));
479 _xdg_glob_hash_free_nodes (XdgGlobHashNode
*node
)
484 _xdg_glob_hash_free_nodes (node
->child
);
486 _xdg_glob_hash_free_nodes (node
->next
);
488 free ((void *) node
->mime_type
);
494 _xdg_glob_hash_free (XdgGlobHash
*glob_hash
)
496 _xdg_glob_list_free (glob_hash
->literal_list
);
497 _xdg_glob_list_free (glob_hash
->full_list
);
498 _xdg_glob_hash_free_nodes (glob_hash
->simple_node
);
503 _xdg_glob_determine_type (const char *glob
)
506 int maybe_in_simple_glob
= FALSE
;
507 int first_char
= TRUE
;
513 if (*ptr
== '*' && first_char
)
514 maybe_in_simple_glob
= TRUE
;
515 else if (*ptr
== '\\' || *ptr
== '[' || *ptr
== '?' || *ptr
== '*')
516 return XDG_GLOB_FULL
;
519 ptr
= _xdg_utf8_next_char (ptr
);
521 if (maybe_in_simple_glob
)
522 return XDG_GLOB_SIMPLE
;
524 return XDG_GLOB_LITERAL
;
527 /* glob must be valid UTF-8 */
529 _xdg_glob_hash_append_glob (XdgGlobHash
*glob_hash
,
531 const char *mime_type
,
537 assert (glob_hash
!= NULL
);
538 assert (glob
!= NULL
);
540 type
= _xdg_glob_determine_type (glob
);
544 case XDG_GLOB_LITERAL
:
545 glob_hash
->literal_list
= _xdg_glob_list_append (glob_hash
->literal_list
, strdup (glob
), strdup (mime_type
), weight
, case_sensitive
);
547 case XDG_GLOB_SIMPLE
:
548 glob_hash
->simple_node
= _xdg_glob_hash_insert_text (glob_hash
->simple_node
, glob
+ 1, mime_type
, weight
, case_sensitive
);
551 glob_hash
->full_list
= _xdg_glob_list_append (glob_hash
->full_list
, strdup (glob
), strdup (mime_type
), weight
, case_sensitive
);
557 _xdg_glob_hash_dump (XdgGlobHash
*glob_hash
)
560 printf ("LITERAL STRINGS\n");
561 if (!glob_hash
|| glob_hash
->literal_list
== NULL
)
567 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
568 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
570 printf ("\nSIMPLE GLOBS\n");
571 if (!glob_hash
|| glob_hash
->simple_node
== NULL
)
577 _xdg_glob_hash_node_dump (glob_hash
->simple_node
, 4);
580 printf ("\nFULL GLOBS\n");
581 if (!glob_hash
|| glob_hash
->full_list
== NULL
)
587 for (list
= glob_hash
->full_list
; list
; list
= list
->next
)
588 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
594 _xdg_mime_glob_read_from_file (XdgGlobHash
*glob_hash
,
595 const char *file_name
,
602 glob_file
= fopen (file_name
, "r");
604 if (glob_file
== NULL
)
607 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
609 while (fgets (line
, 255, glob_file
) != NULL
)
612 char *mimetype
, *glob
, *end
;
616 if (line
[0] == '#' || line
[0] == 0)
619 end
= line
+ strlen(line
) - 1;
626 colon
= strchr (p
, ':');
636 colon
= strchr (p
, ':');
644 case_sensitive
= FALSE
;
646 colon
= strchr (p
, ':');
647 if (version_two
&& colon
!= NULL
)
655 /* Flags end at next colon */
656 colon
= strchr (p
, ':');
660 flag
= strstr (p
, "cs");
662 /* Start or after comma */
665 /* ends with comma or end of string */
668 case_sensitive
= TRUE
;
671 _xdg_glob_hash_append_glob (glob_hash
, glob
, mimetype
, weight
, case_sensitive
);