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
;
56 XdgGlobHashNode
*next
;
57 XdgGlobHashNode
*child
;
62 const char *mime_type
;
69 XdgGlobList
*literal_list
;
70 XdgGlobHashNode
*simple_node
;
71 XdgGlobList
*full_list
;
78 _xdg_glob_list_new (void)
80 XdgGlobList
*new_element
;
82 new_element
= calloc (1, sizeof (XdgGlobList
));
87 /* Frees glob_list and all of it's children */
89 _xdg_glob_list_free (XdgGlobList
*glob_list
)
91 XdgGlobList
*ptr
, *next
;
100 free ((void *) ptr
->data
);
102 free ((void *) ptr
->mime_type
);
110 _xdg_glob_list_append (XdgGlobList
*glob_list
,
112 const char *mime_type
,
115 XdgGlobList
*new_element
;
116 XdgGlobList
*tmp_element
;
118 new_element
= _xdg_glob_list_new ();
119 new_element
->data
= data
;
120 new_element
->mime_type
= mime_type
;
121 new_element
->weight
= weight
;
122 if (glob_list
== NULL
)
125 tmp_element
= glob_list
;
126 while (tmp_element
->next
!= NULL
)
127 tmp_element
= tmp_element
->next
;
129 tmp_element
->next
= new_element
;
137 static XdgGlobHashNode
*
138 _xdg_glob_hash_node_new (void)
140 XdgGlobHashNode
*glob_hash_node
;
142 glob_hash_node
= calloc (1, sizeof (XdgGlobHashNode
));
144 return glob_hash_node
;
148 _xdg_glob_hash_node_dump (XdgGlobHashNode
*glob_hash_node
,
152 for (i
= 0; i
< depth
; i
++)
155 printf ("%c", (char)glob_hash_node
->character
);
156 if (glob_hash_node
->mime_type
)
157 printf (" - %s %d\n", glob_hash_node
->mime_type
, glob_hash_node
->weight
);
160 if (glob_hash_node
->child
)
161 _xdg_glob_hash_node_dump (glob_hash_node
->child
, depth
+ 1);
162 if (glob_hash_node
->next
)
163 _xdg_glob_hash_node_dump (glob_hash_node
->next
, depth
);
166 static XdgGlobHashNode
*
167 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode
*glob_hash_node
,
169 const char *mime_type
,
172 XdgGlobHashNode
*node
;
173 xdg_unichar_t character
;
177 if ((glob_hash_node
== NULL
) ||
178 (character
< glob_hash_node
->character
))
180 node
= _xdg_glob_hash_node_new ();
181 node
->character
= character
;
182 node
->next
= glob_hash_node
;
183 glob_hash_node
= node
;
185 else if (character
== glob_hash_node
->character
)
187 node
= glob_hash_node
;
191 XdgGlobHashNode
*prev_node
;
192 int found_node
= FALSE
;
194 /* Look for the first character of text in glob_hash_node, and insert it if we
196 prev_node
= glob_hash_node
;
197 node
= prev_node
->next
;
201 if (character
< node
->character
)
203 node
= _xdg_glob_hash_node_new ();
204 node
->character
= character
;
205 node
->next
= prev_node
->next
;
206 prev_node
->next
= node
;
211 else 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
;
234 if (strcmp (node
->mime_type
, mime_type
))
236 XdgGlobHashNode
*child
;
237 int found_node
= FALSE
;
240 while (child
&& child
->character
== 0)
242 if (strcmp (child
->mime_type
, mime_type
) == 0)
252 child
= _xdg_glob_hash_node_new ();
253 child
->character
= 0;
254 child
->mime_type
= strdup (mime_type
);
255 child
->weight
= weight
;
257 child
->next
= node
->child
;
264 node
->mime_type
= strdup (mime_type
);
265 node
->weight
= weight
;
270 node
->child
= _xdg_glob_hash_insert_ucs4 (node
->child
, text
, mime_type
, weight
);
272 return glob_hash_node
;
275 /* glob must be valid UTF-8 */
276 static XdgGlobHashNode
*
277 _xdg_glob_hash_insert_text (XdgGlobHashNode
*glob_hash_node
,
279 const char *mime_type
,
282 XdgGlobHashNode
*node
;
283 xdg_unichar_t
*unitext
;
286 unitext
= _xdg_convert_to_ucs4 (text
, &len
);
287 _xdg_reverse_ucs4 (unitext
, len
);
288 node
= _xdg_glob_hash_insert_ucs4 (glob_hash_node
, unitext
, mime_type
, weight
);
299 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode
*glob_hash_node
,
300 xdg_unichar_t
*file_name
,
303 MimeWeight mime_types
[],
307 XdgGlobHashNode
*node
;
308 xdg_unichar_t character
;
310 if (glob_hash_node
== NULL
)
313 character
= file_name
[len
- 1];
315 character
= _xdg_ucs4_to_lower(character
);
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
,
336 mime_types
[n
].mime
= node
->mime_type
;
337 mime_types
[n
].weight
= node
->weight
;
341 while (n
< n_mime_types
&& node
&& node
->character
== 0)
345 mime_types
[n
].mime
= node
->mime_type
;
346 mime_types
[n
].weight
= node
->weight
;
359 static int compare_mime_weight (const void *a
, const void *b
)
361 const MimeWeight
*aa
= (const MimeWeight
*)a
;
362 const MimeWeight
*bb
= (const MimeWeight
*)b
;
364 return aa
->weight
- bb
->weight
;
368 _xdg_glob_hash_lookup_file_name (XdgGlobHash
*glob_hash
,
369 const char *file_name
,
370 const char *mime_types
[],
375 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 ucs4
= _xdg_convert_to_ucs4 (file_name
, &len
);
396 n
= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, ucs4
, len
, FALSE
,
399 n
= _xdg_glob_hash_node_lookup_file_name (glob_hash
->simple_node
, ucs4
, len
, TRUE
,
403 /* FIXME: Not UTF-8 safe */
406 for (list
= glob_hash
->full_list
; list
&& n
< n_mime_types
; list
= list
->next
)
408 if (fnmatch ((const char *)list
->data
, file_name
, 0) == 0)
410 mimes
[n
].mime
= list
->mime_type
;
411 mimes
[n
].weight
= list
->weight
;
417 qsort (mimes
, n
, sizeof (MimeWeight
), compare_mime_weight
);
419 if (n_mime_types
< n
)
422 for (i
= 0; i
< n
; i
++)
423 mime_types
[i
] = mimes
[i
].mime
;
434 _xdg_glob_hash_new (void)
436 XdgGlobHash
*glob_hash
;
438 glob_hash
= calloc (1, sizeof (XdgGlobHash
));
445 _xdg_glob_hash_free_nodes (XdgGlobHashNode
*node
)
450 _xdg_glob_hash_free_nodes (node
->child
);
452 _xdg_glob_hash_free_nodes (node
->next
);
454 free ((void *) node
->mime_type
);
460 _xdg_glob_hash_free (XdgGlobHash
*glob_hash
)
462 _xdg_glob_list_free (glob_hash
->literal_list
);
463 _xdg_glob_list_free (glob_hash
->full_list
);
464 _xdg_glob_hash_free_nodes (glob_hash
->simple_node
);
469 _xdg_glob_determine_type (const char *glob
)
472 int maybe_in_simple_glob
= FALSE
;
473 int first_char
= TRUE
;
479 if (*ptr
== '*' && first_char
)
480 maybe_in_simple_glob
= TRUE
;
481 else if (*ptr
== '\\' || *ptr
== '[' || *ptr
== '?' || *ptr
== '*')
482 return XDG_GLOB_FULL
;
485 ptr
= _xdg_utf8_next_char (ptr
);
487 if (maybe_in_simple_glob
)
488 return XDG_GLOB_SIMPLE
;
490 return XDG_GLOB_LITERAL
;
493 /* glob must be valid UTF-8 */
495 _xdg_glob_hash_append_glob (XdgGlobHash
*glob_hash
,
497 const char *mime_type
,
502 assert (glob_hash
!= NULL
);
503 assert (glob
!= NULL
);
505 type
= _xdg_glob_determine_type (glob
);
509 case XDG_GLOB_LITERAL
:
510 glob_hash
->literal_list
= _xdg_glob_list_append (glob_hash
->literal_list
, strdup (glob
), strdup (mime_type
), weight
);
512 case XDG_GLOB_SIMPLE
:
513 glob_hash
->simple_node
= _xdg_glob_hash_insert_text (glob_hash
->simple_node
, glob
+ 1, mime_type
, weight
);
516 glob_hash
->full_list
= _xdg_glob_list_append (glob_hash
->full_list
, strdup (glob
), strdup (mime_type
), weight
);
522 _xdg_glob_hash_dump (XdgGlobHash
*glob_hash
)
525 printf ("LITERAL STRINGS\n");
526 if (!glob_hash
|| glob_hash
->literal_list
== NULL
)
532 for (list
= glob_hash
->literal_list
; list
; list
= list
->next
)
533 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
535 printf ("\nSIMPLE GLOBS\n");
536 if (!glob_hash
|| glob_hash
->simple_node
== NULL
)
542 _xdg_glob_hash_node_dump (glob_hash
->simple_node
, 4);
545 printf ("\nFULL GLOBS\n");
546 if (!glob_hash
|| glob_hash
->full_list
== NULL
)
552 for (list
= glob_hash
->full_list
; list
; list
= list
->next
)
553 printf (" %s - %s %d\n", (char *)list
->data
, list
->mime_type
, list
->weight
);
559 _xdg_mime_glob_read_from_file (XdgGlobHash
*glob_hash
,
560 const char *file_name
)
565 glob_file
= fopen (file_name
, "r");
567 if (glob_file
== NULL
)
570 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
572 while (fgets (line
, 255, glob_file
) != NULL
)
574 char *colon
, *colon2
;
575 char *mimetype
, *glob
;
581 colon
= strchr (line
, ':');
585 colon
[strlen (colon
) -1] = '\0';
586 colon2
= strchr (colon
, ':');
589 *(colon2
++) = '\000';
590 weight
= atoi (line
);
600 _xdg_glob_hash_append_glob (glob_hash
, glob
, mimetype
, weight
);