Coverage Report

Created: 2025-11-16 07:45

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