Coverage Report

Created: 2026-05-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/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
0
{
42
0
    struct stat statb;
43
44
0
    if (FcStat (file, &statb) != 0)
45
0
  return FcFalse;
46
0
    return S_ISDIR (statb.st_mode);
47
0
}
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
0
{
78
0
    int            i;
79
0
    FcBool         ret = FcTrue;
80
0
    int            old_nfont = set->nfont;
81
0
    const FcChar8 *sysroot = FcConfigGetSysRoot (config);
82
83
0
    if (FcDebug() & FC_DBG_SCAN) {
84
0
  printf ("\tScanning file %s...", file);
85
0
  fflush (stdout);
86
0
    }
87
88
0
    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
0
#endif
94
95
#if ENABLE_FONTATIONS
96
    if (getenv ("FC_FONTATIONS")) {
97
  query_function = FcFontationsQueryAll;
98
    }
99
#endif
100
0
    if (!query_function (file, -1, NULL, NULL, set))
101
0
  return FcFalse;
102
103
0
    if (FcDebug() & FC_DBG_SCAN)
104
0
  printf ("done\n");
105
106
0
    for (i = old_nfont; i < set->nfont; i++) {
107
0
  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
0
  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
0
  if (config && !FcConfigSubstitute (config, font, FcMatchScan))
134
0
      ret = FcFalse;
135
136
0
  if (FcDebug() & FC_DBG_SCANV) {
137
0
      printf ("Final font pattern:\n");
138
0
      FcPatternPrint (font);
139
0
  }
140
0
    }
141
142
0
    return ret;
143
0
}
144
145
FcBool
146
FcFileScanConfig (FcFontSet     *set,
147
                  FcStrSet      *dirs,
148
                  const FcChar8 *file,
149
                  FcConfig      *config)
150
0
{
151
0
    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
0
    } else {
168
0
  if (set)
169
0
      return FcFileScanFontConfig (set, file, config);
170
0
  else
171
0
      return FcTrue;
172
0
    }
173
0
}
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
0
{
201
0
    return strcmp (*(char **)p1, *(char **)p2);
202
0
}
203
204
FcBool
205
FcDirScanConfig (FcFontSet     *set,
206
                 FcStrSet      *dirs,
207
                 const FcChar8 *dir,
208
                 FcBool         force, /* XXX unused */
209
                 FcConfig      *config)
210
0
{
211
0
    DIR           *d;
212
0
    struct dirent *e;
213
0
    FcStrSet      *files;
214
0
    FcChar8       *file_prefix = NULL, *s_dir = NULL;
215
0
    FcChar8       *base;
216
0
    const FcChar8 *sysroot = FcConfigGetSysRoot (config);
217
0
    FcBool         ret = FcTrue;
218
0
    int            i;
219
220
0
    if (!force)
221
0
  return FcFalse;
222
223
0
    if (!set && !dirs)
224
0
  return FcTrue;
225
226
0
    if (sysroot)
227
0
  s_dir = FcStrBuildFilename (sysroot, dir, NULL);
228
0
    else
229
0
  s_dir = FcStrCopy (dir);
230
0
    if (!s_dir) {
231
0
  ret = FcFalse;
232
0
  goto bail;
233
0
    }
234
235
    /* freed below */
236
0
    file_prefix = (FcChar8 *)malloc (strlen ((char *)s_dir) + 1 + FC_MAX_FILE_LEN + 1);
237
0
    if (!file_prefix) {
238
0
  ret = FcFalse;
239
0
  goto bail;
240
0
    }
241
0
    strcpy ((char *)file_prefix, (char *)s_dir);
242
0
    strcat ((char *)file_prefix, FC_DIR_SEPARATOR_S);
243
0
    base = file_prefix + strlen ((char *)file_prefix);
244
245
0
    if (FcDebug() & FC_DBG_SCAN)
246
0
  printf ("\tScanning dir %s\n", s_dir);
247
248
0
    d = opendir ((char *)s_dir);
249
0
    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
0
    files = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64);
257
0
    if (!files) {
258
0
  ret = FcFalse;
259
0
  goto bail1;
260
0
    }
261
0
    while ((e = readdir (d))) {
262
  /* Ignore . and .. */
263
0
  if (e->d_name[0] == '.' &&
264
0
      (e->d_name[1] == 0 ||
265
0
       (e->d_name[1] == '.' && e->d_name[2] == 0)))
266
0
      continue;
267
0
  if (strlen (e->d_name) < FC_MAX_FILE_LEN) {
268
0
      strcpy ((char *)base, (char *)e->d_name);
269
0
      if (!FcStrSetAdd (files, file_prefix)) {
270
0
    ret = FcFalse;
271
0
    goto bail2;
272
0
      }
273
0
  }
274
0
    }
275
276
    /*
277
     * Sort files to make things prettier
278
     */
279
0
    if (files->num)
280
0
  qsort (files->strs, files->num, sizeof (FcChar8 *), cmpstringp);
281
282
    /*
283
     * Scan file files to build font patterns
284
     */
285
0
    for (i = 0; i < files->num; i++)
286
0
  FcFileScanConfig (set, dirs, files->strs[i], config);
287
288
0
bail2:
289
0
    FcStrSetDestroy (files);
290
0
bail1:
291
0
    closedir (d);
292
0
bail:
293
0
    if (s_dir)
294
0
  free (s_dir);
295
0
    if (file_prefix)
296
0
  free (file_prefix);
297
298
0
    return ret;
299
0
}
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
0
{
330
0
    FcStrSet      *dirs;
331
0
    FcFontSet     *set;
332
0
    FcCache       *cache = NULL;
333
0
    struct stat    dir_stat;
334
0
    const FcChar8 *sysroot = FcConfigGetSysRoot (config);
335
0
    FcChar8       *d;
336
0
#ifndef _WIN32
337
0
    int fd = -1;
338
0
#endif
339
340
0
    if (sysroot)
341
0
  d = FcStrBuildFilename (sysroot, dir, NULL);
342
0
    else
343
0
  d = FcStrCopy (dir);
344
345
0
    if (FcDebug() & FC_DBG_FONTSET)
346
0
  printf ("cache scan dir %s\n", d);
347
348
0
    if (FcStatChecksum (d, &dir_stat) < 0)
349
0
  goto bail;
350
351
0
    set = FcFontSetCreate();
352
0
    if (!set)
353
0
  goto bail;
354
355
0
    dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
356
0
    if (!dirs)
357
0
  goto bail1;
358
359
0
#ifndef _WIN32
360
0
    fd = FcDirCacheLock (dir, config);
361
0
#endif
362
    /*
363
     * Scan the dir
364
     */
365
    /* Do not pass sysroot here. FcDirScanConfig() do take care of it */
366
0
    if (!FcDirScanConfig (set, dirs, dir, FcTrue, config))
367
0
  goto bail2;
368
369
    /*
370
     * Build the cache object
371
     */
372
0
    cache = FcDirCacheBuild (set, dir, &dir_stat, dirs);
373
0
    if (!cache)
374
0
  goto bail2;
375
376
    /*
377
     * Write out the cache file, ignoring any troubles
378
     */
379
0
    FcDirCacheWrite (cache, config);
380
381
0
bail2:
382
0
#ifndef _WIN32
383
0
    FcDirCacheUnlock (fd);
384
0
#endif
385
0
    FcStrSetDestroy (dirs);
386
0
bail1:
387
0
    FcFontSetDestroy (set);
388
0
bail:
389
0
    FcStrFree (d);
390
391
0
    return cache;
392
0
}
393
394
FcCache *
395
FcDirCacheRescan (const FcChar8 *dir, FcConfig *config)
396
0
{
397
0
    FcCache       *cache;
398
0
    FcCache       *newp = NULL;
399
0
    struct stat    dir_stat;
400
0
    FcStrSet      *dirs;
401
0
    const FcChar8 *sysroot;
402
0
    FcChar8       *d = NULL;
403
0
#ifndef _WIN32
404
0
    int fd = -1;
405
0
#endif
406
407
0
    config = FcConfigReference (config);
408
0
    if (!config)
409
0
  return NULL;
410
0
    sysroot = FcConfigGetSysRoot (config);
411
0
    cache = FcDirCacheLoad (dir, config, NULL);
412
0
    if (!cache)
413
0
  goto bail;
414
415
0
    if (sysroot)
416
0
  d = FcStrBuildFilename (sysroot, dir, NULL);
417
0
    else
418
0
  d = FcStrCopy (dir);
419
0
    if (FcStatChecksum (d, &dir_stat) < 0)
420
0
  goto bail;
421
0
    dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
422
0
    if (!dirs)
423
0
  goto bail;
424
425
0
#ifndef _WIN32
426
0
    fd = FcDirCacheLock (dir, config);
427
0
#endif
428
    /*
429
     * Scan the dir
430
     */
431
    /* Do not pass sysroot here. FcDirScanConfig() do take care of it */
432
0
    if (!FcDirScanConfig (NULL, dirs, dir, FcTrue, config))
433
0
  goto bail1;
434
    /*
435
     * Rebuild the cache object
436
     */
437
0
    newp = FcDirCacheRebuild (cache, &dir_stat, dirs);
438
0
    if (!newp)
439
0
  goto bail1;
440
0
    FcDirCacheUnload (cache);
441
    /*
442
     * Write out the cache file, ignoring any troubles
443
     */
444
0
    FcDirCacheWrite (newp, config);
445
446
0
bail1:
447
0
#ifndef _WIN32
448
0
    FcDirCacheUnlock (fd);
449
0
#endif
450
0
    FcStrSetDestroy (dirs);
451
0
bail:
452
0
    if (d)
453
0
  FcStrFree (d);
454
0
    FcConfigDestroy (config);
455
456
0
    return newp;
457
0
}
458
459
/*
460
 * Read (or construct) the cache for a directory
461
 */
462
FcCache *
463
FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config)
464
0
{
465
0
    FcCache *cache = NULL;
466
467
0
    config = FcConfigReference (config);
468
    /* Try to use existing cache file */
469
0
    if (!force)
470
0
  cache = FcDirCacheLoad (dir, config, NULL);
471
472
    /* Not using existing cache file, construct new cache */
473
0
    if (!cache) {
474
0
  FcDirCacheDeleteUUID (dir, config);
475
0
  cache = FcDirCacheScan (dir, config);
476
0
    }
477
0
    FcConfigDestroy (config);
478
479
0
    return cache;
480
0
}
481
482
FcBool
483
FcDirSave (FcFontSet *set FC_UNUSED, FcStrSet *dirs FC_UNUSED, const FcChar8 *dir FC_UNUSED)
484
0
{
485
0
    return FcFalse; /* XXX deprecated */
486
0
}
487
#define __fcdir__
488
#include "fcaliastail.h"
489
#undef __fcdir__