]> git.proxmox.com Git - libxdgmime-perl.git/blame - xdgmime-source/src/xdgmimeglob.c
Merge commit '73d5a30a1d93eb79761f2472c685afb8e42a8646' from upstream
[libxdgmime-perl.git] / xdgmime-source / src / xdgmimeglob.c
CommitLineData
cf31d981
SI
1/* -*- mode: C; c-file-style: "gnu" -*- */
2/* xdgmimeglob.c: Private file. Datastructure for storing the globs.
3 *
4 * More info can be found at http://www.freedesktop.org/standards/
5 *
6 * Copyright (C) 2003 Red Hat, Inc.
7 * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
8 *
73d5a30a 9 * SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0
cf31d981
SI
10 */
11
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16#include "xdgmimeglob.h"
17#include "xdgmimeint.h"
18#include <stdlib.h>
19#include <stdio.h>
20#include <assert.h>
21#include <string.h>
22#include <fnmatch.h>
23
24#ifndef FALSE
25#define FALSE (0)
26#endif
27
28#ifndef TRUE
29#define TRUE (!FALSE)
30#endif
31
32typedef struct XdgGlobHashNode XdgGlobHashNode;
33typedef struct XdgGlobList XdgGlobList;
34
35struct XdgGlobHashNode
36{
37 xdg_unichar_t character;
38 const char *mime_type;
39 int weight;
40 int case_sensitive;
41 XdgGlobHashNode *next;
42 XdgGlobHashNode *child;
43};
44struct XdgGlobList
45{
46 const char *data;
47 const char *mime_type;
48 int weight;
49 int case_sensitive;
50 XdgGlobList *next;
51};
52
53struct XdgGlobHash
54{
55 XdgGlobList *literal_list;
56 XdgGlobHashNode *simple_node;
57 XdgGlobList *full_list;
58};
59
60
61/* XdgGlobList
62 */
63static XdgGlobList *
64_xdg_glob_list_new (void)
65{
66 XdgGlobList *new_element;
67
68 new_element = calloc (1, sizeof (XdgGlobList));
69
70 return new_element;
71}
72
73/* Frees glob_list and all of its children */
74static void
75_xdg_glob_list_free (XdgGlobList *glob_list)
76{
77 XdgGlobList *ptr, *next;
78
79 ptr = glob_list;
80
81 while (ptr != NULL)
82 {
83 next = ptr->next;
84
85 if (ptr->data)
86 free ((void *) ptr->data);
87 if (ptr->mime_type)
88 free ((void *) ptr->mime_type);
89 free (ptr);
90
91 ptr = next;
92 }
93}
94
95static XdgGlobList *
96_xdg_glob_list_append (XdgGlobList *glob_list,
97 void *data,
98 const char *mime_type,
99 int weight,
100 int case_sensitive)
101{
102 XdgGlobList *new_element;
103 XdgGlobList *tmp_element;
104
105 tmp_element = glob_list;
106 while (tmp_element != NULL)
107 {
108 if (strcmp (tmp_element->data, data) == 0 &&
109 strcmp (tmp_element->mime_type, mime_type) == 0)
110 return glob_list;
111
112 tmp_element = tmp_element->next;
113 }
114
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)
121 return new_element;
122
123 tmp_element = glob_list;
124 while (tmp_element->next != NULL)
125 tmp_element = tmp_element->next;
126
127 tmp_element->next = new_element;
128
129 return glob_list;
130}
131
132/* XdgGlobHashNode
133 */
134
135static XdgGlobHashNode *
136_xdg_glob_hash_node_new (void)
137{
138 XdgGlobHashNode *glob_hash_node;
139
140 glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
141
142 return glob_hash_node;
143}
144
145static void
146_xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
147 int depth)
148{
149 int i;
150 for (i = 0; i < depth; i++)
151 printf (" ");
152
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);
156 else
157 printf ("\n");
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);
162}
163
164static XdgGlobHashNode *
165_xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
166 xdg_unichar_t *text,
167 const char *mime_type,
168 int weight,
169 int case_sensitive)
170{
171 XdgGlobHashNode *node;
172 xdg_unichar_t character;
173
174 character = text[0];
175
176 if ((glob_hash_node == NULL) ||
177 (character < glob_hash_node->character))
178 {
179 node = _xdg_glob_hash_node_new ();
180 node->character = character;
181 node->next = glob_hash_node;
182 glob_hash_node = node;
183 }
184 else if (character == glob_hash_node->character)
185 {
186 node = glob_hash_node;
187 }
188 else
189 {
190 XdgGlobHashNode *prev_node;
191 int found_node = FALSE;
192
193 /* Look for the first character of text in glob_hash_node, and insert it if we
194 * have to.*/
195 prev_node = glob_hash_node;
196 node = prev_node->next;
197
198 while (node != NULL)
199 {
200 if (character < node->character)
201 {
202 node = _xdg_glob_hash_node_new ();
203 node->character = character;
204 node->next = prev_node->next;
205 prev_node->next = node;
206
207 found_node = TRUE;
208 break;
209 }
210 else if (character == node->character)
211 {
212 found_node = TRUE;
213 break;
214 }
215 prev_node = node;
216 node = node->next;
217 }
218
219 if (! found_node)
220 {
221 node = _xdg_glob_hash_node_new ();
222 node->character = character;
223 node->next = prev_node->next;
224 prev_node->next = node;
225 }
226 }
227
228 text++;
229 if (*text == 0)
230 {
231 if (node->mime_type)
232 {
233 if (strcmp (node->mime_type, mime_type) != 0)
234 {
235 XdgGlobHashNode *child;
236 int found_node = FALSE;
237
238 child = node->child;
239 while (child && child->character == 0)
240 {
241 if (strcmp (child->mime_type, mime_type) == 0)
242 {
243 found_node = TRUE;
244 break;
245 }
246 child = child->next;
247 }
248
249 if (!found_node)
250 {
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;
256 child->child = NULL;
257 child->next = node->child;
258 node->child = child;
259 }
260 }
261 }
262 else
263 {
264 node->mime_type = strdup (mime_type);
265 node->weight = weight;
266 node->case_sensitive = case_sensitive;
267 }
268 }
269 else
270 {
271 node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight, case_sensitive);
272 }
273 return glob_hash_node;
274}
275
276/* glob must be valid UTF-8 */
277static XdgGlobHashNode *
278_xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
279 const char *text,
280 const char *mime_type,
281 int weight,
282 int case_sensitive)
283{
284 XdgGlobHashNode *node;
285 xdg_unichar_t *unitext;
286 int len;
287
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);
291 free (unitext);
292 return node;
293}
294
295typedef struct {
296 const char *mime;
297 int weight;
298} MimeWeight;
299
300static int
301_xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
302 const char *file_name,
303 int len,
304 int case_sensitive_check,
305 MimeWeight mime_types[],
306 int n_mime_types)
307{
308 int n;
309 XdgGlobHashNode *node;
310 xdg_unichar_t character;
311
312 if (glob_hash_node == NULL)
313 return 0;
314
315 character = file_name[len - 1];
316
317 for (node = glob_hash_node; node && character >= node->character; node = node->next)
318 {
319 if (character == node->character)
320 {
321 len--;
322 n = 0;
323 if (len > 0)
324 {
325 n = _xdg_glob_hash_node_lookup_file_name (node->child,
326 file_name,
327 len,
328 case_sensitive_check,
329 mime_types,
330 n_mime_types);
331 }
332 if (n == 0)
333 {
334 if (node->mime_type &&
335 (case_sensitive_check ||
336 !node->case_sensitive))
337 {
338 mime_types[n].mime = node->mime_type;
339 mime_types[n].weight = node->weight;
340 n++;
341 }
342 node = node->child;
343 while (n < n_mime_types && node && node->character == 0)
344 {
345 if (node->mime_type &&
346 (case_sensitive_check ||
347 !node->case_sensitive))
348 {
349 mime_types[n].mime = node->mime_type;
350 mime_types[n].weight = node->weight;
351 n++;
352 }
353 node = node->next;
354 }
355 }
356 return n;
357 }
358 }
359
360 return 0;
361}
362
363static int compare_mime_weight (const void *a, const void *b)
364{
365 const MimeWeight *aa = (const MimeWeight *)a;
366 const MimeWeight *bb = (const MimeWeight *)b;
367
368 return bb->weight - aa->weight;
369}
370
371#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
372static char *
373ascii_tolower (const char *str)
374{
375 char *p, *lower;
376
377 lower = strdup (str);
378 p = lower;
379 while (*p != 0)
380 {
381 char c = *p;
382 *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
383 }
384 return lower;
385}
386
387int
388_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
389 const char *file_name,
390 const char *mime_types[],
391 int n_mime_types)
392{
393 XdgGlobList *list;
394 int i, n;
395 MimeWeight mimes[10];
396 int n_mimes = 10;
397 int len;
398 char *lower_case;
399
400 /* First, check the literals */
401
402 assert (file_name != NULL && n_mime_types > 0);
403
404 n = 0;
405
406 lower_case = ascii_tolower (file_name);
407
408 for (list = glob_hash->literal_list; list; list = list->next)
409 {
410 if (strcmp ((const char *)list->data, file_name) == 0)
411 {
412 mime_types[0] = list->mime_type;
413 free (lower_case);
414 return 1;
415 }
416 }
417
418 for (list = glob_hash->literal_list; list; list = list->next)
419 {
420 if (!list->case_sensitive &&
421 strcmp ((const char *)list->data, lower_case) == 0)
422 {
423 mime_types[0] = list->mime_type;
424 free (lower_case);
425 return 1;
426 }
427 }
428
429
430 len = strlen (file_name);
431 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, lower_case, len, FALSE,
432 mimes, n_mimes);
433 if (n == 0)
434 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, file_name, len, TRUE,
435 mimes, n_mimes);
436
437 if (n == 0)
438 {
439 for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
440 {
441 if (fnmatch ((const char *)list->data, file_name, 0) == 0)
442 {
443 mimes[n].mime = list->mime_type;
444 mimes[n].weight = list->weight;
445 n++;
446 }
447 }
448 }
449 free (lower_case);
450
451 qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
452
453 if (n_mime_types < n)
454 n = n_mime_types;
455
456 for (i = 0; i < n; i++)
457 mime_types[i] = mimes[i].mime;
458
459 return n;
460}
461
462
463
464/* XdgGlobHash
465 */
466
467XdgGlobHash *
468_xdg_glob_hash_new (void)
469{
470 XdgGlobHash *glob_hash;
471
472 glob_hash = calloc (1, sizeof (XdgGlobHash));
473
474 return glob_hash;
475}
476
477
478static void
479_xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
480{
481 if (node)
482 {
483 if (node->child)
484 _xdg_glob_hash_free_nodes (node->child);
485 if (node->next)
486 _xdg_glob_hash_free_nodes (node->next);
487 if (node->mime_type)
488 free ((void *) node->mime_type);
489 free (node);
490 }
491}
492
493void
494_xdg_glob_hash_free (XdgGlobHash *glob_hash)
495{
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);
499 free (glob_hash);
500}
501
502XdgGlobType
503_xdg_glob_determine_type (const char *glob)
504{
505 const char *ptr;
506 int maybe_in_simple_glob = FALSE;
507 int first_char = TRUE;
508
509 ptr = glob;
510
511 while (*ptr != '\0')
512 {
513 if (*ptr == '*' && first_char)
514 maybe_in_simple_glob = TRUE;
515 else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
516 return XDG_GLOB_FULL;
517
518 first_char = FALSE;
519 ptr = _xdg_utf8_next_char (ptr);
520 }
521 if (maybe_in_simple_glob)
522 return XDG_GLOB_SIMPLE;
523 else
524 return XDG_GLOB_LITERAL;
525}
526
527/* glob must be valid UTF-8 */
528void
529_xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
530 const char *glob,
531 const char *mime_type,
532 int weight,
533 int case_sensitive)
534{
535 XdgGlobType type;
536
537 assert (glob_hash != NULL);
538 assert (glob != NULL);
539
540 type = _xdg_glob_determine_type (glob);
541
542 switch (type)
543 {
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);
546 break;
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);
549 break;
550 case XDG_GLOB_FULL:
551 glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
552 break;
553 }
554}
555
556void
557_xdg_glob_hash_dump (XdgGlobHash *glob_hash)
558{
559 XdgGlobList *list;
560 printf ("LITERAL STRINGS\n");
561 if (!glob_hash || glob_hash->literal_list == NULL)
562 {
563 printf (" None\n");
564 }
565 else
566 {
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);
569 }
570 printf ("\nSIMPLE GLOBS\n");
571 if (!glob_hash || glob_hash->simple_node == NULL)
572 {
573 printf (" None\n");
574 }
575 else
576 {
577 _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
578 }
579
580 printf ("\nFULL GLOBS\n");
581 if (!glob_hash || glob_hash->full_list == NULL)
582 {
583 printf (" None\n");
584 }
585 else
586 {
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);
589 }
590}
591
592
593void
594_xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
595 const char *file_name,
596 int version_two)
597{
598 FILE *glob_file;
599 char line[255];
600 char *p;
601
602 glob_file = fopen (file_name, "r");
603
604 if (glob_file == NULL)
605 return;
606
607 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
608 * Blah */
609 while (fgets (line, 255, glob_file) != NULL)
610 {
611 char *colon;
612 char *mimetype, *glob, *end;
613 int weight;
614 int case_sensitive;
615
616 if (line[0] == '#' || line[0] == 0)
617 continue;
618
619 end = line + strlen(line) - 1;
620 if (*end == '\n')
621 *end = 0;
622
623 p = line;
624 if (version_two)
625 {
626 colon = strchr (p, ':');
627 if (colon == NULL)
628 continue;
629 *colon = 0;
630 weight = atoi (p);
631 p = colon + 1;
632 }
633 else
634 weight = 50;
635
636 colon = strchr (p, ':');
637 if (colon == NULL)
638 continue;
639 *colon = 0;
640
641 mimetype = p;
642 p = colon + 1;
643 glob = p;
644 case_sensitive = FALSE;
645
646 colon = strchr (p, ':');
647 if (version_two && colon != NULL)
648 {
649 char *flag;
650
651 /* We got flags */
652 *colon = 0;
653 p = colon + 1;
654
655 /* Flags end at next colon */
656 colon = strchr (p, ':');
657 if (colon != NULL)
658 *colon = 0;
659
660 flag = strstr (p, "cs");
661 if (flag != NULL &&
662 /* Start or after comma */
663 (flag == p ||
664 flag[-1] == ',') &&
665 /* ends with comma or end of string */
666 (flag[2] == 0 ||
667 flag[2] == ','))
668 case_sensitive = TRUE;
669 }
670
671 _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight, case_sensitive);
672 }
673
674 fclose (glob_file);
675}