Coverage Report

Created: 2025-07-07 10:01

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