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