Coverage Report

Created: 2025-08-26 06:04

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