Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/fontconfig/src/fcdir.c
Line
Count
Source
1
/*
2
 * fontconfig/src/fcdir.c
3
 *
4
 * Copyright © 2000 Keith Packard
5
 *
6
 * Permission to use, copy, modify, distribute, and sell this software and its
7
 * documentation for any purpose is hereby granted without fee, provided that
8
 * the above copyright notice appear in all copies and that both that
9
 * copyright notice and this permission notice appear in supporting
10
 * documentation, and that the name of the author(s) not be used in
11
 * advertising or publicity pertaining to distribution of the software without
12
 * specific, written prior permission.  The authors make no
13
 * representations about the suitability of this software for any purpose.  It
14
 * is provided "as is" without express or implied warranty.
15
 *
16
 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18
 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22
 * PERFORMANCE OF THIS SOFTWARE.
23
 */
24
25
#include "fcint.h"
26
27
#ifdef HAVE_DIRENT_H
28
#  include <dirent.h>
29
#endif
30
31
#if ENABLE_FONTATIONS
32
#  include "fontconfig/fcfontations.h"
33
#endif
34
35
#if ENABLE_FREETYPE
36
#include "fontconfig/fcfreetype.h"
37
#endif
38
39
FcBool
40
FcFileIsDir (const FcChar8 *file)
41
121
{
42
121
    struct stat statb;
43
44
121
    if (FcStat (file, &statb) != 0)
45
3
  return FcFalse;
46
118
    return S_ISDIR (statb.st_mode);
47
121
}
48
49
FcBool
50
FcFileIsLink (const FcChar8 *file)
51
0
{
52
0
#if HAVE_LSTAT
53
0
    struct stat statb;
54
55
0
    if (lstat ((const char *)file, &statb) != 0)
56
0
  return FcFalse;
57
0
    return S_ISLNK (statb.st_mode);
58
#else
59
    return FcFalse;
60
#endif
61
0
}
62
63
FcBool
64
FcFileIsFile (const FcChar8 *file)
65
0
{
66
0
    struct stat statb;
67
68
0
    if (FcStat (file, &statb) != 0)
69
0
  return FcFalse;
70
0
    return S_ISREG (statb.st_mode);
71
0
}
72
73
static FcBool
74
FcFileScanFontConfig (FcFontSet     *set,
75
                      const FcChar8 *file,
76
                      FcConfig      *config)
77
17
{
78
17
    int            i;
79
17
    FcBool         ret = FcTrue;
80
17
    int            old_nfont = set->nfont;
81
17
    const FcChar8 *sysroot = FcConfigGetSysRoot (config);
82
83
17
    if (FcDebug() & FC_DBG_SCAN) {
84
0
  printf ("\tScanning file %s...", file);
85
0
  fflush (stdout);
86
0
    }
87
88
17
    unsigned int (*query_function) (const FcChar8 *, unsigned int, FcBlanks *, int *, FcFontSet *) =
89
#if ENABLE_FONTATIONS && !defined(ENABLE_FREETYPE)
90
    FcFontationsQueryAll;
91
#elif ENABLE_FREETYPE
92
    FcFreeTypeQueryAll;
93
17
#endif
94
95
#if ENABLE_FONTATIONS
96
    if (getenv ("FC_FONTATIONS")) {
97
  query_function = FcFontationsQueryAll;
98
    }
99
#endif
100
17
    if (!query_function (file, -1, NULL, NULL, set))
101
3
  return FcFalse;
102
103
14
    if (FcDebug() & FC_DBG_SCAN)
104
0
  printf ("done\n");
105
106
28
    for (i = old_nfont; i < set->nfont; i++) {
107
14
  FcPattern *font = set->fonts[i];
108
109
  /*
110
   * Get rid of sysroot here so that targeting scan rule may contains FC_FILE pattern
111
   * and they should usually expect without sysroot.
112
   */
113
14
  if (sysroot) {
114
0
      size_t   len = strlen ((const char *)sysroot);
115
0
      FcChar8 *f = NULL;
116
117
0
      if (FcPatternObjectGetString (font, FC_FILE_OBJECT, 0, &f) == FcResultMatch &&
118
0
          strncmp ((const char *)f, (const char *)sysroot, len) == 0) {
119
0
    FcChar8 *s = FcStrCopy (f);
120
0
    FcPatternObjectDel (font, FC_FILE_OBJECT);
121
0
    if (s[len] != '/')
122
0
        len--;
123
0
    else if (s[len + 1] == '/')
124
0
        len++;
125
0
    FcPatternObjectAddString (font, FC_FILE_OBJECT, &s[len]);
126
0
    FcStrFree (s);
127
0
      }
128
0
  }
129
130
  /*
131
   * Edit pattern with user-defined rules
132
   */
133
14
  if (config && !FcConfigSubstitute (config, font, FcMatchScan))
134
0
      ret = FcFalse;
135
136
14
  if (FcDebug() & FC_DBG_SCANV) {
137
0
      printf ("Final font pattern:\n");
138
0
      FcPatternPrint (font);
139
0
  }
140
14
    }
141
142
14
    return ret;
143
17
}
144
145
FcBool
146
FcFileScanConfig (FcFontSet     *set,
147
                  FcStrSet      *dirs,
148
                  const FcChar8 *file,
149
                  FcConfig      *config)
150
17
{
151
17
    if (FcFileIsDir (file)) {
152
0
  const FcChar8 *sysroot = FcConfigGetSysRoot (config);
153
0
  const FcChar8 *d = file;
154
0
  size_t         len;
155
156
0
  if (sysroot) {
157
0
      len = strlen ((const char *)sysroot);
158
0
      if (strncmp ((const char *)file, (const char *)sysroot, len) == 0) {
159
0
    if (file[len] != '/')
160
0
        len--;
161
0
    else if (file[len + 1] == '/')
162
0
        len++;
163
0
    d = &file[len];
164
0
      }
165
0
  }
166
0
  return FcStrSetAdd (dirs, d);
167
17
    } else {
168
17
  if (set)
169
17
      return FcFileScanFontConfig (set, file, config);
170
0
  else
171
0
      return FcTrue;
172
17
    }
173
17
}
174
175
FcBool
176
FcFileScan (FcFontSet     *set,
177
            FcStrSet      *dirs,
178
            FcFileCache   *cache FC_UNUSED,
179
            FcBlanks      *blanks FC_UNUSED,
180
            const FcChar8 *file,
181
            FcBool         force FC_UNUSED)
182
0
{
183
0
    FcConfig *config;
184
0
    FcBool    ret;
185
186
0
    config = FcConfigReference (NULL);
187
0
    if (!config)
188
0
  return FcFalse;
189
0
    ret = FcFileScanConfig (set, dirs, file, config);
190
0
    FcConfigDestroy (config);
191
192
0
    return ret;
193
0
}
194
195
/*
196
 * Strcmp helper that takes pointers to pointers, copied from qsort(3) manpage
197
 */
198
static int
199
cmpstringp (const void *p1, const void *p2)
200
36
{
201
36
    return strcmp (*(char **)p1, *(char **)p2);
202
36
}
203
204
FcBool
205
FcDirScanConfig (FcFontSet     *set,
206
                 FcStrSet      *dirs,
207
                 const FcChar8 *dir,
208
                 FcBool         force, /* XXX unused */
209
                 FcConfig      *config)
210
1
{
211
1
    DIR           *d;
212
1
    struct dirent *e;
213
1
    FcStrSet      *files;
214
1
    FcChar8       *file_prefix = NULL, *s_dir = NULL;
215
1
    FcChar8       *base;
216
1
    const FcChar8 *sysroot = FcConfigGetSysRoot (config);
217
1
    FcBool         ret = FcTrue;
218
1
    int            i;
219
220
1
    if (!force)
221
0
  return FcFalse;
222
223
1
    if (!set && !dirs)
224
0
  return FcTrue;
225
226
1
    if (sysroot)
227
0
  s_dir = FcStrBuildFilename (sysroot, dir, NULL);
228
1
    else
229
1
  s_dir = FcStrCopy (dir);
230
1
    if (!s_dir) {
231
0
  ret = FcFalse;
232
0
  goto bail;
233
0
    }
234
235
    /* freed below */
236
1
    file_prefix = (FcChar8 *)malloc (strlen ((char *)s_dir) + 1 + FC_MAX_FILE_LEN + 1);
237
1
    if (!file_prefix) {
238
0
  ret = FcFalse;
239
0
  goto bail;
240
0
    }
241
1
    strcpy ((char *)file_prefix, (char *)s_dir);
242
1
    strcat ((char *)file_prefix, FC_DIR_SEPARATOR_S);
243
1
    base = file_prefix + strlen ((char *)file_prefix);
244
245
1
    if (FcDebug() & FC_DBG_SCAN)
246
0
  printf ("\tScanning dir %s\n", s_dir);
247
248
1
    d = opendir ((char *)s_dir);
249
1
    if (!d) {
250
  /* Don't complain about missing directories */
251
0
  if (errno != ENOENT)
252
0
      ret = FcFalse;
253
0
  goto bail;
254
0
    }
255
256
1
    files = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64);
257
1
    if (!files) {
258
0
  ret = FcFalse;
259
0
  goto bail1;
260
0
    }
261
16
    while ((e = readdir (d))) {
262
  /* Ignore . and .. */
263
15
  if (e->d_name[0] == '.' &&
264
2
      (e->d_name[1] == 0 ||
265
1
       (e->d_name[1] == '.' && e->d_name[2] == 0)))
266
2
      continue;
267
13
  if (strlen (e->d_name) < FC_MAX_FILE_LEN) {
268
13
      strcpy ((char *)base, (char *)e->d_name);
269
13
      if (!FcStrSetAdd (files, file_prefix)) {
270
0
    ret = FcFalse;
271
0
    goto bail2;
272
0
      }
273
13
  }
274
13
    }
275
276
    /*
277
     * Sort files to make things prettier
278
     */
279
1
    if (files->num)
280
1
  qsort (files->strs, files->num, sizeof (FcChar8 *), cmpstringp);
281
282
    /*
283
     * Scan file files to build font patterns
284
     */
285
14
    for (i = 0; i < files->num; i++)
286
13
  FcFileScanConfig (set, dirs, files->strs[i], config);
287
288
1
bail2:
289
1
    FcStrSetDestroy (files);
290
1
bail1:
291
1
    closedir (d);
292
1
bail:
293
1
    if (s_dir)
294
1
  free (s_dir);
295
1
    if (file_prefix)
296
1
  free (file_prefix);
297
298
1
    return ret;
299
1
}
300
301
FcBool
302
FcDirScan (FcFontSet     *set,
303
           FcStrSet      *dirs,
304
           FcFileCache   *cache FC_UNUSED,
305
           FcBlanks      *blanks FC_UNUSED,
306
           const FcChar8 *dir,
307
           FcBool         force FC_UNUSED)
308
0
{
309
0
    FcConfig *config;
310
0
    FcBool    ret;
311
312
0
    if (cache || !force)
313
0
  return FcFalse;
314
315
0
    config = FcConfigReference (NULL);
316
0
    if (!config)
317
0
  return FcFalse;
318
0
    ret = FcDirScanConfig (set, dirs, dir, force, config);
319
0
    FcConfigDestroy (config);
320
321
0
    return ret;
322
0
}
323
324
/*
325
 * Scan the specified directory and construct a cache of its contents
326
 */
327
FcCache *
328
FcDirCacheScan (const FcChar8 *dir, FcConfig *config)
329
209
{
330
209
    FcStrSet      *dirs;
331
209
    FcFontSet     *set;
332
209
    FcCache       *cache = NULL;
333
209
    struct stat    dir_stat;
334
209
    const FcChar8 *sysroot = FcConfigGetSysRoot (config);
335
209
    FcChar8       *d;
336
209
#ifndef _WIN32
337
209
    int fd = -1;
338
209
#endif
339
340
209
    if (!FcConfigAcceptFilename (config, dir)) {
341
0
  if (FcDebug() & FC_DBG_CACHE)
342
0
      printf ("%s: skipping, matching with deny list\n", dir);
343
0
  return NULL;
344
0
    }
345
209
    if (sysroot)
346
0
  d = FcStrBuildFilename (sysroot, dir, NULL);
347
209
    else
348
209
  d = FcStrCopy (dir);
349
350
209
    if (FcDebug() & FC_DBG_FONTSET)
351
0
  printf ("cache scan dir %s\n", d);
352
353
209
    if (FcStatChecksum (d, &dir_stat) < 0)
354
208
  goto bail;
355
356
1
    set = FcFontSetCreate();
357
1
    if (!set)
358
0
  goto bail;
359
360
1
    dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
361
1
    if (!dirs)
362
0
  goto bail1;
363
364
1
#ifndef _WIN32
365
1
    fd = FcDirCacheLock (dir, config);
366
1
#endif
367
    /*
368
     * Scan the dir
369
     */
370
    /* Do not pass sysroot here. FcDirScanConfig() do take care of it */
371
1
    if (!FcDirScanConfig (set, dirs, dir, FcTrue, config))
372
0
  goto bail2;
373
374
    /*
375
     * Build the cache object
376
     */
377
1
    cache = FcDirCacheBuild (set, dir, &dir_stat, dirs);
378
1
    if (!cache)
379
0
  goto bail2;
380
381
    /*
382
     * Write out the cache file, ignoring any troubles
383
     */
384
1
    FcDirCacheWrite (cache, config);
385
386
1
bail2:
387
1
#ifndef _WIN32
388
1
    FcDirCacheUnlock (fd);
389
1
#endif
390
1
    FcStrSetDestroy (dirs);
391
1
bail1:
392
1
    FcFontSetDestroy (set);
393
209
bail:
394
209
    FcStrFree (d);
395
396
209
    return cache;
397
1
}
398
399
FcCache *
400
FcDirCacheRescan (const FcChar8 *dir, FcConfig *config)
401
0
{
402
0
    FcCache       *cache;
403
0
    FcCache       *newp = NULL;
404
0
    struct stat    dir_stat;
405
0
    FcStrSet      *dirs;
406
0
    const FcChar8 *sysroot;
407
0
    FcChar8       *d = NULL;
408
0
#ifndef _WIN32
409
0
    int fd = -1;
410
0
#endif
411
412
0
    config = FcConfigReference (config);
413
0
    if (!config)
414
0
  return NULL;
415
0
    sysroot = FcConfigGetSysRoot (config);
416
0
    cache = FcDirCacheLoad (dir, config, NULL);
417
0
    if (!cache)
418
0
  goto bail;
419
420
0
    if (sysroot)
421
0
  d = FcStrBuildFilename (sysroot, dir, NULL);
422
0
    else
423
0
  d = FcStrCopy (dir);
424
0
    if (FcStatChecksum (d, &dir_stat) < 0)
425
0
  goto bail;
426
0
    dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
427
0
    if (!dirs)
428
0
  goto bail;
429
430
0
#ifndef _WIN32
431
0
    fd = FcDirCacheLock (dir, config);
432
0
#endif
433
    /*
434
     * Scan the dir
435
     */
436
    /* Do not pass sysroot here. FcDirScanConfig() do take care of it */
437
0
    if (!FcDirScanConfig (NULL, dirs, dir, FcTrue, config))
438
0
  goto bail1;
439
    /*
440
     * Rebuild the cache object
441
     */
442
0
    newp = FcDirCacheRebuild (cache, &dir_stat, dirs);
443
0
    if (!newp)
444
0
  goto bail1;
445
0
    FcDirCacheUnload (cache);
446
    /*
447
     * Write out the cache file, ignoring any troubles
448
     */
449
0
    FcDirCacheWrite (newp, config);
450
451
0
bail1:
452
0
#ifndef _WIN32
453
0
    FcDirCacheUnlock (fd);
454
0
#endif
455
0
    FcStrSetDestroy (dirs);
456
0
bail:
457
0
    if (d)
458
0
  FcStrFree (d);
459
0
    FcConfigDestroy (config);
460
461
0
    return newp;
462
0
}
463
464
/*
465
 * Read (or construct) the cache for a directory
466
 */
467
FcCache *
468
FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config)
469
312
{
470
312
    FcCache *cache = NULL;
471
472
312
    config = FcConfigReference (config);
473
    /* Try to use existing cache file */
474
312
    if (!force)
475
312
  cache = FcDirCacheLoad (dir, config, NULL);
476
477
    /* Not using existing cache file, construct new cache */
478
312
    if (!cache) {
479
209
  FcDirCacheDeleteUUID (dir, config);
480
209
  cache = FcDirCacheScan (dir, config);
481
209
    }
482
312
    FcConfigDestroy (config);
483
484
312
    return cache;
485
312
}
486
487
FcBool
488
FcDirSave (FcFontSet *set FC_UNUSED, FcStrSet *dirs FC_UNUSED, const FcChar8 *dir FC_UNUSED)
489
0
{
490
0
    return FcFalse; /* XXX deprecated */
491
0
}
492
#define __fcdir__
493
#include "fcaliastail.h"
494
#undef __fcdir__