Coverage Report

Created: 2025-06-13 06:55

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