Coverage Report

Created: 2026-10-14 08:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fontconfig/src/fccfg.c
Line
Count
Source
1
/*
2
 * fontconfig/src/fccfg.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
/* Objects MT-safe for readonly access. */
26
27
#include "fcint.h"
28
29
#include "fontconfig/fontconfig.h"
30
#ifdef HAVE_DIRENT_H
31
#  include <dirent.h>
32
#endif
33
#include <sys/types.h>
34
35
#if defined(_WIN32) && !defined(R_OK)
36
#  define R_OK 4
37
#endif
38
39
#if defined(_WIN32) && !defined(S_ISFIFO)
40
#  define S_ISFIFO(m) 0
41
#endif
42
43
static FcConfig *_fcConfig; /* MT-safe */
44
static FcMutex  *_lock;
45
46
static void
47
lock_config (void)
48
322k
{
49
322k
    FcMutex *lock;
50
322k
retry:
51
322k
    lock = fc_atomic_ptr_get (&_lock);
52
322k
    if (!lock) {
53
11
  lock = (FcMutex *)malloc (sizeof (FcMutex));
54
11
  FcMutexInit (lock);
55
11
  if (!fc_atomic_ptr_cmpexch (&_lock, NULL, lock)) {
56
0
      FcMutexFinish (lock);
57
0
      free (lock);
58
0
      goto retry;
59
0
  }
60
11
  FcMutexLock (lock);
61
  /* Initialize random state */
62
11
  FcRandom();
63
11
  return;
64
11
    }
65
322k
    FcMutexLock (lock);
66
322k
}
67
68
static void
69
unlock_config (void)
70
322k
{
71
322k
    FcMutex *lock;
72
322k
    lock = fc_atomic_ptr_get (&_lock);
73
322k
    if (lock)
74
322k
  FcMutexUnlock (lock);
75
322k
}
76
77
static void
78
free_lock (void)
79
0
{
80
0
    FcMutex *lock;
81
0
    lock = fc_atomic_ptr_get (&_lock);
82
0
    if (lock && fc_atomic_ptr_cmpexch (&_lock, lock, NULL)) {
83
0
  FcMutexFinish (lock);
84
0
  free (lock);
85
0
    }
86
0
}
87
88
static FcConfig *
89
FcConfigEnsureLocked (void)
90
322k
{
91
322k
    FcConfig *config = fc_atomic_ptr_get (&_fcConfig);
92
322k
    if (!config) {
93
11
  config = FcInitLoadConfigAndFonts();
94
11
  if (config) {
95
11
      if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
96
0
    FcConfigDestroy (config);
97
0
    config = fc_atomic_ptr_get (&_fcConfig);
98
0
      }
99
11
  }
100
11
    }
101
322k
    return config;
102
322k
}
103
104
static FcConfig *
105
FcConfigEnsure (void)
106
237
{
107
237
    FcConfig *config;
108
109
237
    config = fc_atomic_ptr_get (&_fcConfig);
110
237
    if (!config) {
111
0
  lock_config();
112
0
  config = FcConfigEnsureLocked();
113
0
  unlock_config();
114
0
    }
115
237
    return config;
116
237
}
117
118
static void
119
FcDestroyAsRule (void *data)
120
0
{
121
0
    FcRuleDestroy (data);
122
0
}
123
124
static void
125
FcDestroyAsRuleSet (void *data)
126
0
{
127
0
    FcRuleSetDestroy (data);
128
0
}
129
130
FcBool
131
FcConfigInit (void)
132
0
{
133
0
    FcBool is_new;
134
0
    FcBool ret;
135
136
0
    lock_config();
137
0
    is_new = (fc_atomic_ptr_get (&_fcConfig) == NULL);
138
0
    ret = FcConfigEnsureLocked() ? FcTrue : FcFalse;
139
0
    if (ret && !is_new)
140
0
  FcRefInc (&_fcConfig->ref);
141
0
    unlock_config();
142
0
    return ret;
143
0
}
144
145
void
146
FcConfigFini (void)
147
0
{
148
0
    FcConfig *cfg;
149
150
0
    FcConfigDestroy (_fcConfig);
151
0
    cfg = fc_atomic_ptr_get (&_fcConfig);
152
0
    if (!cfg)
153
0
  free_lock();
154
0
}
155
156
FcConfig *
157
FcConfigCreate (void)
158
22
{
159
22
    FcSetName   set;
160
22
    FcConfig   *config;
161
22
    FcMatchKind k;
162
22
    FcBool      err = FcFalse;
163
164
22
    config = calloc (1, sizeof (FcConfig));
165
22
    if (!config)
166
0
  goto bail0;
167
22
    FcRefInit (&config->ref, 1);
168
22
    FcObjectInit();
169
170
22
    config->configDirs = FcStrSetCreate();
171
22
    if (!config->configDirs)
172
0
  goto bail1;
173
174
22
    config->configFiles = FcStrSetCreate();
175
22
    if (!config->configFiles)
176
0
  goto bail1;
177
178
22
    config->fontDirs = FcStrSetCreateEx (FCSS_TRIPLE);
179
22
    if (!config->fontDirs)
180
0
  goto bail1;
181
182
22
    config->acceptGlobs = FcStrSetCreate();
183
22
    if (!config->acceptGlobs)
184
0
  goto bail1;
185
186
22
    config->rejectGlobs = FcStrSetCreate();
187
22
    if (!config->rejectGlobs)
188
0
  goto bail1;
189
190
22
    config->acceptPatterns = FcFontSetCreate();
191
22
    if (!config->acceptPatterns)
192
0
  goto bail1;
193
194
22
    config->rejectPatterns = FcFontSetCreate();
195
22
    if (!config->rejectPatterns)
196
0
  goto bail1;
197
198
22
    config->cacheDirs = FcStrSetCreate();
199
22
    if (!config->cacheDirs)
200
0
  goto bail1;
201
202
88
    for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++) {
203
66
  config->subst[k] = FcPtrListCreate (FcDestroyAsRuleSet);
204
66
  if (!config->subst[k])
205
0
      err = FcTrue;
206
66
    }
207
22
    if (err)
208
0
  goto bail1;
209
210
22
    config->maxObjects = 0;
211
66
    for (set = FcSetSystem; set <= FcSetApplication; set++)
212
44
  config->fonts[set] = 0;
213
22
    FcRwLockInit (&config->fonts_lock);
214
215
22
    config->rescanTime = time (0);
216
22
    config->rescanInterval = 30;
217
218
22
    config->expr_pool = NULL;
219
220
22
    config->sysRoot = FcStrRealPath ((const FcChar8 *)getenv ("FONTCONFIG_SYSROOT"));
221
222
22
    config->rulesetList = FcPtrListCreate (FcDestroyAsRuleSet);
223
22
    if (!config->rulesetList)
224
0
  goto bail1;
225
22
    config->availConfigFiles = FcStrSetCreate();
226
22
    if (!config->availConfigFiles)
227
0
  goto bail1;
228
229
22
    config->filter_func = NULL;
230
22
    config->filter_data = NULL;
231
22
    config->destroy_data_func = NULL;
232
22
    config->default_lang = NULL;
233
22
    config->default_langs = NULL;
234
22
    config->prgname = NULL;
235
22
    config->desktop_name = NULL;
236
237
22
    config->prefer_app_fonts = FcFalse;
238
22
    config->warns = 0;
239
240
22
    config->appFonts = FcStrSetCreate();
241
22
    if (!config->appFonts)
242
0
  goto bail1;
243
244
22
    return config;
245
246
0
bail1:
247
0
    FcConfigDestroy (config);
248
0
bail0:
249
0
    return 0;
250
0
}
251
252
static FcFileTime
253
FcConfigNewestFile (FcStrSet *files)
254
8
{
255
8
    FcStrList  *list = FcStrListCreate (files);
256
8
    FcFileTime  newest = { 0, FcFalse };
257
8
    FcChar8    *file;
258
8
    struct stat statb;
259
260
8
    if (list) {
261
18
  while ((file = FcStrListNext (list)))
262
10
      if (FcStat (file, &statb) == 0)
263
6
    if (!newest.set || statb.st_mtime - newest.time > 0) {
264
4
        newest.set = FcTrue;
265
4
        newest.time = statb.st_mtime;
266
4
    }
267
8
  FcStrListDone (list);
268
8
    }
269
8
    return newest;
270
8
}
271
272
FcBool
273
FcConfigUptoDate (FcConfig *config)
274
2
{
275
2
    FcFileTime config_time, config_dir_time, font_time, app_fonts_time;
276
2
    time_t     now = time (0);
277
2
    FcBool     ret = FcTrue;
278
279
2
    config = FcConfigReference (config);
280
2
    if (!config)
281
0
  return FcFalse;
282
283
2
    config_time = FcConfigNewestFile (config->configFiles);
284
2
    config_dir_time = FcConfigNewestFile (config->configDirs);
285
2
    font_time = FcConfigNewestFile (config->fontDirs);
286
2
    app_fonts_time = FcConfigNewestFile (config->appFonts);
287
2
    if ((config_time.set && config_time.time - config->rescanTime > 0) ||
288
2
        (config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) ||
289
2
        (font_time.set && (font_time.time - config->rescanTime) > 0) ||
290
2
        (app_fonts_time.set && (app_fonts_time.time - config->rescanTime) > 0)) {
291
  /* We need to check for potential clock problems here (OLPC ticket #6046) */
292
0
  if ((config_time.set && (config_time.time - now) > 0) ||
293
0
      (config_dir_time.set && (config_dir_time.time - now) > 0) ||
294
0
      (font_time.set && (font_time.time - now) > 0) ||
295
0
      (app_fonts_time.set && (app_fonts_time.time - now) > 0)) {
296
0
      fprintf (stderr,
297
0
               "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n");
298
0
      config->rescanTime = now;
299
0
      goto bail;
300
0
  } else {
301
0
      ret = FcFalse;
302
0
      goto bail;
303
0
  }
304
0
    }
305
2
    config->rescanTime = now;
306
2
bail:
307
2
    FcConfigDestroy (config);
308
309
2
    return ret;
310
2
}
311
312
FcExpr *
313
FcConfigAllocExpr (FcConfig *config)
314
0
{
315
0
    if (!config->expr_pool || config->expr_pool->next == config->expr_pool->end) {
316
0
  FcExprPage *new_page;
317
318
0
  new_page = malloc (sizeof (FcExprPage));
319
0
  if (!new_page)
320
0
      return 0;
321
322
0
  new_page->next_page = config->expr_pool;
323
0
  new_page->next = new_page->exprs;
324
0
  config->expr_pool = new_page;
325
0
    }
326
327
0
    return config->expr_pool->next++;
328
0
}
329
330
FcConfig *
331
FcConfigReference (FcConfig *config)
332
860k
{
333
860k
    if (!config) {
334
  /* lock during obtaining the value from _fcConfig and count up refcount there,
335
   * there are the race between them.
336
   */
337
322k
  lock_config();
338
322k
  config = FcConfigEnsureLocked();
339
322k
  if (config)
340
322k
      FcRefInc (&config->ref);
341
322k
  unlock_config();
342
322k
    } else
343
537k
  FcRefInc (&config->ref);
344
345
860k
    return config;
346
860k
}
347
348
void
349
FcConfigDestroy (FcConfig *config)
350
860k
{
351
860k
    FcSetName   set;
352
860k
    FcExprPage *page;
353
860k
    FcMatchKind k;
354
355
860k
    if (config) {
356
860k
  if (FcRefDec (&config->ref) != 1)
357
860k
      return;
358
359
11
  FcObjectFini();
360
11
  (void)fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
361
362
11
  if (config->configDirs)
363
11
      FcStrSetDestroy (config->configDirs);
364
11
  if (config->fontDirs)
365
11
      FcStrSetDestroy (config->fontDirs);
366
11
  if (config->cacheDirs)
367
11
      FcStrSetDestroy (config->cacheDirs);
368
11
  if (config->configFiles)
369
11
      FcStrSetDestroy (config->configFiles);
370
11
  if (config->acceptGlobs)
371
11
      FcStrSetDestroy (config->acceptGlobs);
372
11
  if (config->rejectGlobs)
373
11
      FcStrSetDestroy (config->rejectGlobs);
374
11
  if (config->acceptPatterns)
375
11
      FcFontSetDestroy (config->acceptPatterns);
376
11
  if (config->rejectPatterns)
377
11
      FcFontSetDestroy (config->rejectPatterns);
378
379
44
  for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++) {
380
33
      if (config->subst[k])
381
33
    FcPtrListDestroy (config->subst[k]);
382
33
  }
383
11
  if (config->rulesetList)
384
11
      FcPtrListDestroy (config->rulesetList);
385
11
  if (config->availConfigFiles)
386
11
      FcStrSetDestroy (config->availConfigFiles);
387
33
  for (set = FcSetSystem; set <= FcSetApplication; set++) {
388
22
      if (config->fonts[set])
389
0
    FcFontSetDestroy (config->fonts[set]);
390
22
  }
391
392
11
  page = config->expr_pool;
393
11
  while (page) {
394
0
      FcExprPage *next = page->next_page;
395
0
      free (page);
396
0
      page = next;
397
0
  }
398
11
  if (config->sysRoot)
399
0
      FcStrFree (config->sysRoot);
400
401
11
  if (config->filter_data && config->destroy_data_func)
402
0
      config->destroy_data_func (config->filter_data);
403
404
11
  if (config->default_lang)
405
0
      FcStrFree (config->default_lang);
406
11
  if (config->default_langs) {
407
0
      FcRefInit (&config->default_langs->ref, 1);
408
0
      FcStrSetDestroy (config->default_langs);
409
0
  }
410
11
  if (config->prgname)
411
0
      FcStrFree (config->prgname);
412
11
  if (config->desktop_name)
413
0
      FcStrFree (config->desktop_name);
414
11
  if (config->appFonts)
415
11
      FcStrSetDestroy (config->appFonts);
416
417
11
  FcRwLockFinish (&config->fonts_lock);
418
419
11
  free (config);
420
11
    }
421
860k
}
422
423
/*
424
 * Add cache to configuration, adding fonts and directories
425
 */
426
427
FcBool
428
FcConfigAddCache (FcConfig *config, FcCache *cache,
429
                  FcSetName set, FcStrSet *dirSet, FcChar8 *forDir)
430
66
{
431
66
    FcFontSet *fs;
432
66
    intptr_t  *dirs;
433
66
    int        i;
434
66
    FcBool     relocated = FcFalse;
435
436
66
    if (strcmp ((char *)FcCacheDir (cache), (char *)forDir) != 0)
437
0
  relocated = FcTrue;
438
439
    /*
440
     * Add fonts
441
     */
442
66
    fs = FcCacheSet (cache);
443
66
    if (fs) {
444
66
  int nref = 0;
445
446
550
  for (i = 0; i < fs->nfont; i++) {
447
484
      FcPattern *font = FcFontSetFont (fs, i);
448
484
      FcChar8   *font_file;
449
484
      FcChar8   *relocated_font_file = NULL;
450
451
484
      if (FcPatternObjectGetString (font, FC_FILE_OBJECT,
452
484
                                    0, &font_file) == FcResultMatch) {
453
484
    if (relocated) {
454
0
        FcChar8 *slash = FcStrLastSlash (font_file);
455
0
        relocated_font_file = FcStrBuildFilename (forDir, slash + 1, NULL);
456
0
        font_file = relocated_font_file;
457
0
    }
458
459
    /*
460
     * Check to see if font is banned by filename
461
     */
462
484
    if (!FcConfigAcceptFilename (config, font_file)) {
463
0
        free (relocated_font_file);
464
0
        continue;
465
0
    }
466
484
      }
467
468
      /*
469
       * Check to see if font is banned by pattern
470
       */
471
484
      if (!FcConfigAcceptFont (config, font)) {
472
0
    free (relocated_font_file);
473
0
    continue;
474
0
      }
475
476
      /*
477
       * Check to see if font is banned by client
478
       */
479
484
      if (!FcConfigAcceptFilter (config, font)) {
480
0
    free (relocated_font_file);
481
0
    continue;
482
0
      }
483
484
      if (relocated_font_file) {
484
0
    font = FcPatternCacheRewriteFile (font, cache, relocated_font_file);
485
0
    free (relocated_font_file);
486
0
      }
487
488
484
      if (FcFontSetAdd (config->fonts[set], font))
489
484
    nref++;
490
484
  }
491
66
  FcDirCacheReference (cache, nref);
492
66
    }
493
494
    /*
495
     * Add directories
496
     */
497
66
    dirs = FcCacheDirs (cache);
498
66
    if (dirs) {
499
110
  for (i = 0; i < cache->dirs_count; i++) {
500
44
      const FcChar8 *dir = FcCacheSubdir (cache, i);
501
44
      FcChar8       *s = NULL;
502
503
44
      if (relocated) {
504
0
    FcChar8 *base = FcStrBasename (dir);
505
0
    FcChar8 *p;
506
0
    p = FcStrBuildFilename (forDir, base, NULL);
507
0
    dir = s = FcStrCopyFilename (p);
508
0
    FcStrFree (p);
509
0
    FcStrFree (base);
510
0
      }
511
44
      if (FcConfigAcceptFilename (config, dir)) {
512
44
    if (FcStrSetHasControlBit (dirSet, FCSS_TRIPLE))
513
44
        FcStrSetAddTriple (dirSet, dir, NULL, NULL);
514
0
    else
515
0
        FcStrSetAdd (dirSet, dir);
516
44
      }
517
44
      if (s)
518
0
    FcStrFree (s);
519
44
  }
520
66
    }
521
66
    return FcTrue;
522
66
}
523
524
static FcBool
525
FcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet)
526
22
{
527
22
    FcStrList *dirlist;
528
22
    FcChar8   *dir;
529
22
    FcCache   *cache;
530
531
22
    dirlist = FcStrListCreate (dirSet);
532
22
    if (!dirlist)
533
0
  return FcFalse;
534
535
132
    while ((dir = FcStrListNext (dirlist))) {
536
110
  if (FcDebug() & FC_DBG_FONTSET)
537
0
      printf ("adding fonts from %s\n", dir);
538
110
  cache = FcDirCacheRead (dir, FcFalse, config);
539
110
  if (!cache)
540
44
      continue;
541
66
  FcConfigAddCache (config, cache, set, dirSet, dir);
542
66
  FcDirCacheUnload (cache);
543
66
    }
544
22
    FcStrListDone (dirlist);
545
22
    return FcTrue;
546
22
}
547
548
/*
549
 * Scan the current list of directories in the configuration
550
 * and build the set of available fonts.
551
 */
552
553
static void
554
FcConfigSetFontsLocked (FcConfig  *config,
555
                        FcFontSet *fonts,
556
                        FcSetName  set)
557
22
{
558
22
    if (config->fonts[set])
559
11
  FcFontSetDestroy (config->fonts[set]);
560
22
    config->fonts[set] = fonts;
561
22
}
562
563
FcBool
564
FcConfigBuildFonts (FcConfig *config)
565
22
{
566
22
    FcFontSet *fonts;
567
22
    FcBool     ret = FcTrue;
568
569
22
    config = FcConfigReference (config);
570
22
    if (!config)
571
0
  return FcFalse;
572
573
22
    fonts = FcFontSetCreate();
574
22
    if (!fonts) {
575
0
  ret = FcFalse;
576
0
  goto bail;
577
0
    }
578
579
22
    FcRwLockWriteLock (&config->fonts_lock);
580
22
    FcConfigSetFontsLocked (config, fonts, FcSetSystem);
581
582
22
    if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs)) {
583
0
  ret = FcFalse;
584
0
  goto bail_unlock;
585
0
    }
586
22
    if (FcDebug() & FC_DBG_FONTSET)
587
0
  FcFontSetPrint (fonts);
588
22
bail_unlock:
589
22
    FcRwLockUnlockWrite (&config->fonts_lock);
590
22
bail:
591
22
    FcConfigDestroy (config);
592
593
22
    return ret;
594
22
}
595
596
FcBool
597
FcConfigSetCurrent (FcConfig *config)
598
0
{
599
0
    FcConfig *cfg;
600
601
0
    if (config) {
602
0
  if (!config->fonts[FcSetSystem])
603
0
      if (!FcConfigBuildFonts (config))
604
0
    return FcFalse;
605
0
  FcRefInc (&config->ref);
606
0
    }
607
608
0
    lock_config();
609
0
retry:
610
0
    cfg = fc_atomic_ptr_get (&_fcConfig);
611
612
0
    if (config == cfg) {
613
0
  unlock_config();
614
0
  if (config)
615
0
      FcConfigDestroy (config);
616
0
  return FcTrue;
617
0
    }
618
619
0
    if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
620
0
  goto retry;
621
0
    unlock_config();
622
0
    if (cfg)
623
0
  FcConfigDestroy (cfg);
624
625
0
    return FcTrue;
626
0
}
627
628
FcConfig *
629
FcConfigGetCurrent (void)
630
237
{
631
237
    return FcConfigEnsure();
632
237
}
633
634
FcBool
635
FcConfigAddConfigDir (FcConfig      *config,
636
                      const FcChar8 *d)
637
0
{
638
0
    return FcStrSetAddFilename (config->configDirs, d);
639
0
}
640
641
FcStrList *
642
FcConfigGetConfigDirs (FcConfig *config)
643
0
{
644
0
    FcStrList *ret;
645
646
0
    config = FcConfigReference (config);
647
0
    if (!config)
648
0
  return NULL;
649
0
    ret = FcStrListCreate (config->configDirs);
650
0
    FcConfigDestroy (config);
651
652
0
    return ret;
653
0
}
654
655
FcBool
656
FcConfigAddFontDir (FcConfig      *config,
657
                    const FcChar8 *d,
658
                    const FcChar8 *m,
659
                    const FcChar8 *salt)
660
55
{
661
55
    if (FcDebug() & FC_DBG_CACHE) {
662
0
  if (m) {
663
0
      printf ("%s -> %s%s%s%s\n", d, m, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
664
0
  } else if (salt) {
665
0
      printf ("%s%s%s%s\n", d, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
666
0
  }
667
0
    }
668
55
    return FcStrSetAddFilenamePairWithSalt (config->fontDirs, d, m, salt);
669
55
}
670
671
FcBool
672
FcConfigResetFontDirs (FcConfig *config)
673
0
{
674
0
    if (FcDebug() & FC_DBG_CACHE) {
675
0
  printf ("Reset font directories!\n");
676
0
    }
677
0
    return FcStrSetDeleteAll (config->fontDirs);
678
0
}
679
680
FcStrList *
681
FcConfigGetFontDirs (FcConfig *config)
682
132
{
683
132
    FcStrList *ret;
684
685
132
    config = FcConfigReference (config);
686
132
    if (!config)
687
0
  return NULL;
688
132
    ret = FcStrListCreate (config->fontDirs);
689
132
    FcConfigDestroy (config);
690
691
132
    return ret;
692
132
}
693
694
FcStrList *
695
FcConfigGetAppFonts (FcConfig *config)
696
0
{
697
0
    FcStrList *ret;
698
699
0
    config = FcConfigReference (config);
700
0
    if (!config)
701
0
  return NULL;
702
0
    ret = FcStrListCreate (config->appFonts);
703
0
    FcConfigDestroy (config);
704
705
0
    return ret;
706
0
}
707
708
static FcBool
709
FcConfigPathStartsWith (const FcChar8 *path,
710
                        const FcChar8 *start)
711
396
{
712
396
    int len = strlen ((char *)start);
713
714
396
    if (strncmp ((char *)path, (char *)start, len) != 0)
715
264
  return FcFalse;
716
717
132
    switch (path[len]) {
718
44
    case '\0':
719
132
    case FC_DIR_SEPARATOR:
720
132
  return FcTrue;
721
0
    default:
722
0
  return FcFalse;
723
132
    }
724
132
}
725
726
FcChar8 *
727
FcConfigMapFontPath (FcConfig      *config,
728
                     const FcChar8 *path)
729
66
{
730
66
    FcStrList     *list;
731
66
    FcChar8       *dir;
732
66
    const FcChar8 *map, *rpath;
733
66
    FcChar8       *retval;
734
735
66
    list = FcConfigGetFontDirs (config);
736
66
    if (!list)
737
0
  return 0;
738
198
    while ((dir = FcStrListNext (list)))
739
198
  if (FcConfigPathStartsWith (path, dir))
740
66
      break;
741
66
    FcStrListDone (list);
742
66
    if (!dir)
743
0
  return 0;
744
66
    map = FcStrTripleSecond (dir);
745
66
    if (!map)
746
66
  return 0;
747
0
    rpath = path + strlen ((char *)dir);
748
0
    while (*rpath == '/')
749
0
  rpath++;
750
0
    retval = FcStrBuildFilename (map, rpath, NULL);
751
0
    if (retval) {
752
0
  size_t len = strlen ((const char *)retval);
753
0
  while (len > 0 && retval[len - 1] == '/')
754
0
      len--;
755
  /* trim the last slash */
756
0
  retval[len] = 0;
757
0
    }
758
0
    return retval;
759
66
}
760
761
const FcChar8 *
762
FcConfigMapSalt (FcConfig      *config,
763
                 const FcChar8 *path)
764
66
{
765
66
    FcStrList *list;
766
66
    FcChar8   *dir;
767
768
66
    list = FcConfigGetFontDirs (config);
769
66
    if (!list)
770
0
  return NULL;
771
198
    while ((dir = FcStrListNext (list)))
772
198
  if (FcConfigPathStartsWith (path, dir))
773
66
      break;
774
66
    FcStrListDone (list);
775
66
    if (!dir)
776
0
  return NULL;
777
778
66
    return FcStrTripleThird (dir);
779
66
}
780
781
FcBool
782
FcConfigAddCacheDir (FcConfig      *config,
783
                     const FcChar8 *d)
784
22
{
785
22
    return FcStrSetAddFilename (config->cacheDirs, d);
786
22
}
787
788
FcStrList *
789
FcConfigGetCacheDirs (FcConfig *config)
790
0
{
791
0
    FcStrList *ret;
792
793
0
    config = FcConfigReference (config);
794
0
    if (!config)
795
0
  return NULL;
796
0
    ret = FcStrListCreate (config->cacheDirs);
797
0
    FcConfigDestroy (config);
798
799
0
    return ret;
800
0
}
801
802
FcBool
803
FcConfigAddConfigFile (FcConfig      *config,
804
                       const FcChar8 *f)
805
0
{
806
0
    FcBool   ret;
807
0
    FcChar8 *file = FcConfigGetFilename (config, f);
808
809
0
    if (!file)
810
0
  return FcFalse;
811
812
0
    ret = FcStrSetAdd (config->configFiles, file);
813
0
    FcStrFree (file);
814
0
    return ret;
815
0
}
816
817
FcStrList *
818
FcConfigGetConfigFiles (FcConfig *config)
819
0
{
820
0
    FcStrList *ret;
821
822
0
    config = FcConfigReference (config);
823
0
    if (!config)
824
0
  return NULL;
825
0
    ret = FcStrListCreate (config->configFiles);
826
0
    FcConfigDestroy (config);
827
828
0
    return ret;
829
0
}
830
831
FcChar8 *
832
FcConfigGetCache (FcConfig *config FC_UNUSED)
833
0
{
834
0
    return NULL;
835
0
}
836
837
/*
838
 * Note: this function is not thread-safe as documented in doc/fcconfig.fncs.
839
 * It returns an internal pointer to config->fonts[set], which cannot be safely
840
 * accessed without external synchronization if other threads may concurrently
841
 * modify font sets (e.g. via FcConfigAppFontAdd* or FcConfigAppFontClear).
842
 * Acquiring fonts_lock only during the pointer read would not protect callers
843
 * after this function returns.
844
 */
845
FcFontSet *
846
FcConfigGetFonts (FcConfig *config,
847
                  FcSetName set)
848
0
{
849
0
    if (!config) {
850
0
  config = FcConfigGetCurrent();
851
0
  if (!config)
852
0
      return 0;
853
0
    }
854
0
    return config->fonts[set];
855
0
}
856
857
void
858
FcConfigSetFonts (FcConfig  *config,
859
                  FcFontSet *fonts,
860
                  FcSetName  set)
861
0
{
862
0
    FcRwLockWriteLock (&config->fonts_lock);
863
0
    FcConfigSetFontsLocked (config, fonts, set);
864
0
    FcRwLockUnlockWrite (&config->fonts_lock);
865
0
}
866
867
FcConfig *
868
FcConfigSetFontSetFilter (FcConfig           *config,
869
                          FcFilterFontSetFunc filter_func,
870
                          FcDestroyFunc       destroy_data_func,
871
                          void               *user_data)
872
0
{
873
0
    FcBool rebuild = FcFalse;
874
875
0
    if (!config) {
876
  /* Do not use FcConfigEnsure() here for optimization */
877
0
    retry:
878
0
  config = fc_atomic_ptr_get (&_fcConfig);
879
0
  if (!config)
880
0
      config = FcConfigCreate();
881
0
  else
882
0
      rebuild = FcTrue;
883
0
    } else
884
0
  rebuild = FcTrue;
885
0
    if (config->filter_data == user_data &&
886
0
        config->filter_func == filter_func) {
887
  /* No need to update */
888
0
  rebuild = FcFalse;
889
0
    } else {
890
0
  if (config->filter_data && config->destroy_data_func) {
891
0
      config->destroy_data_func (config->filter_data);
892
0
  }
893
0
  config->filter_func = filter_func;
894
0
  config->destroy_data_func = destroy_data_func;
895
0
  config->filter_data = user_data;
896
0
    }
897
898
0
    if (rebuild) {
899
  /* Rebuild FontSet */
900
0
  FcConfigBuildFonts (config);
901
0
    } else {
902
  /* Initialize FcConfig with regular procedure */
903
0
  config = FcInitLoadOwnConfigAndFonts (config);
904
905
0
  if (!config || !fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
906
0
      if (config)
907
0
    FcConfigDestroy (config);
908
0
      goto retry;
909
0
  }
910
0
    }
911
912
0
    return config;
913
0
}
914
915
FcBlanks *
916
FcBlanksCreate (void)
917
0
{
918
    /* Deprecated. */
919
0
    return NULL;
920
0
}
921
922
void
923
FcBlanksDestroy (FcBlanks *b FC_UNUSED)
924
0
{
925
    /* Deprecated. */
926
0
}
927
928
FcBool
929
FcBlanksAdd (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
930
0
{
931
    /* Deprecated. */
932
0
    return FcFalse;
933
0
}
934
935
FcBool
936
FcBlanksIsMember (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
937
0
{
938
    /* Deprecated. */
939
0
    return FcFalse;
940
0
}
941
942
FcBlanks *
943
FcConfigGetBlanks (FcConfig *config FC_UNUSED)
944
0
{
945
    /* Deprecated. */
946
0
    return NULL;
947
0
}
948
949
FcBool
950
FcConfigAddBlank (FcConfig *config FC_UNUSED,
951
                  FcChar32  blank FC_UNUSED)
952
0
{
953
    /* Deprecated. */
954
0
    return FcFalse;
955
0
}
956
957
int
958
FcConfigGetRescanInterval (FcConfig *config)
959
0
{
960
0
    int ret;
961
962
0
    config = FcConfigReference (config);
963
0
    if (!config)
964
0
  return 0;
965
0
    ret = config->rescanInterval;
966
0
    FcConfigDestroy (config);
967
968
0
    return ret;
969
0
}
970
971
FcBool
972
FcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
973
0
{
974
0
    config = FcConfigReference (config);
975
0
    if (!config)
976
0
  return FcFalse;
977
0
    config->rescanInterval = rescanInterval;
978
0
    FcConfigDestroy (config);
979
980
0
    return FcTrue;
981
0
}
982
983
/*
984
 * A couple of typos escaped into the library
985
 */
986
int
987
FcConfigGetRescanInverval (FcConfig *config)
988
0
{
989
0
    return FcConfigGetRescanInterval (config);
990
0
}
991
992
FcBool
993
FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
994
0
{
995
0
    return FcConfigSetRescanInterval (config, rescanInterval);
996
0
}
997
998
FcBool
999
FcConfigAddRule (FcConfig   *config,
1000
                 FcRule     *rule,
1001
                 FcMatchKind kind)
1002
0
{
1003
    /* deprecated */
1004
0
    return FcFalse;
1005
0
}
1006
1007
static FcValue
1008
FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
1009
0
{
1010
0
    switch (v.type) {
1011
0
    case FcTypeInteger:
1012
0
  v.type = FcTypeDouble;
1013
0
  v.u.d = (double)v.u.i;
1014
  /* Fallthrough */
1015
0
    case FcTypeDouble:
1016
0
  if (u.type == FcTypeRange && buf) {
1017
0
      v.u.r = FcRangePromote (v.u.d, buf);
1018
0
      v.type = FcTypeRange;
1019
0
  }
1020
0
  break;
1021
0
    case FcTypeVoid:
1022
0
  if (u.type == FcTypeMatrix) {
1023
0
      v.u.m = &FcIdentityMatrix;
1024
0
      v.type = FcTypeMatrix;
1025
0
  } else if (u.type == FcTypeLangSet && buf) {
1026
0
      v.u.l = FcLangSetPromote (NULL, buf);
1027
0
      v.type = FcTypeLangSet;
1028
0
  } else if (u.type == FcTypeCharSet && buf) {
1029
0
      v.u.c = FcCharSetPromote (buf);
1030
0
      v.type = FcTypeCharSet;
1031
0
  }
1032
0
  break;
1033
0
    case FcTypeString:
1034
0
  if (u.type == FcTypeLangSet && buf) {
1035
0
      v.u.l = FcLangSetPromote (v.u.s, buf);
1036
0
      v.type = FcTypeLangSet;
1037
0
  }
1038
0
  break;
1039
0
    default:
1040
0
  break;
1041
0
    }
1042
0
    return v;
1043
0
}
1044
1045
FcBool
1046
FcConfigCompareValue (const FcValue *left_o,
1047
                      unsigned int   op_,
1048
                      const FcValue *right_o)
1049
0
{
1050
0
    FcValue                left;
1051
0
    FcValue                right;
1052
0
    FcBool                 ret = FcFalse;
1053
0
    FcOp                   op = FC_OP_GET_OP (op_);
1054
0
    int                    flags = FC_OP_GET_FLAGS (op_);
1055
0
    FcValuePromotionBuffer buf1, buf2;
1056
1057
0
    if (left_o->type != right_o->type) {
1058
0
  left = FcValueCanonicalize (left_o);
1059
0
  right = FcValueCanonicalize (right_o);
1060
0
  left = FcConfigPromote (left, right, &buf1);
1061
0
  right = FcConfigPromote (right, left, &buf2);
1062
0
  left_o = &left;
1063
0
  right_o = &right;
1064
0
  if (left_o->type != right_o->type) {
1065
0
      if (op == FcOpNotEqual || op == FcOpNotContains)
1066
0
    ret = FcTrue;
1067
0
      return ret;
1068
0
  }
1069
0
    }
1070
0
    switch (left_o->type) {
1071
0
    case FcTypeUnknown:
1072
0
  break; /* No way to guess how to compare for this object */
1073
0
    case FcTypeInteger: {
1074
0
  int l = left_o->u.i;
1075
0
  int r = right_o->u.i;
1076
0
  switch ((int)op) {
1077
0
  case FcOpEqual:
1078
0
  case FcOpContains:
1079
0
  case FcOpListing:
1080
0
      ret = l == r;
1081
0
      break;
1082
0
  case FcOpNotEqual:
1083
0
  case FcOpNotContains:
1084
0
      ret = l != r;
1085
0
      break;
1086
0
  case FcOpLess:
1087
0
      ret = l < r;
1088
0
      break;
1089
0
  case FcOpLessEqual:
1090
0
      ret = l <= r;
1091
0
      break;
1092
0
  case FcOpMore:
1093
0
      ret = l > r;
1094
0
      break;
1095
0
  case FcOpMoreEqual:
1096
0
      ret = l >= r;
1097
0
      break;
1098
0
  default:
1099
0
      break;
1100
0
  }
1101
0
  break;
1102
0
    }
1103
0
    case FcTypeDouble: {
1104
0
  double l = left_o->u.d;
1105
0
  double r = right_o->u.d;
1106
0
  switch ((int)op) {
1107
0
  case FcOpEqual:
1108
0
  case FcOpContains:
1109
0
  case FcOpListing:
1110
0
      ret = l == r;
1111
0
      break;
1112
0
  case FcOpNotEqual:
1113
0
  case FcOpNotContains:
1114
0
      ret = l != r;
1115
0
      break;
1116
0
  case FcOpLess:
1117
0
      ret = l < r;
1118
0
      break;
1119
0
  case FcOpLessEqual:
1120
0
      ret = l <= r;
1121
0
      break;
1122
0
  case FcOpMore:
1123
0
      ret = l > r;
1124
0
      break;
1125
0
  case FcOpMoreEqual:
1126
0
      ret = l >= r;
1127
0
      break;
1128
0
  default:
1129
0
      break;
1130
0
  }
1131
0
  break;
1132
0
    }
1133
0
    case FcTypeBool: {
1134
0
  FcBool l = left_o->u.b;
1135
0
  FcBool r = right_o->u.b;
1136
0
  switch ((int)op) {
1137
0
  case FcOpEqual:
1138
0
      ret = l == r;
1139
0
      break;
1140
0
  case FcOpContains:
1141
0
  case FcOpListing:
1142
0
      ret = l == r || l >= FcDontCare;
1143
0
      break;
1144
0
  case FcOpNotEqual:
1145
0
      ret = l != r;
1146
0
      break;
1147
0
  case FcOpNotContains:
1148
0
      ret = !(l == r || l >= FcDontCare);
1149
0
      break;
1150
0
  case FcOpLess:
1151
0
      ret = l != r && r >= FcDontCare;
1152
0
      break;
1153
0
  case FcOpLessEqual:
1154
0
      ret = l == r || r >= FcDontCare;
1155
0
      break;
1156
0
  case FcOpMore:
1157
0
      ret = l != r && l >= FcDontCare;
1158
0
      break;
1159
0
  case FcOpMoreEqual:
1160
0
      ret = l == r || l >= FcDontCare;
1161
0
      break;
1162
0
  default:
1163
0
      break;
1164
0
  }
1165
0
  break;
1166
0
    }
1167
0
    case FcTypeString: {
1168
0
  const FcChar8 *l = FcValueString (left_o);
1169
0
  const FcChar8 *r = FcValueString (right_o);
1170
0
  switch ((int)op) {
1171
0
  case FcOpEqual:
1172
0
  case FcOpListing:
1173
0
      if (flags & FcOpFlagIgnoreBlanks)
1174
0
    ret = FcStrCmpIgnoreBlanksAndCase (l, r) == 0;
1175
0
      else
1176
0
    ret = FcStrCmpIgnoreCase (l, r) == 0;
1177
0
      break;
1178
0
  case FcOpContains:
1179
0
      ret = FcStrStrIgnoreCase (l, r) != 0;
1180
0
      break;
1181
0
  case FcOpNotEqual:
1182
0
      if (flags & FcOpFlagIgnoreBlanks)
1183
0
    ret = FcStrCmpIgnoreBlanksAndCase (l, r) != 0;
1184
0
      else
1185
0
    ret = FcStrCmpIgnoreCase (l, r) != 0;
1186
0
      break;
1187
0
  case FcOpNotContains:
1188
0
      ret = FcStrStrIgnoreCase (l, r) == 0;
1189
0
      break;
1190
0
  default:
1191
0
      break;
1192
0
  }
1193
0
  break;
1194
0
    }
1195
0
    case FcTypeMatrix: {
1196
0
  switch ((int)op) {
1197
0
  case FcOpEqual:
1198
0
  case FcOpContains:
1199
0
  case FcOpListing:
1200
0
      ret = FcMatrixEqual (left_o->u.m, right_o->u.m);
1201
0
      break;
1202
0
  case FcOpNotEqual:
1203
0
  case FcOpNotContains:
1204
0
      ret = !FcMatrixEqual (left_o->u.m, right_o->u.m);
1205
0
      break;
1206
0
  default:
1207
0
      break;
1208
0
  }
1209
0
  break;
1210
0
    }
1211
0
    case FcTypeCharSet: {
1212
0
  const FcCharSet *l = FcValueCharSet (left_o);
1213
0
  const FcCharSet *r = FcValueCharSet (right_o);
1214
0
  switch ((int)op) {
1215
0
  case FcOpContains:
1216
0
  case FcOpListing:
1217
      /* left contains right if right is a subset of left */
1218
0
      ret = FcCharSetIsSubset (r, l);
1219
0
      break;
1220
0
  case FcOpNotContains:
1221
      /* left contains right if right is a subset of left */
1222
0
      ret = !FcCharSetIsSubset (r, l);
1223
0
      break;
1224
0
  case FcOpEqual:
1225
0
      ret = FcCharSetEqual (l, r);
1226
0
      break;
1227
0
  case FcOpNotEqual:
1228
0
      ret = !FcCharSetEqual (l, r);
1229
0
      break;
1230
0
  default:
1231
0
      break;
1232
0
  }
1233
0
  break;
1234
0
    }
1235
0
    case FcTypeLangSet: {
1236
0
  const FcLangSet *l = FcValueLangSet (left_o);
1237
0
  const FcLangSet *r = FcValueLangSet (right_o);
1238
0
  switch ((int)op) {
1239
0
  case FcOpContains:
1240
0
  case FcOpListing:
1241
0
      ret = FcLangSetContains (l, r);
1242
0
      break;
1243
0
  case FcOpNotContains:
1244
0
      ret = !FcLangSetContains (l, r);
1245
0
      break;
1246
0
  case FcOpEqual:
1247
0
      ret = FcLangSetEqual (l, r);
1248
0
      break;
1249
0
  case FcOpNotEqual:
1250
0
      ret = !FcLangSetEqual (l, r);
1251
0
      break;
1252
0
  default:
1253
0
      break;
1254
0
  }
1255
0
  break;
1256
0
    }
1257
0
    case FcTypeVoid:
1258
0
  switch ((int)op) {
1259
0
  case FcOpEqual:
1260
0
  case FcOpContains:
1261
0
  case FcOpListing:
1262
0
      ret = FcTrue;
1263
0
      break;
1264
0
  default:
1265
0
      break;
1266
0
  }
1267
0
  break;
1268
0
    case FcTypeFTFace:
1269
0
  switch ((int)op) {
1270
0
  case FcOpEqual:
1271
0
  case FcOpContains:
1272
0
  case FcOpListing:
1273
0
      ret = left_o->u.f == right_o->u.f;
1274
0
      break;
1275
0
  case FcOpNotEqual:
1276
0
  case FcOpNotContains:
1277
0
      ret = left_o->u.f != right_o->u.f;
1278
0
      break;
1279
0
  default:
1280
0
      break;
1281
0
  }
1282
0
  break;
1283
0
    case FcTypeRange: {
1284
0
  const FcRange *l = FcValueRange (left_o);
1285
0
  const FcRange *r = FcValueRange (right_o);
1286
0
  ret = FcRangeCompare (op, l, r);
1287
0
  break;
1288
0
    }
1289
0
    }
1290
0
    return ret;
1291
0
}
1292
1293
0
#define _FcDoubleFloor(d) ((int)(d))
1294
0
#define _FcDoubleCeil(d)  ((double)(int)(d) == (d) ? (int)(d) : (int)((d) + 1))
1295
0
#define FcDoubleFloor(d)  ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleCeil (-(d)))
1296
0
#define FcDoubleCeil(d)   ((d) >= 0 ? _FcDoubleCeil (d) : -_FcDoubleFloor (-(d)))
1297
0
#define FcDoubleRound(d)  FcDoubleFloor ((d) + 0.5)
1298
0
#define FcDoubleTrunc(d)  ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
1299
1300
static FcValue
1301
FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcObject object, FcMatchKind kind, FcExpr *e)
1302
0
{
1303
0
    FcValue                v, vl, vr, vle, vre;
1304
0
    FcMatrix              *m;
1305
0
    FcChar8               *str;
1306
0
    FcOp                   op = FC_OP_GET_OP (e->op);
1307
0
    FcValuePromotionBuffer buf1, buf2;
1308
1309
0
    switch ((int)op) {
1310
0
    case FcOpInteger:
1311
0
  v.type = FcTypeInteger;
1312
0
  v.u.i = e->u.ival;
1313
0
  break;
1314
0
    case FcOpDouble:
1315
0
  v.type = FcTypeDouble;
1316
0
  v.u.d = e->u.dval;
1317
0
  break;
1318
0
    case FcOpString:
1319
0
  v.type = FcTypeString;
1320
0
  v.u.s = e->u.sval;
1321
0
  v = FcValueSave (v);
1322
0
  break;
1323
0
    case FcOpMatrix: {
1324
0
  FcMatrix m;
1325
0
  FcValue  xx, xy, yx, yy;
1326
0
  v.type = FcTypeMatrix;
1327
0
  xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, object, kind, e->u.mexpr->xx), v, NULL);
1328
0
  xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, object, kind, e->u.mexpr->xy), v, NULL);
1329
0
  yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, object, kind, e->u.mexpr->yx), v, NULL);
1330
0
  yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, object, kind, e->u.mexpr->yy), v, NULL);
1331
0
  if (xx.type == FcTypeDouble && xy.type == FcTypeDouble &&
1332
0
      yx.type == FcTypeDouble && yy.type == FcTypeDouble) {
1333
0
      m.xx = xx.u.d;
1334
0
      m.xy = xy.u.d;
1335
0
      m.yx = yx.u.d;
1336
0
      m.yy = yy.u.d;
1337
0
      v.u.m = &m;
1338
0
  } else
1339
0
      v.type = FcTypeVoid;
1340
0
  v = FcValueSave (v);
1341
0
    } break;
1342
0
    case FcOpCharSet:
1343
0
  v.type = FcTypeCharSet;
1344
0
  v.u.c = e->u.cval;
1345
0
  v = FcValueSave (v);
1346
0
  break;
1347
0
    case FcOpLangSet:
1348
0
  v.type = FcTypeLangSet;
1349
0
  v.u.l = e->u.lval;
1350
0
  v = FcValueSave (v);
1351
0
  break;
1352
0
    case FcOpRange:
1353
0
  v.type = FcTypeRange;
1354
0
  v.u.r = e->u.rval;
1355
0
  v = FcValueSave (v);
1356
0
  break;
1357
0
    case FcOpBool:
1358
0
  v.type = FcTypeBool;
1359
0
  v.u.b = e->u.bval;
1360
0
  break;
1361
0
    case FcOpField:
1362
0
  if (kind == FcMatchFont && e->u.name.kind == FcMatchPattern) {
1363
0
      if (FcResultMatch != FcPatternObjectGet (p_pat, e->u.name.object, 0, &v))
1364
0
    v.type = FcTypeVoid;
1365
0
  } else if (kind == FcMatchPattern && e->u.name.kind == FcMatchFont) {
1366
0
      fprintf (stderr,
1367
0
               "Fontconfig warning: <name> tag has target=\"font\" in a <match target=\"pattern\">.\n");
1368
0
      v.type = FcTypeVoid;
1369
0
  } else {
1370
0
      if (FcResultMatch != FcPatternObjectGet (p, e->u.name.object, 0, &v))
1371
0
    v.type = FcTypeVoid;
1372
0
  }
1373
0
  v = FcValueSave (v);
1374
0
  break;
1375
0
    case FcOpConst:
1376
0
  if (FcNameConstantWithObjectCheck (e->u.constant, object, &v.u.i))
1377
0
      v.type = FcTypeInteger;
1378
0
  else
1379
0
      v.type = FcTypeVoid;
1380
0
  break;
1381
0
    case FcOpQuest:
1382
0
  vl = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1383
0
  if (vl.type == FcTypeBool) {
1384
0
      if (vl.u.b)
1385
0
    v = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.right->u.tree.left);
1386
0
      else
1387
0
    v = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.right->u.tree.right);
1388
0
  } else
1389
0
      v.type = FcTypeVoid;
1390
0
  FcValueDestroy (vl);
1391
0
  break;
1392
0
    case FcOpEqual:
1393
0
    case FcOpNotEqual:
1394
0
    case FcOpLess:
1395
0
    case FcOpLessEqual:
1396
0
    case FcOpMore:
1397
0
    case FcOpMoreEqual:
1398
0
    case FcOpContains:
1399
0
    case FcOpNotContains:
1400
0
    case FcOpListing:
1401
0
  vl = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1402
0
  vr = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.right);
1403
0
  v.type = FcTypeBool;
1404
0
  v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
1405
0
  FcValueDestroy (vl);
1406
0
  FcValueDestroy (vr);
1407
0
  break;
1408
0
    case FcOpOr:
1409
0
    case FcOpAnd:
1410
0
    case FcOpPlus:
1411
0
    case FcOpMinus:
1412
0
    case FcOpTimes:
1413
0
    case FcOpDivide:
1414
0
  vl = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1415
0
  vr = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.right);
1416
0
  vle = FcConfigPromote (vl, vr, &buf1);
1417
0
  vre = FcConfigPromote (vr, vle, &buf2);
1418
0
  if (vle.type == vre.type) {
1419
0
      switch ((int)vle.type) {
1420
0
      case FcTypeDouble:
1421
0
    switch ((int)op) {
1422
0
    case FcOpPlus:
1423
0
        v.type = FcTypeDouble;
1424
0
        v.u.d = vle.u.d + vre.u.d;
1425
0
        break;
1426
0
    case FcOpMinus:
1427
0
        v.type = FcTypeDouble;
1428
0
        v.u.d = vle.u.d - vre.u.d;
1429
0
        break;
1430
0
    case FcOpTimes:
1431
0
        v.type = FcTypeDouble;
1432
0
        v.u.d = vle.u.d * vre.u.d;
1433
0
        break;
1434
0
    case FcOpDivide:
1435
0
        v.type = FcTypeDouble;
1436
0
        v.u.d = vle.u.d / vre.u.d;
1437
0
        break;
1438
0
    default:
1439
0
        v.type = FcTypeVoid;
1440
0
        break;
1441
0
    }
1442
0
    if (v.type == FcTypeDouble &&
1443
0
        v.u.d == (double)(int)v.u.d) {
1444
0
        v.type = FcTypeInteger;
1445
0
        v.u.i = (int)v.u.d;
1446
0
    }
1447
0
    break;
1448
0
      case FcTypeBool:
1449
0
    switch ((int)op) {
1450
0
    case FcOpOr:
1451
0
        v.type = FcTypeBool;
1452
0
        v.u.b = vle.u.b || vre.u.b;
1453
0
        break;
1454
0
    case FcOpAnd:
1455
0
        v.type = FcTypeBool;
1456
0
        v.u.b = vle.u.b && vre.u.b;
1457
0
        break;
1458
0
    default:
1459
0
        v.type = FcTypeVoid;
1460
0
        break;
1461
0
    }
1462
0
    break;
1463
0
      case FcTypeString:
1464
0
    switch ((int)op) {
1465
0
    case FcOpPlus:
1466
0
        v.type = FcTypeString;
1467
0
        str = FcStrPlus (vle.u.s, vre.u.s);
1468
0
        v.u.s = FcStrCopy (str);
1469
0
        FcStrFree (str);
1470
1471
0
        if (!v.u.s)
1472
0
      v.type = FcTypeVoid;
1473
0
        break;
1474
0
    default:
1475
0
        v.type = FcTypeVoid;
1476
0
        break;
1477
0
    }
1478
0
    break;
1479
0
      case FcTypeMatrix:
1480
0
    switch ((int)op) {
1481
0
    case FcOpTimes:
1482
0
        v.type = FcTypeMatrix;
1483
0
        m = malloc (sizeof (FcMatrix));
1484
0
        if (m) {
1485
0
      FcMatrixMultiply (m, vle.u.m, vre.u.m);
1486
0
      v.u.m = m;
1487
0
        } else {
1488
0
      v.type = FcTypeVoid;
1489
0
        }
1490
0
        break;
1491
0
    default:
1492
0
        v.type = FcTypeVoid;
1493
0
        break;
1494
0
    }
1495
0
    break;
1496
0
      case FcTypeCharSet:
1497
0
    switch ((int)op) {
1498
0
    case FcOpPlus:
1499
0
        v.type = FcTypeCharSet;
1500
0
        v.u.c = FcCharSetUnion (vle.u.c, vre.u.c);
1501
0
        if (!v.u.c)
1502
0
      v.type = FcTypeVoid;
1503
0
        break;
1504
0
    case FcOpMinus:
1505
0
        v.type = FcTypeCharSet;
1506
0
        v.u.c = FcCharSetSubtract (vle.u.c, vre.u.c);
1507
0
        if (!v.u.c)
1508
0
      v.type = FcTypeVoid;
1509
0
        break;
1510
0
    default:
1511
0
        v.type = FcTypeVoid;
1512
0
        break;
1513
0
    }
1514
0
    break;
1515
0
      case FcTypeLangSet:
1516
0
    switch ((int)op) {
1517
0
    case FcOpPlus:
1518
0
        v.type = FcTypeLangSet;
1519
0
        v.u.l = FcLangSetUnion (vle.u.l, vre.u.l);
1520
0
        if (!v.u.l)
1521
0
      v.type = FcTypeVoid;
1522
0
        break;
1523
0
    case FcOpMinus:
1524
0
        v.type = FcTypeLangSet;
1525
0
        v.u.l = FcLangSetSubtract (vle.u.l, vre.u.l);
1526
0
        if (!v.u.l)
1527
0
      v.type = FcTypeVoid;
1528
0
        break;
1529
0
    default:
1530
0
        v.type = FcTypeVoid;
1531
0
        break;
1532
0
    }
1533
0
    break;
1534
0
      default:
1535
0
    v.type = FcTypeVoid;
1536
0
    break;
1537
0
      }
1538
0
  } else
1539
0
      v.type = FcTypeVoid;
1540
0
  FcValueDestroy (vl);
1541
0
  FcValueDestroy (vr);
1542
0
  break;
1543
0
    case FcOpNot:
1544
0
  vl = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1545
0
  switch ((int)vl.type) {
1546
0
  case FcTypeBool:
1547
0
      v.type = FcTypeBool;
1548
0
      v.u.b = !vl.u.b;
1549
0
      break;
1550
0
  default:
1551
0
      v.type = FcTypeVoid;
1552
0
      break;
1553
0
  }
1554
0
  FcValueDestroy (vl);
1555
0
  break;
1556
0
    case FcOpFloor:
1557
0
  vl = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1558
0
  switch ((int)vl.type) {
1559
0
  case FcTypeInteger:
1560
0
      v = vl;
1561
0
      break;
1562
0
  case FcTypeDouble:
1563
0
      v.type = FcTypeInteger;
1564
0
      v.u.i = FcDoubleFloor (vl.u.d);
1565
0
      break;
1566
0
  default:
1567
0
      v.type = FcTypeVoid;
1568
0
      break;
1569
0
  }
1570
0
  FcValueDestroy (vl);
1571
0
  break;
1572
0
    case FcOpCeil:
1573
0
  vl = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1574
0
  switch ((int)vl.type) {
1575
0
  case FcTypeInteger:
1576
0
      v = vl;
1577
0
      break;
1578
0
  case FcTypeDouble:
1579
0
      v.type = FcTypeInteger;
1580
0
      v.u.i = FcDoubleCeil (vl.u.d);
1581
0
      break;
1582
0
  default:
1583
0
      v.type = FcTypeVoid;
1584
0
      break;
1585
0
  }
1586
0
  FcValueDestroy (vl);
1587
0
  break;
1588
0
    case FcOpRound:
1589
0
  vl = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1590
0
  switch ((int)vl.type) {
1591
0
  case FcTypeInteger:
1592
0
      v = vl;
1593
0
      break;
1594
0
  case FcTypeDouble:
1595
0
      v.type = FcTypeInteger;
1596
0
      v.u.i = FcDoubleRound (vl.u.d);
1597
0
      break;
1598
0
  default:
1599
0
      v.type = FcTypeVoid;
1600
0
      break;
1601
0
  }
1602
0
  FcValueDestroy (vl);
1603
0
  break;
1604
0
    case FcOpTrunc:
1605
0
  vl = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1606
0
  switch ((int)vl.type) {
1607
0
  case FcTypeInteger:
1608
0
      v = vl;
1609
0
      break;
1610
0
  case FcTypeDouble:
1611
0
      v.type = FcTypeInteger;
1612
0
      v.u.i = FcDoubleTrunc (vl.u.d);
1613
0
      break;
1614
0
  default:
1615
0
      v.type = FcTypeVoid;
1616
0
      break;
1617
0
  }
1618
0
  FcValueDestroy (vl);
1619
0
  break;
1620
0
    default:
1621
0
  v.type = FcTypeVoid;
1622
0
  break;
1623
0
    }
1624
0
    return v;
1625
0
}
1626
1627
/* The bulk of the time in FcConfigSubstitute is spent walking
1628
 * lists of family names. We speed this up with a hash table.
1629
 * Since we need to take the ignore-blanks option into account,
1630
 * we use two separate hash tables.
1631
 */
1632
typedef struct
1633
{
1634
    int count;
1635
} FamilyTableEntry;
1636
1637
typedef struct
1638
{
1639
    FcHashTable *family_blank_hash;
1640
    FcHashTable *family_hash;
1641
} FamilyTable;
1642
1643
static FcBool
1644
FamilyTableLookup (FamilyTable   *table,
1645
                   FcOp           _op,
1646
                   const FcChar8 *s)
1647
0
{
1648
0
    FamilyTableEntry *fe;
1649
0
    int               flags = FC_OP_GET_FLAGS (_op);
1650
0
    FcHashTable      *hash;
1651
1652
0
    if (flags & FcOpFlagIgnoreBlanks)
1653
0
  hash = table->family_blank_hash;
1654
0
    else
1655
0
  hash = table->family_hash;
1656
1657
0
    return FcHashTableFind (hash, (const void *)s, (void **)&fe);
1658
0
}
1659
1660
static FcBool
1661
FamilyTableAdd (FamilyTable   *table,
1662
                FcValueListPtr values)
1663
107k
{
1664
107k
    FcValueListPtr ll;
1665
214k
    for (ll = values; ll; ll = FcValueListNext (ll)) {
1666
107k
  const FcChar8    *s = FcValueString (&ll->value);
1667
107k
  FamilyTableEntry *fe;
1668
1669
107k
  if (!FcHashTableFind (table->family_hash, (const void *)s, (void **)&fe)) {
1670
107k
      fe = malloc (sizeof (FamilyTableEntry));
1671
107k
      if (!fe)
1672
0
    return FcFalse;
1673
107k
      fe->count = 0;
1674
107k
      FcHashTableAdd (table->family_hash, (void *)s, fe);
1675
107k
  }
1676
107k
  fe->count++;
1677
1678
107k
  if (!FcHashTableFind (table->family_blank_hash, (const void *)s, (void **)&fe)) {
1679
107k
      fe = malloc (sizeof (FamilyTableEntry));
1680
107k
      if (!fe)
1681
0
    return FcFalse;
1682
107k
      fe->count = 0;
1683
107k
      FcHashTableAdd (table->family_blank_hash, (void *)s, fe);
1684
107k
  }
1685
107k
  fe->count++;
1686
107k
    }
1687
107k
    return FcTrue;
1688
107k
}
1689
1690
static void
1691
FamilyTableDel (FamilyTable   *table,
1692
                const FcChar8 *s)
1693
0
{
1694
0
    FamilyTableEntry *fe;
1695
1696
0
    if (FcHashTableFind (table->family_hash, (void *)s, (void **)&fe)) {
1697
0
  fe->count--;
1698
0
  if (fe->count == 0)
1699
0
      FcHashTableRemove (table->family_hash, (void *)s);
1700
0
    }
1701
1702
0
    if (FcHashTableFind (table->family_blank_hash, (void *)s, (void **)&fe)) {
1703
0
  fe->count--;
1704
0
  if (fe->count == 0)
1705
0
      FcHashTableRemove (table->family_blank_hash, (void *)s);
1706
0
    }
1707
0
}
1708
1709
static FcBool
1710
copy_string (const void *src, void **dest)
1711
214k
{
1712
214k
    *dest = FcStrCopy ((const FcChar8 *)src);
1713
214k
    return FcTrue;
1714
214k
}
1715
1716
static FcBool
1717
FamilyTableInit (FamilyTable *table,
1718
                 FcPattern   *p)
1719
107k
{
1720
107k
    FcPatternElt *e;
1721
107k
    FcBool        ret = FcTrue;
1722
1723
107k
    table->family_blank_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
1724
107k
                                                  (FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
1725
107k
                                                  (FcCopyFunc)copy_string,
1726
107k
                                                  NULL,
1727
107k
                                                  free,
1728
107k
                                                  free);
1729
107k
    table->family_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreCase,
1730
107k
                                            (FcCompareFunc)FcStrCmpIgnoreCase,
1731
107k
                                            (FcCopyFunc)copy_string,
1732
107k
                                            NULL,
1733
107k
                                            free,
1734
107k
                                            free);
1735
107k
    if (!table->family_blank_hash || !table->family_hash)
1736
0
  return FcFalse;
1737
107k
    e = FcPatternObjectFindElt (p, FC_FAMILY_OBJECT);
1738
107k
    if (e)
1739
107k
  ret = FamilyTableAdd (table, FcPatternEltValues (e));
1740
107k
    return ret;
1741
107k
}
1742
1743
static void
1744
FamilyTableClear (FamilyTable *table)
1745
107k
{
1746
107k
    if (table->family_blank_hash)
1747
107k
  FcHashTableDestroy (table->family_blank_hash);
1748
107k
    if (table->family_hash)
1749
107k
  FcHashTableDestroy (table->family_hash);
1750
107k
}
1751
1752
static FcValueList *
1753
FcConfigMatchValueList (FcPattern   *p,
1754
                        FcPattern   *p_pat,
1755
                        FcObject     object,
1756
                        FcMatchKind  kind,
1757
                        FcTest      *t,
1758
                        FcValueList *values,
1759
                        FamilyTable *table)
1760
0
{
1761
0
    FcValueList *ret = 0;
1762
0
    FcExpr      *e = t->expr;
1763
0
    FcValue      value;
1764
0
    FcValueList *v;
1765
0
    FcOp         op;
1766
1767
0
    while (e) {
1768
  /* Compute the value of the match expression */
1769
0
  if (FC_OP_GET_OP (e->op) == FcOpComma) {
1770
0
      value = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1771
0
      e = e->u.tree.right;
1772
0
  } else {
1773
0
      value = FcConfigEvaluate (p, p_pat, object, kind, e);
1774
0
      e = 0;
1775
0
  }
1776
1777
0
  if (t->object == FC_FAMILY_OBJECT && table) {
1778
0
      op = FC_OP_GET_OP (t->op);
1779
0
      if (op == FcOpEqual || op == FcOpListing) {
1780
0
    if (!FamilyTableLookup (table, t->op, FcValueString (&value))) {
1781
0
        ret = 0;
1782
0
        goto done;
1783
0
    }
1784
0
      }
1785
0
      if (op == FcOpNotEqual && t->qual == FcQualAll) {
1786
0
    ret = 0;
1787
0
    if (!FamilyTableLookup (table, t->op, FcValueString (&value))) {
1788
0
        ret = values;
1789
0
    }
1790
0
    goto done;
1791
0
      }
1792
0
  }
1793
0
  for (v = values; v; v = FcValueListNext (v)) {
1794
      /* Compare the pattern value to the match expression value */
1795
0
      if (FcConfigCompareValue (&v->value, t->op, &value)) {
1796
0
    if (!ret)
1797
0
        ret = v;
1798
0
    if (t->qual != FcQualAll)
1799
0
        break;
1800
0
      } else {
1801
0
    if (t->qual == FcQualAll) {
1802
0
        ret = 0;
1803
0
        break;
1804
0
    }
1805
0
      }
1806
0
  }
1807
0
    done:
1808
0
  FcValueDestroy (value);
1809
0
    }
1810
0
    return ret;
1811
0
}
1812
1813
static FcValueList *
1814
FcConfigValues (FcPattern *p, FcPattern *p_pat, FcObject object, FcMatchKind kind, FcExpr *e, FcValueBinding binding)
1815
0
{
1816
0
    FcValueList *l;
1817
1818
0
    if (!e)
1819
0
  return 0;
1820
0
    l = (FcValueList *)malloc (sizeof (FcValueList));
1821
0
    if (!l)
1822
0
  return 0;
1823
0
    if (FC_OP_GET_OP (e->op) == FcOpComma) {
1824
0
  l->value = FcConfigEvaluate (p, p_pat, object, kind, e->u.tree.left);
1825
0
  l->next = FcConfigValues (p, p_pat, object, kind, e->u.tree.right, binding);
1826
0
    } else {
1827
0
  l->value = FcConfigEvaluate (p, p_pat, object, kind, e);
1828
0
  l->next = NULL;
1829
0
    }
1830
0
    l->binding = binding;
1831
0
    if (l->value.type == FcTypeVoid) {
1832
0
  FcValueList *next = FcValueListNext (l);
1833
1834
0
  free (l);
1835
0
  l = next;
1836
0
    }
1837
1838
0
    return l;
1839
0
}
1840
1841
static FcBool
1842
FcConfigAdd (FcValueListPtr *head,
1843
             FcValueList    *position,
1844
             FcBool          append,
1845
             FcValueList    *newp,
1846
             FcObject        object,
1847
             FamilyTable    *table)
1848
0
{
1849
0
    FcValueListPtr *prev, l, last;
1850
0
    FcValueBinding  sameBinding;
1851
1852
0
    if (!newp)
1853
0
  return FcFalse;
1854
0
    if (position)
1855
0
  sameBinding = position->binding;
1856
0
    else
1857
0
  sameBinding = FcValueBindingWeak;
1858
    /*
1859
     * Make sure the stored type is valid for built-in objects
1860
     */
1861
0
    for (l = newp; l != NULL; l = FcValueListNext (l)) {
1862
0
  if (!FcObjectValidType (object, l->value.type)) {
1863
0
      fprintf (stderr,
1864
0
               "Fontconfig warning: FcPattern object %s does not accept value", FcObjectName (object));
1865
0
      FcValuePrintFile (stderr, l->value);
1866
0
      fprintf (stderr, "\n");
1867
1868
0
      if (FcDebug() & FC_DBG_EDIT) {
1869
0
    printf ("Not adding\n");
1870
0
      }
1871
1872
0
      return FcFalse;
1873
0
  }
1874
0
  if (l->binding == FcValueBindingSame)
1875
0
      l->binding = sameBinding;
1876
0
    }
1877
1878
0
    if (object == FC_FAMILY_OBJECT && table) {
1879
0
  if (!FamilyTableAdd (table, newp))
1880
0
      return FcFalse;
1881
0
    }
1882
1883
0
    if (append) {
1884
0
  if (position)
1885
0
      prev = &position->next;
1886
0
  else
1887
0
      for (prev = head; *prev != NULL;
1888
0
           prev = &(*prev)->next)
1889
0
    ;
1890
0
    } else {
1891
0
  if (position) {
1892
0
      for (prev = head; *prev != NULL;
1893
0
           prev = &(*prev)->next) {
1894
0
    if (*prev == position)
1895
0
        break;
1896
0
      }
1897
0
  } else
1898
0
      prev = head;
1899
1900
0
  if (FcDebug() & FC_DBG_EDIT) {
1901
0
      if (*prev == NULL)
1902
0
    printf ("position not on list\n");
1903
0
  }
1904
0
    }
1905
1906
0
    if (FcDebug() & FC_DBG_EDIT) {
1907
0
  printf ("%s list before ", append ? "Append" : "Prepend");
1908
0
  FcValueListPrintWithPosition (*head, *prev);
1909
0
  printf ("\n");
1910
0
    }
1911
1912
0
    if (newp) {
1913
0
  last = newp;
1914
0
  while (last->next != NULL)
1915
0
      last = last->next;
1916
1917
0
  last->next = *prev;
1918
0
  *prev = newp;
1919
0
    }
1920
1921
0
    if (FcDebug() & FC_DBG_EDIT) {
1922
0
  printf ("%s list after ", append ? "Append" : "Prepend");
1923
0
  FcValueListPrint (*head);
1924
0
  printf ("\n");
1925
0
    }
1926
1927
0
    return FcTrue;
1928
0
}
1929
1930
static void
1931
FcConfigDel (FcValueListPtr *head,
1932
             FcValueList    *position,
1933
             FcObject        object,
1934
             FamilyTable    *table)
1935
0
{
1936
0
    FcValueListPtr *prev;
1937
1938
0
    if (object == FC_FAMILY_OBJECT && table) {
1939
0
  FamilyTableDel (table, FcValueString (&position->value));
1940
0
    }
1941
1942
0
    for (prev = head; *prev != NULL; prev = &(*prev)->next) {
1943
0
  if (*prev == position) {
1944
0
      *prev = position->next;
1945
0
      position->next = NULL;
1946
0
      FcValueListDestroy (position);
1947
0
      break;
1948
0
  }
1949
0
    }
1950
0
}
1951
1952
static void
1953
FcConfigPatternAdd (FcPattern   *p,
1954
                    FcObject     object,
1955
                    FcValueList *list,
1956
                    FcBool       append,
1957
                    FamilyTable *table)
1958
0
{
1959
0
    if (list) {
1960
0
  FcPatternElt *e = FcPatternObjectInsertElt (p, object);
1961
1962
0
  if (!e)
1963
0
      return;
1964
0
  FcConfigAdd (&e->values, 0, append, list, object, table);
1965
0
    }
1966
0
}
1967
1968
/*
1969
 * Delete all values associated with a field
1970
 */
1971
static void
1972
FcConfigPatternDel (FcPattern   *p,
1973
                    FcObject     object,
1974
                    FamilyTable *table)
1975
0
{
1976
0
    FcPatternElt *e = FcPatternObjectFindElt (p, object);
1977
0
    if (!e)
1978
0
  return;
1979
0
    while (e->values != NULL)
1980
0
  FcConfigDel (&e->values, e->values, object, table);
1981
0
}
1982
1983
static void
1984
FcConfigPatternCanon (FcPattern *p,
1985
                      FcObject   object)
1986
0
{
1987
0
    FcPatternElt *e = FcPatternObjectFindElt (p, object);
1988
0
    if (!e)
1989
0
  return;
1990
0
    if (e->values == NULL)
1991
0
  FcPatternObjectDel (p, object);
1992
0
}
1993
1994
FcBool
1995
FcConfigSubstituteWithPat (FcConfig   *config,
1996
                           FcPattern  *p,
1997
                           FcPattern  *p_pat,
1998
                           FcMatchKind kind)
1999
107k
{
2000
107k
    FcValue        v;
2001
107k
    FcPtrList     *s;
2002
107k
    FcPtrListIter  iter, iter2;
2003
107k
    FcRule        *r;
2004
107k
    FcRuleSet     *rs;
2005
107k
    FcValueList   *l, **value = NULL, *vl;
2006
107k
    FcPattern     *m;
2007
107k
    FcStrSet      *strs;
2008
107k
    FcObject       object = FC_INVALID_OBJECT;
2009
107k
    FcPatternElt **elt = NULL, *e;
2010
107k
    int            i, nobjs;
2011
107k
    FcBool         retval = FcTrue;
2012
107k
    FcTest       **tst = NULL;
2013
107k
    FamilyTable    data;
2014
107k
    FamilyTable   *table = &data;
2015
2016
107k
    if (kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
2017
0
  return FcFalse;
2018
2019
107k
    config = FcConfigReference (config);
2020
107k
    if (!config)
2021
0
  return FcFalse;
2022
2023
107k
    s = config->subst[kind];
2024
107k
    if (kind == FcMatchPattern) {
2025
107k
  strs = FcConfigGetDefaultLangs (config);
2026
107k
  if (strs) {
2027
107k
      FcStrList *l = FcStrListCreate (strs);
2028
107k
      FcChar8   *lang;
2029
107k
      FcValue    v;
2030
107k
      FcLangSet *lsund = FcLangSetCreate();
2031
2032
107k
      FcLangSetAdd (lsund, (const FcChar8 *)"und");
2033
107k
      FcStrSetDestroy (strs);
2034
214k
      while (l && (lang = FcStrListNext (l))) {
2035
107k
    FcPatternElt *e = FcPatternObjectFindElt (p, FC_LANG_OBJECT);
2036
2037
107k
    if (e) {
2038
107k
        FcValueListPtr ll;
2039
2040
214k
        for (ll = FcPatternEltValues (e); ll; ll = FcValueListNext (ll)) {
2041
107k
      FcValue vv = FcValueCanonicalize (&ll->value);
2042
2043
107k
      if (vv.type == FcTypeLangSet) {
2044
0
          FcLangSet *ls = FcLangSetCreate();
2045
0
          FcBool     b;
2046
2047
0
          FcLangSetAdd (ls, lang);
2048
0
          b = FcLangSetContains (vv.u.l, ls);
2049
0
          FcLangSetDestroy (ls);
2050
0
          if (b)
2051
0
        goto bail_lang;
2052
0
          if (FcLangSetContains (vv.u.l, lsund))
2053
0
        goto bail_lang;
2054
107k
      } else {
2055
107k
          if (FcStrCmpIgnoreCase (vv.u.s, lang) == 0)
2056
0
        goto bail_lang;
2057
107k
          if (FcStrCmpIgnoreCase (vv.u.s, (const FcChar8 *)"und") == 0)
2058
0
        goto bail_lang;
2059
107k
      }
2060
107k
        }
2061
107k
    }
2062
107k
    v.type = FcTypeString;
2063
107k
    v.u.s = lang;
2064
2065
107k
    FcPatternObjectAddWithBinding (p, FC_LANG_OBJECT, v, FcValueBindingWeak, FcTrue);
2066
107k
      }
2067
107k
  bail_lang:
2068
107k
      FcStrListDone (l);
2069
107k
      FcLangSetDestroy (lsund);
2070
107k
  }
2071
107k
  if (FcPatternObjectGet (p, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch) {
2072
107k
      FcChar8 *prgname = FcConfigGetPrgname (config);
2073
107k
      if (prgname)
2074
107k
    FcPatternObjectAddString (p, FC_PRGNAME_OBJECT, prgname);
2075
107k
  }
2076
107k
    }
2077
2078
107k
    nobjs = FC_MAX_BASE_OBJECT + config->maxObjects + 2;
2079
107k
    value = (FcValueList **)malloc (SIZEOF_VOID_P * nobjs);
2080
107k
    if (!value) {
2081
0
  retval = FcFalse;
2082
0
  goto bail1;
2083
0
    }
2084
107k
    elt = (FcPatternElt **)malloc (SIZEOF_VOID_P * nobjs);
2085
107k
    if (!elt) {
2086
0
  retval = FcFalse;
2087
0
  goto bail1;
2088
0
    }
2089
107k
    tst = (FcTest **)malloc (SIZEOF_VOID_P * nobjs);
2090
107k
    if (!tst) {
2091
0
  retval = FcFalse;
2092
0
  goto bail1;
2093
0
    }
2094
2095
107k
    if (FcDebug() & FC_DBG_EDIT) {
2096
0
  printf ("FcConfigSubstitute(%s) ", kind == FcMatchPattern ? "Pattern" : kind == FcMatchFont ? "Font" : kind == FcMatchScan ? "Scan" : "Unknown");
2097
0
  FcPatternPrint (p);
2098
0
    }
2099
2100
107k
    if (!FamilyTableInit (&data, p)) {
2101
0
  retval = FcFalse;
2102
0
  FamilyTableClear (&data);
2103
0
  goto bail1;
2104
0
    }
2105
2106
107k
    FcPtrListIterInit (s, &iter);
2107
107k
    for (; FcPtrListIterIsValid (s, &iter); FcPtrListIterNext (s, &iter)) {
2108
0
  rs = (FcRuleSet *)FcPtrListIterGetValue (s, &iter);
2109
0
  if (FcDebug() & FC_DBG_EDIT) {
2110
0
      printf ("\nRule Set: %s\n", rs->name);
2111
0
  }
2112
0
  FcPtrListIterInit (rs->subst[kind], &iter2);
2113
0
  for (; FcPtrListIterIsValid (rs->subst[kind], &iter2); FcPtrListIterNext (rs->subst[kind], &iter2)) {
2114
0
      r = (FcRule *)FcPtrListIterGetValue (rs->subst[kind], &iter2);
2115
0
      for (i = 0; i < nobjs; i++) {
2116
0
    elt[i] = NULL;
2117
0
    value[i] = NULL;
2118
0
    tst[i] = NULL;
2119
0
      }
2120
0
      for (; r; r = r->next) {
2121
0
    switch (r->type) {
2122
0
    case FcRuleUnknown:
2123
        /* shouldn't be reached */
2124
0
        break;
2125
0
    case FcRuleTest:
2126
0
        object = FC_OBJ_ID (r->u.test->object);
2127
        /*
2128
         * Check the tests to see if
2129
         * they all match the pattern
2130
         */
2131
0
        if (FcDebug() & FC_DBG_EDIT) {
2132
0
      printf ("FcConfigSubstitute test ");
2133
0
      FcTestPrint (r->u.test);
2134
0
        }
2135
0
        if (kind == FcMatchFont && r->u.test->kind == FcMatchPattern) {
2136
0
      m = p_pat;
2137
0
      table = NULL;
2138
0
        } else {
2139
0
      m = p;
2140
0
      table = &data;
2141
0
        }
2142
0
        if (m)
2143
0
      e = FcPatternObjectFindElt (m, r->u.test->object);
2144
0
        else
2145
0
      e = NULL;
2146
        /* different 'kind' won't be the target of edit */
2147
0
        if (!elt[object] && kind == r->u.test->kind) {
2148
0
      elt[object] = e;
2149
0
      tst[object] = r->u.test;
2150
0
        }
2151
        /*
2152
         * If there's no such field in the font,
2153
         * then FcQualAll matches while FcQualAny does not
2154
         */
2155
0
        if (!e) {
2156
0
      if (r->u.test->qual == FcQualAll) {
2157
0
          value[object] = NULL;
2158
0
          continue;
2159
0
      } else {
2160
0
          if (FcDebug() & FC_DBG_EDIT)
2161
0
        printf ("No match\n");
2162
0
          goto bail;
2163
0
      }
2164
0
        }
2165
        /*
2166
         * Check to see if there is a match, mark the location
2167
         * to apply match-relative edits
2168
         */
2169
0
        vl = FcConfigMatchValueList (m, p_pat, object, kind, r->u.test, e->values, table);
2170
        /* different 'kind' won't be the target of edit */
2171
0
        if (!value[object] && kind == r->u.test->kind)
2172
0
      value[object] = vl;
2173
0
        if (!vl ||
2174
0
            (r->u.test->qual == FcQualFirst && vl != e->values) ||
2175
0
            (r->u.test->qual == FcQualNotFirst && vl == e->values)) {
2176
0
      if (FcDebug() & FC_DBG_EDIT)
2177
0
          printf ("No match\n");
2178
0
      goto bail;
2179
0
        }
2180
0
        break;
2181
0
    case FcRuleEdit:
2182
0
        object = FC_OBJ_ID (r->u.edit->object);
2183
0
        if (FcDebug() & FC_DBG_EDIT) {
2184
0
      printf ("Substitute ");
2185
0
      FcEditPrint (r->u.edit);
2186
0
      printf ("\n\n");
2187
0
        }
2188
        /*
2189
         * Evaluate the list of expressions
2190
         */
2191
0
        l = FcConfigValues (p, p_pat, object, kind, r->u.edit->expr, r->u.edit->binding);
2192
0
        if (tst[object] && (tst[object]->kind == FcMatchFont || kind == FcMatchPattern))
2193
0
      elt[object] = FcPatternObjectFindElt (p, tst[object]->object);
2194
2195
0
        switch (FC_OP_GET_OP (r->u.edit->op)) {
2196
0
        case FcOpAssign:
2197
      /*
2198
       * If there was a test, then replace the matched
2199
       * value with the new list of values
2200
       */
2201
0
      if (value[object]) {
2202
0
          FcValueList *thisValue = value[object];
2203
0
          FcValueList *nextValue = l;
2204
2205
          /*
2206
           * Append the new list of values after the current value
2207
           */
2208
0
          FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object, table);
2209
          /*
2210
           * Delete the marked value
2211
           */
2212
0
          if (thisValue)
2213
0
        FcConfigDel (&elt[object]->values, thisValue, object, table);
2214
          /*
2215
           * Adjust a pointer into the value list to ensure
2216
           * future edits occur at the same place
2217
           */
2218
0
          value[object] = nextValue;
2219
0
          break;
2220
0
      }
2221
      /* fall through ... */
2222
0
        case FcOpAssignReplace:
2223
      /*
2224
       * Delete all of the values and insert
2225
       * the new set
2226
       */
2227
0
      FcConfigPatternDel (p, r->u.edit->object, table);
2228
0
      FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
2229
      /*
2230
       * Adjust a pointer into the value list as they no
2231
       * longer point to anything valid
2232
       */
2233
0
      value[object] = NULL;
2234
0
      break;
2235
0
        case FcOpPrepend:
2236
0
      if (value[object]) {
2237
0
          FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object, table);
2238
0
          break;
2239
0
      }
2240
      /* fall through ... */
2241
0
        case FcOpPrependFirst:
2242
0
      FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse, table);
2243
0
      break;
2244
0
        case FcOpAppend:
2245
0
      if (value[object]) {
2246
0
          FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object, table);
2247
0
          break;
2248
0
      }
2249
      /* fall through ... */
2250
0
        case FcOpAppendLast:
2251
0
      FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
2252
0
      break;
2253
0
        case FcOpDelete:
2254
0
      if (value[object]) {
2255
0
          FcConfigDel (&elt[object]->values, value[object], object, table);
2256
0
          FcValueListDestroy (l);
2257
0
          break;
2258
0
      }
2259
      /* fall through ... */
2260
0
        case FcOpDeleteAll:
2261
0
      FcConfigPatternDel (p, r->u.edit->object, table);
2262
0
      FcValueListDestroy (l);
2263
0
      break;
2264
0
        default:
2265
0
      FcValueListDestroy (l);
2266
0
      break;
2267
0
        }
2268
        /*
2269
         * Now go through the pattern and eliminate
2270
         * any properties without data
2271
         */
2272
0
        FcConfigPatternCanon (p, r->u.edit->object);
2273
2274
0
        if (FcDebug() & FC_DBG_EDIT) {
2275
0
      printf ("FcConfigSubstitute edit");
2276
0
      FcPatternPrint (p);
2277
0
        }
2278
0
        break;
2279
0
    }
2280
0
      }
2281
0
  bail:;
2282
0
  }
2283
0
    }
2284
107k
    if (FcDebug() & FC_DBG_EDIT) {
2285
0
  printf ("FcConfigSubstitute done");
2286
0
  FcPatternPrint (p);
2287
0
    }
2288
107k
    FamilyTableClear (&data);
2289
107k
bail1:
2290
107k
    if (elt)
2291
107k
  free (elt);
2292
107k
    if (value)
2293
107k
  free (value);
2294
107k
    if (tst)
2295
107k
  free (tst);
2296
107k
    FcConfigDestroy (config);
2297
2298
107k
    return retval;
2299
107k
}
2300
2301
FcBool
2302
FcConfigSubstitute (FcConfig   *config,
2303
                    FcPattern  *p,
2304
                    FcMatchKind kind)
2305
107k
{
2306
107k
    return FcConfigSubstituteWithPat (config, p, 0, kind);
2307
107k
}
2308
2309
#if defined(_WIN32)
2310
2311
static FcChar8 fontconfig_path[1000] = "";       /* MT-dontcare */
2312
FcChar8        fontconfig_instprefix[1000] = ""; /* MT-dontcare */
2313
2314
#  if (defined(PIC) || defined(DLL_EXPORT))
2315
2316
BOOL WINAPI
2317
DllMain (HINSTANCE hinstDLL,
2318
         DWORD     fdwReason,
2319
         LPVOID    lpvReserved);
2320
2321
BOOL WINAPI
2322
DllMain (HINSTANCE hinstDLL,
2323
         DWORD     fdwReason,
2324
         LPVOID    lpvReserved)
2325
{
2326
    FcChar8 *p;
2327
2328
    switch (fdwReason) {
2329
    case DLL_PROCESS_ATTACH:
2330
  if (!GetModuleFileName ((HMODULE)hinstDLL, (LPCH)fontconfig_path,
2331
                          sizeof (fontconfig_path)))
2332
      break;
2333
2334
  /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
2335
   * assume it's a Unix-style installation tree, and use
2336
   * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
2337
   * folder where the DLL is as FONTCONFIG_PATH.
2338
   */
2339
  p = (FcChar8 *)strrchr ((const char *)fontconfig_path, '\\');
2340
  if (p) {
2341
      *p = '\0';
2342
      p = (FcChar8 *)strrchr ((const char *)fontconfig_path, '\\');
2343
      if (p && (FcStrCmpIgnoreCase (p + 1, (const FcChar8 *)"bin") == 0 ||
2344
                FcStrCmpIgnoreCase (p + 1, (const FcChar8 *)"lib") == 0))
2345
    *p = '\0';
2346
      strcat ((char *)fontconfig_instprefix, (char *)fontconfig_path);
2347
      strcat ((char *)fontconfig_path, "\\etc\\fonts");
2348
  } else
2349
      fontconfig_path[0] = '\0';
2350
2351
  break;
2352
    }
2353
2354
    return TRUE;
2355
}
2356
2357
#  endif /* !PIC */
2358
2359
#  undef FONTCONFIG_PATH
2360
#  define FONTCONFIG_PATH fontconfig_path
2361
2362
#endif /* !_WIN32 */
2363
2364
#ifndef FONTCONFIG_FILE
2365
11
#  define FONTCONFIG_FILE "fonts.conf"
2366
#endif
2367
2368
static FcChar8 *
2369
FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
2370
44
{
2371
44
    FcChar8 *path;
2372
44
    int      size, osize;
2373
2374
44
    if (!dir)
2375
33
  dir = (FcChar8 *)"";
2376
2377
44
    osize = strlen ((char *)dir) + 1 + strlen ((char *)file) + 1;
2378
    /*
2379
     * workaround valgrind warning because glibc takes advantage of how it knows memory is
2380
     * allocated to implement strlen by reading in groups of 4
2381
     */
2382
44
    size = (osize + 3) & ~3;
2383
2384
44
    path = malloc (size);
2385
44
    if (!path)
2386
0
  return 0;
2387
2388
44
    strcpy ((char *)path, (const char *)dir);
2389
    /* make sure there's a single separator */
2390
#ifdef _WIN32
2391
    if ((!path[0] || (path[strlen ((char *)path) - 1] != '/' &&
2392
                      path[strlen ((char *)path) - 1] != '\\')) &&
2393
        !(file[0] == '/' ||
2394
          file[0] == '\\' ||
2395
          (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
2396
  strcat ((char *)path, "\\");
2397
#else
2398
44
    if ((!path[0] || path[strlen ((char *)path) - 1] != '/') && file[0] != '/')
2399
11
  strcat ((char *)path, "/");
2400
33
    else
2401
33
  osize--;
2402
44
#endif
2403
44
    strcat ((char *)path, (char *)file);
2404
2405
44
    if (access ((char *)path, R_OK) == 0)
2406
0
  return path;
2407
2408
44
    FcStrFree (path);
2409
2410
44
    return 0;
2411
44
}
2412
2413
static FcChar8 **
2414
FcConfigGetPath (void)
2415
11
{
2416
11
    FcChar8 **path;
2417
11
    FcChar8  *env, *e, *colon;
2418
11
    FcChar8  *dir;
2419
11
    int       npath;
2420
11
    int       i;
2421
2422
11
    npath = 2; /* default dir + null */
2423
11
    env = (FcChar8 *)getenv ("FONTCONFIG_PATH");
2424
11
    if (env) {
2425
0
  e = env;
2426
0
  npath++;
2427
0
  while (*e)
2428
0
      if (*e++ == FC_SEARCH_PATH_SEPARATOR)
2429
0
    npath++;
2430
0
    }
2431
11
    path = calloc (npath, sizeof (FcChar8 *));
2432
11
    if (!path)
2433
0
  goto bail0;
2434
11
    i = 0;
2435
2436
11
    if (env) {
2437
0
  e = env;
2438
0
  while (*e) {
2439
0
      colon = (FcChar8 *)strchr ((char *)e, FC_SEARCH_PATH_SEPARATOR);
2440
0
      if (!colon)
2441
0
    colon = e + strlen ((char *)e);
2442
0
      path[i] = malloc (colon - e + 1);
2443
0
      if (!path[i])
2444
0
    goto bail1;
2445
0
      strncpy ((char *)path[i], (const char *)e, colon - e);
2446
0
      path[i][colon - e] = '\0';
2447
0
      if (*colon)
2448
0
    e = colon + 1;
2449
0
      else
2450
0
    e = colon;
2451
0
      i++;
2452
0
  }
2453
0
    }
2454
2455
#ifdef _WIN32
2456
    if (fontconfig_path[0] == '\0') {
2457
  char *p;
2458
  if (!GetModuleFileName (NULL, (LPCH)fontconfig_path, sizeof (fontconfig_path)))
2459
      goto bail1;
2460
  p = strrchr ((const char *)fontconfig_path, '\\');
2461
  if (p)
2462
      *p = '\0';
2463
  strcat ((char *)fontconfig_path, "\\fonts");
2464
    }
2465
#endif
2466
11
    dir = (FcChar8 *)FONTCONFIG_PATH;
2467
11
    path[i] = malloc (strlen ((char *)dir) + 1);
2468
11
    if (!path[i])
2469
0
  goto bail1;
2470
11
    strcpy ((char *)path[i], (const char *)dir);
2471
11
    return path;
2472
2473
0
bail1:
2474
0
    for (i = 0; path[i]; i++)
2475
0
  free (path[i]);
2476
0
    free (path);
2477
0
bail0:
2478
0
    return 0;
2479
0
}
2480
2481
static void
2482
FcConfigFreePath (FcChar8 **path)
2483
11
{
2484
11
    FcChar8 **p;
2485
2486
22
    for (p = path; *p; p++)
2487
11
  free (*p);
2488
11
    free (path);
2489
11
}
2490
2491
static FcBool _FcConfigHomeEnabled = FcTrue; /* MT-goodenough */
2492
2493
FcChar8 *
2494
FcConfigHome (void)
2495
44
{
2496
44
    if (_FcConfigHomeEnabled) {
2497
44
  char *home = getenv ("HOME");
2498
2499
#ifdef _WIN32
2500
  if (home == NULL)
2501
      home = getenv ("USERPROFILE");
2502
#endif
2503
2504
44
  return (FcChar8 *)home;
2505
44
    }
2506
0
    return 0;
2507
44
}
2508
2509
FcChar8 *
2510
FcConfigXdgCacheHome (void)
2511
11
{
2512
11
    const char *env = getenv ("XDG_CACHE_HOME");
2513
11
    FcChar8    *ret = NULL;
2514
2515
11
    if (!_FcConfigHomeEnabled)
2516
0
  return NULL;
2517
11
    if (env && env[0])
2518
0
  ret = FcStrCopy ((const FcChar8 *)env);
2519
11
    else {
2520
11
  const FcChar8 *home = FcConfigHome();
2521
11
  size_t         len = home ? strlen ((const char *)home) : 0;
2522
2523
11
  ret = malloc (len + 7 + 1);
2524
11
  if (ret) {
2525
11
      if (home)
2526
11
    memcpy (ret, home, len);
2527
11
      memcpy (&ret[len], FC_DIR_SEPARATOR_S ".cache", 7);
2528
11
      ret[len + 7] = 0;
2529
11
  }
2530
11
    }
2531
2532
11
    return ret;
2533
11
}
2534
2535
FcChar8 *
2536
FcConfigXdgConfigHome (void)
2537
22
{
2538
22
    const char *env = getenv ("XDG_CONFIG_HOME");
2539
22
    FcChar8    *ret = NULL;
2540
2541
22
    if (!_FcConfigHomeEnabled)
2542
0
  return NULL;
2543
22
    if (env)
2544
0
  ret = FcStrCopy ((const FcChar8 *)env);
2545
22
    else {
2546
22
  const FcChar8 *home = FcConfigHome();
2547
22
  size_t         len = home ? strlen ((const char *)home) : 0;
2548
2549
22
  ret = malloc (len + 8 + 1);
2550
22
  if (ret) {
2551
22
      if (home)
2552
22
    memcpy (ret, home, len);
2553
22
      memcpy (&ret[len], FC_DIR_SEPARATOR_S ".config", 8);
2554
22
      ret[len + 8] = 0;
2555
22
  }
2556
22
    }
2557
2558
22
    return ret;
2559
22
}
2560
2561
FcChar8 *
2562
FcConfigXdgDataHome (void)
2563
11
{
2564
11
    const char *env = getenv ("XDG_DATA_HOME");
2565
11
    FcChar8    *ret = NULL;
2566
2567
11
    if (!_FcConfigHomeEnabled)
2568
0
  return NULL;
2569
11
    if (env)
2570
0
  ret = FcStrCopy ((const FcChar8 *)env);
2571
11
    else {
2572
11
  const FcChar8 *home = FcConfigHome();
2573
11
  size_t         len = home ? strlen ((const char *)home) : 0;
2574
2575
11
  ret = malloc (len + 13 + 1);
2576
11
  if (ret) {
2577
11
      if (home)
2578
11
    memcpy (ret, home, len);
2579
11
      memcpy (&ret[len], FC_DIR_SEPARATOR_S ".local" FC_DIR_SEPARATOR_S "share", 13);
2580
11
      ret[len + 13] = 0;
2581
11
  }
2582
11
    }
2583
2584
11
    return ret;
2585
11
}
2586
2587
FcStrSet *
2588
FcConfigXdgDataDirs (void)
2589
11
{
2590
11
    const char *env = getenv ("XDG_DATA_DIRS");
2591
11
    FcStrSet   *ret = FcStrSetCreate();
2592
2593
11
    if (env && *env) {
2594
0
  FcChar8 *ee, *e = ee = FcStrCopy ((const FcChar8 *)env);
2595
2596
  /* We don't intentionally use FC_SEARCH_PATH_SEPARATOR here because of:
2597
   *   The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
2598
   * in doc.
2599
   */
2600
0
  while (e) {
2601
0
      FcChar8 *p = (FcChar8 *)strchr ((const char *)e, ':');
2602
0
      FcChar8 *s;
2603
0
      size_t   len;
2604
2605
0
      if (!p) {
2606
0
    s = FcStrCopy (e);
2607
0
    e = NULL;
2608
0
      } else {
2609
0
    *p = 0;
2610
0
    s = FcStrCopy (e);
2611
0
    e = p + 1;
2612
0
      }
2613
0
      len = strlen ((const char *)s);
2614
0
      if (len == 0) {
2615
0
    FcStrFree (s);
2616
0
    continue;
2617
0
      }
2618
2619
0
      if (s[len - 1] == FC_DIR_SEPARATOR) {
2620
0
    do {
2621
0
        len--;
2622
0
    } while (len > 1 && s[len - 1] == FC_DIR_SEPARATOR);
2623
0
    s[len] = 0;
2624
0
      }
2625
0
      FcStrSetAdd (ret, s);
2626
0
      FcStrFree (s);
2627
0
  }
2628
0
  FcStrFree (ee);
2629
11
    } else {
2630
  /* From spec doc at https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
2631
   *
2632
   * If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
2633
   */
2634
11
  FcStrSetAdd (ret, (const FcChar8 *)"/usr/local/share");
2635
11
  FcStrSetAdd (ret, (const FcChar8 *)"/usr/share");
2636
11
    }
2637
2638
11
    return ret;
2639
11
}
2640
2641
FcBool
2642
FcConfigEnableHome (FcBool enable)
2643
0
{
2644
0
    FcBool prev = _FcConfigHomeEnabled;
2645
0
    _FcConfigHomeEnabled = enable;
2646
0
    return prev;
2647
0
}
2648
2649
FcChar8 *
2650
FcConfigGetFilename (FcConfig      *config,
2651
                     const FcChar8 *url)
2652
44
{
2653
44
    FcChar8       *file, *dir, **path, **p;
2654
44
    const FcChar8 *sysroot;
2655
2656
44
    config = FcConfigReference (config);
2657
44
    if (!config)
2658
0
  return NULL;
2659
44
    sysroot = FcConfigGetSysRoot (config);
2660
44
    if (!url || !*url) {
2661
11
  url = (FcChar8 *)getenv ("FONTCONFIG_FILE");
2662
11
  if (!url)
2663
11
      url = (FcChar8 *)FONTCONFIG_FILE;
2664
11
    }
2665
44
    file = 0;
2666
2667
44
    if (FcStrIsAbsoluteFilename (url)) {
2668
33
  if (sysroot) {
2669
0
      size_t len = strlen ((const char *)sysroot);
2670
2671
      /* Workaround to avoid adding sysroot repeatedly */
2672
0
      if (strncmp ((const char *)url, (const char *)sysroot, len) == 0)
2673
0
    sysroot = NULL;
2674
0
  }
2675
33
  file = FcConfigFileExists (sysroot, url);
2676
33
  goto bail;
2677
33
    }
2678
2679
11
    if (*url == '~') {
2680
0
  dir = FcConfigHome();
2681
0
  if (dir) {
2682
0
      FcChar8 *s;
2683
2684
0
      if (sysroot)
2685
0
    s = FcStrBuildFilename (sysroot, dir, NULL);
2686
0
      else
2687
0
    s = dir;
2688
0
      file = FcConfigFileExists (s, url + 1);
2689
0
      if (sysroot)
2690
0
    FcStrFree (s);
2691
0
  } else
2692
0
      file = 0;
2693
11
    } else {
2694
11
  path = FcConfigGetPath();
2695
11
  if (!path) {
2696
0
      file = NULL;
2697
0
      goto bail;
2698
0
  }
2699
22
  for (p = path; *p; p++) {
2700
11
      FcChar8 *s;
2701
2702
11
      if (sysroot)
2703
0
    s = FcStrBuildFilename (sysroot, *p, NULL);
2704
11
      else
2705
11
    s = *p;
2706
11
      file = FcConfigFileExists (s, url);
2707
11
      if (sysroot)
2708
0
    FcStrFree (s);
2709
11
      if (file)
2710
0
    break;
2711
11
  }
2712
11
  FcConfigFreePath (path);
2713
11
    }
2714
44
bail:
2715
44
    FcConfigDestroy (config);
2716
2717
44
    return file;
2718
11
}
2719
2720
FcChar8 *
2721
FcConfigFilename (const FcChar8 *url)
2722
0
{
2723
0
    return FcConfigGetFilename (NULL, url);
2724
0
}
2725
2726
FcChar8 *
2727
FcConfigRealFilename (FcConfig      *config,
2728
                      const FcChar8 *url)
2729
0
{
2730
0
    FcChar8 *n = FcConfigGetFilename (config, url);
2731
2732
0
    if (n) {
2733
0
  FcChar8     buf[FC_PATH_MAX];
2734
0
  ssize_t     len;
2735
0
  struct stat sb;
2736
2737
0
  if ((len = FcReadLink (n, buf, sizeof (buf) - 1)) != -1) {
2738
0
      buf[len] = 0;
2739
2740
      /* We try to pick up a config from FONTCONFIG_FILE
2741
       * when url is null. don't try to address the real filename
2742
       * if it is a named pipe.
2743
       */
2744
0
      if (!url && FcStat (n, &sb) == 0 && S_ISFIFO (sb.st_mode))
2745
0
    return n;
2746
0
      else if (!FcStrIsAbsoluteFilename (buf)) {
2747
0
    FcChar8 *dirname = FcStrDirname (n);
2748
0
    FcStrFree (n);
2749
0
    if (!dirname)
2750
0
        return NULL;
2751
2752
0
    FcChar8 *path = FcStrBuildFilename (dirname, buf, NULL);
2753
0
    FcStrFree (dirname);
2754
0
    if (!path)
2755
0
        return NULL;
2756
2757
0
    n = FcStrCanonFilename (path);
2758
0
    FcStrFree (path);
2759
0
      } else {
2760
0
    FcStrFree (n);
2761
0
    n = FcStrCopy (buf);
2762
0
      }
2763
0
  }
2764
0
    }
2765
2766
0
    return n;
2767
0
}
2768
2769
/*
2770
 * Manage the application-specific fonts
2771
 */
2772
2773
FcBool
2774
FcConfigAppFontAddFile (FcConfig      *config,
2775
                        const FcChar8 *file)
2776
0
{
2777
0
    FcFontSet *set;
2778
0
    FcBool     ret = FcTrue;
2779
2780
0
    if (FcFileIsDir (file))
2781
0
  return FcConfigAppFontAddDir (config, file);
2782
2783
0
    config = FcConfigReference (config);
2784
0
    if (!config)
2785
0
  return FcFalse;
2786
2787
0
    FcRwLockWriteLock (&config->fonts_lock);
2788
2789
0
    set = config->fonts[FcSetApplication];
2790
0
    if (!set) {
2791
0
  set = FcFontSetCreate();
2792
0
  if (!set) {
2793
0
      ret = FcFalse;
2794
0
      goto bail;
2795
0
  }
2796
0
  FcConfigSetFontsLocked (config, set, FcSetApplication);
2797
0
    }
2798
2799
0
    if (!FcFileScanFontFile (set, file, config)) {
2800
0
  ret = FcFalse;
2801
0
  goto bail;
2802
0
    }
2803
0
    if (!FcStrSetAddFilename (config->appFonts, file)) {
2804
0
  ret = FcFalse;
2805
0
  goto bail;
2806
0
    }
2807
0
bail:
2808
0
    FcRwLockUnlockWrite (&config->fonts_lock);
2809
0
    FcConfigDestroy (config);
2810
2811
0
    return ret;
2812
0
}
2813
2814
FcBool
2815
FcConfigAppFontAddDir (FcConfig      *config,
2816
                       const FcChar8 *dir)
2817
0
{
2818
0
    FcFontSet *set;
2819
0
    FcStrSet  *dirs;
2820
0
    FcBool     ret = FcTrue;
2821
2822
0
    if (FcFileIsFile (dir))
2823
0
  return FcConfigAppFontAddFile (config, dir);
2824
2825
0
    config = FcConfigReference (config);
2826
0
    if (!config)
2827
0
  return FcFalse;
2828
2829
0
    dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
2830
0
    if (!dirs) {
2831
0
  ret = FcFalse;
2832
0
  goto bail;
2833
0
    }
2834
2835
0
    FcRwLockWriteLock (&config->fonts_lock);
2836
2837
0
    set = config->fonts[FcSetApplication];
2838
0
    if (!set) {
2839
0
  set = FcFontSetCreate();
2840
0
  if (!set) {
2841
0
      ret = FcFalse;
2842
0
      goto bail2;
2843
0
  }
2844
0
  FcConfigSetFontsLocked (config, set, FcSetApplication);
2845
0
    }
2846
2847
0
    if (!FcStrSetAddFilename (dirs, dir)) {
2848
0
  ret = FcFalse;
2849
0
  goto bail2;
2850
0
    }
2851
0
    if (!FcStrSetAddFilename (config->appFonts, dir)) {
2852
0
  ret = FcFalse;
2853
0
  goto bail2;
2854
0
    }
2855
0
    if (!FcConfigAddDirList (config, FcSetApplication, dirs)) {
2856
0
  ret = FcFalse;
2857
0
  goto bail2;
2858
0
    }
2859
0
bail2:
2860
0
    FcRwLockUnlockWrite (&config->fonts_lock);
2861
0
    FcStrSetDestroy (dirs);
2862
0
bail:
2863
0
    FcConfigDestroy (config);
2864
2865
0
    return ret;
2866
0
}
2867
2868
void
2869
FcConfigAppFontClear (FcConfig *config)
2870
0
{
2871
0
    config = FcConfigReference (config);
2872
0
    if (!config)
2873
0
  return;
2874
2875
0
    FcRwLockWriteLock (&config->fonts_lock);
2876
0
    FcStrSetDeleteAll (config->appFonts);
2877
0
    FcConfigSetFontsLocked (config, 0, FcSetApplication);
2878
0
    FcRwLockUnlockWrite (&config->fonts_lock);
2879
2880
0
    FcConfigDestroy (config);
2881
0
}
2882
2883
void
2884
FcConfigPreferAppFont (FcConfig *config, FcBool flag)
2885
0
{
2886
0
    config = FcConfigReference (config);
2887
0
    if (!config)
2888
0
  return;
2889
2890
0
    config->prefer_app_fonts = flag;
2891
2892
0
    FcConfigDestroy (config);
2893
0
}
2894
2895
void
2896
FcConfigSetWarningFlags (FcConfig *config, int warn, FcBool flag)
2897
0
{
2898
0
    FcBool init = FcFalse;
2899
0
    int    nretry = 3;
2900
2901
0
retry:
2902
0
    if (!config) {
2903
  /* We can't use FcConfigGetCurrent() here to initialize
2904
   * bitfields before loading config files
2905
   */
2906
0
  config = fc_atomic_ptr_get (&_fcConfig);
2907
0
  if (!config) {
2908
0
      config = FcConfigCreate();
2909
0
      if (!config)
2910
0
    return;
2911
0
      init = FcTrue;
2912
0
  }
2913
0
    }
2914
0
    if (flag)
2915
0
  config->warns |= warn;
2916
0
    else
2917
0
  config->warns ^= warn;
2918
2919
0
    if (init) {
2920
0
  config = FcInitLoadOwnConfigAndFonts (config);
2921
0
  if (!config) {
2922
      /* Something failed. this is usually unlikely. so retrying */
2923
0
      init = FcFalse;
2924
0
      if (--nretry == 0) {
2925
0
    fprintf (stderr, "Fontconfig warning: Unable to initialize config and retry limit exceeded. all warning flags are turned off.\n");
2926
0
    return;
2927
0
      }
2928
0
      goto retry;
2929
0
  }
2930
0
  FcConfigSetCurrent (config);
2931
0
  FcConfigDestroy (config);
2932
0
    }
2933
0
}
2934
2935
int
2936
FcConfigGetWarningFlags (FcConfig *config)
2937
55
{
2938
55
    int ret;
2939
2940
55
    config = FcConfigReference (config);
2941
55
    if (!config)
2942
0
  return 0;
2943
55
    ret = config->warns;
2944
2945
55
    FcConfigDestroy (config);
2946
2947
55
    return ret;
2948
55
}
2949
2950
/*
2951
 * Manage filename-based font source selectors
2952
 */
2953
2954
FcBool
2955
FcConfigGlobAdd (FcConfig      *config,
2956
                 const FcChar8 *glob,
2957
                 FcBool         accept)
2958
0
{
2959
0
    FcStrSet      *set = accept ? config->acceptGlobs : config->rejectGlobs;
2960
0
    FcChar8       *realglob = FcStrCopyFilename (glob);
2961
0
    FcChar8       *cwd = NULL;
2962
0
    const FcChar8 *s;
2963
0
    FcBool         ret;
2964
2965
    /*
2966
     * FcStrCopyFilename canonicalize a path string and prepend
2967
     * current directory name if no path included in a string.
2968
     * This isn't a desired behavior here.
2969
     * So drop the extra path name if they have. Otherwise use it as it is.
2970
     *
2971
     * Only a relative glob can have gained that prefix. An absolute one
2972
     * has to be kept as it is, otherwise the code below would strip its
2973
     * leading components whenever the current directory happens to be a
2974
     * lexical prefix of it. That is particularly harmful when running from
2975
     * the root directory, where FcStrCopyFilename(".") gives an empty
2976
     * string and every absolute glob would then lose its leading '/'.
2977
     */
2978
0
    if (FcStrIsAbsoluteFilename (glob) || FcStrUsesHome (glob)) {
2979
  /* Nothing to fall back on when ~ cannot be expanded */
2980
0
  if (!realglob)
2981
0
      return FcFalse;
2982
0
  s = realglob;
2983
0
    } else {
2984
0
  cwd = FcStrCopyFilename ((const FcChar8 *)".");
2985
0
  if (!cwd || !realglob)
2986
0
      s = glob;
2987
0
  else {
2988
0
      size_t len = strlen ((const char *)cwd);
2989
      /* No need to use FC_DIR_SEPARATOR because '\\' will be
2990
       * replaced with / by FcConvertDosPath in FcStrCanonFilename
2991
       */
2992
0
      if (strncmp ((const char *)cwd, (const char *)realglob, len) == 0 &&
2993
0
          realglob[len] == '/')
2994
0
    s = &realglob[len + 1];
2995
0
      else
2996
0
    s = realglob;
2997
0
  }
2998
0
    }
2999
3000
0
    ret = FcStrSetAdd (set, s);
3001
0
    FcStrFree (realglob);
3002
0
    if (cwd)
3003
0
  FcStrFree (cwd);
3004
0
    return ret;
3005
0
}
3006
3007
static FcBool
3008
FcConfigGlobsMatch (const FcStrSet *globs,
3009
                    const FcChar8  *string)
3010
1.14k
{
3011
1.14k
    int i;
3012
3013
1.14k
    for (i = 0; i < globs->num; i++)
3014
0
  if (FcStrGlobMatch (globs->strs[i], string))
3015
0
      return FcTrue;
3016
1.14k
    return FcFalse;
3017
1.14k
}
3018
3019
FcBool
3020
FcConfigAcceptFilename (FcConfig      *config,
3021
                        const FcChar8 *filename)
3022
572
{
3023
572
    if (FcConfigGlobsMatch (config->acceptGlobs, filename))
3024
0
  return FcTrue;
3025
572
    if (FcConfigGlobsMatch (config->rejectGlobs, filename))
3026
0
  return FcFalse;
3027
572
    return FcTrue;
3028
572
}
3029
3030
/*
3031
 * Manage font-pattern based font source selectors
3032
 */
3033
3034
FcBool
3035
FcConfigPatternsAdd (FcConfig  *config,
3036
                     FcPattern *pattern,
3037
                     FcBool     accept)
3038
0
{
3039
0
    FcFontSet *set = accept ? config->acceptPatterns : config->rejectPatterns;
3040
3041
0
    return FcFontSetAdd (set, pattern);
3042
0
}
3043
3044
static FcBool
3045
FcConfigPatternsMatch (const FcFontSet *patterns,
3046
                       const FcPattern *font)
3047
968
{
3048
968
    int i;
3049
3050
968
    for (i = 0; i < patterns->nfont; i++)
3051
0
  if (FcListPatternMatchAny (patterns->fonts[i], font))
3052
0
      return FcTrue;
3053
968
    return FcFalse;
3054
968
}
3055
3056
FcBool
3057
FcConfigAcceptFont (FcConfig        *config,
3058
                    const FcPattern *font)
3059
484
{
3060
484
    if (FcConfigPatternsMatch (config->acceptPatterns, font))
3061
0
  return FcTrue;
3062
484
    if (FcConfigPatternsMatch (config->rejectPatterns, font))
3063
0
  return FcFalse;
3064
484
    return FcTrue;
3065
484
}
3066
3067
FcBool
3068
FcConfigAcceptFilter (FcConfig        *config,
3069
                      const FcPattern *font)
3070
484
{
3071
484
    if (config && config->filter_func) {
3072
0
  return config->filter_func (font, config->filter_data);
3073
0
    }
3074
484
    return FcTrue;
3075
484
}
3076
const FcChar8 *
3077
FcConfigGetSysRoot (const FcConfig *config)
3078
319
{
3079
319
    if (!config) {
3080
0
  config = FcConfigGetCurrent();
3081
0
  if (!config)
3082
0
      return NULL;
3083
0
    }
3084
319
    return config->sysRoot;
3085
319
}
3086
3087
void
3088
FcConfigSetSysRoot (FcConfig      *config,
3089
                    const FcChar8 *sysroot)
3090
11
{
3091
11
    FcChar8 *s = NULL;
3092
11
    FcBool   init = FcFalse;
3093
11
    int      nretry = 3;
3094
3095
11
retry:
3096
11
    if (!config) {
3097
  /* We can't use FcConfigGetCurrent() here to ensure
3098
   * the sysroot is set prior to initialize FcConfig,
3099
   * to avoid loading caches from non-sysroot dirs.
3100
   * So postpone the initialization later.
3101
   */
3102
0
  config = fc_atomic_ptr_get (&_fcConfig);
3103
0
  if (!config) {
3104
0
      config = FcConfigCreate();
3105
0
      if (!config)
3106
0
    return;
3107
0
      init = FcTrue;
3108
0
  }
3109
0
    }
3110
3111
11
    if (sysroot) {
3112
0
  s = FcStrRealPath (sysroot);
3113
0
  if (!s)
3114
0
      return;
3115
0
    }
3116
3117
11
    if (config->sysRoot)
3118
0
  FcStrFree (config->sysRoot);
3119
3120
11
    config->sysRoot = s;
3121
11
    if (init) {
3122
0
  config = FcInitLoadOwnConfigAndFonts (config);
3123
0
  if (!config) {
3124
      /* Something failed. this is usually unlikely. so retrying */
3125
0
      init = FcFalse;
3126
0
      if (--nretry == 0) {
3127
0
    fprintf (stderr, "Fontconfig warning: Unable to initialize config and retry limit exceeded. sysroot functionality may not work as expected.\n");
3128
0
    return;
3129
0
      }
3130
0
      goto retry;
3131
0
  }
3132
0
  FcConfigSetCurrent (config);
3133
  /* FcConfigSetCurrent() increases the refcount.
3134
   * decrease it here to avoid the memory leak.
3135
   */
3136
0
  FcConfigDestroy (config);
3137
0
    }
3138
11
}
3139
3140
FcRuleSet *
3141
FcRuleSetCreate (const FcChar8 *name)
3142
44
{
3143
44
    FcRuleSet     *ret = (FcRuleSet *)malloc (sizeof (FcRuleSet));
3144
44
    FcMatchKind    k;
3145
44
    const FcChar8 *p;
3146
3147
44
    if (!name)
3148
0
  p = (const FcChar8 *)"";
3149
44
    else
3150
44
  p = name;
3151
3152
44
    if (ret) {
3153
44
  ret->name = FcStrCopy (p);
3154
44
  ret->description = NULL;
3155
44
  ret->domain = NULL;
3156
176
  for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
3157
132
      ret->subst[k] = FcPtrListCreate (FcDestroyAsRule);
3158
44
  FcRefInit (&ret->ref, 1);
3159
44
    }
3160
3161
44
    return ret;
3162
44
}
3163
3164
void
3165
FcRuleSetDestroy (FcRuleSet *rs)
3166
44
{
3167
44
    FcMatchKind k;
3168
3169
44
    if (!rs)
3170
0
  return;
3171
44
    if (FcRefDec (&rs->ref) != 1)
3172
11
  return;
3173
3174
33
    if (rs->name)
3175
33
  FcStrFree (rs->name);
3176
33
    if (rs->description)
3177
0
  FcStrFree (rs->description);
3178
33
    if (rs->domain)
3179
0
  FcStrFree (rs->domain);
3180
132
    for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
3181
99
  FcPtrListDestroy (rs->subst[k]);
3182
3183
33
    free (rs);
3184
33
}
3185
3186
void
3187
FcRuleSetReference (FcRuleSet *rs)
3188
11
{
3189
11
    if (!FcRefIsConst (&rs->ref))
3190
11
  FcRefInc (&rs->ref);
3191
11
}
3192
3193
void
3194
FcRuleSetEnable (FcRuleSet *rs,
3195
                 FcBool     flag)
3196
44
{
3197
44
    if (rs) {
3198
44
  rs->enabled = flag;
3199
  /* XXX: we may want to provide a feature
3200
   * to enable/disable rulesets through API
3201
   * in the future?
3202
   */
3203
44
    }
3204
44
}
3205
3206
void
3207
FcRuleSetAddDescription (FcRuleSet     *rs,
3208
                         const FcChar8 *domain,
3209
                         const FcChar8 *description)
3210
33
{
3211
33
    if (rs->domain)
3212
0
  FcStrFree (rs->domain);
3213
33
    if (rs->description)
3214
0
  FcStrFree (rs->description);
3215
3216
33
    rs->domain = domain ? FcStrCopy (domain) : NULL;
3217
33
    rs->description = description ? FcStrCopy (description) : NULL;
3218
33
}
3219
3220
int
3221
FcRuleSetAdd (FcRuleSet  *rs,
3222
              FcRule     *rule,
3223
              FcMatchKind kind)
3224
0
{
3225
0
    FcPtrListIter iter;
3226
0
    FcRule       *r;
3227
0
    int           n = 0, ret;
3228
3229
0
    if (!rs ||
3230
0
        kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
3231
0
  return -1;
3232
0
    FcPtrListIterInitAtLast (rs->subst[kind], &iter);
3233
0
    if (!FcPtrListIterAdd (rs->subst[kind], &iter, rule))
3234
0
  return -1;
3235
3236
0
    for (r = rule; r; r = r->next) {
3237
0
  switch (r->type) {
3238
0
  case FcRuleTest:
3239
0
      if (r->u.test) {
3240
0
    if (r->u.test->kind == FcMatchDefault)
3241
0
        r->u.test->kind = kind;
3242
0
    if (n < r->u.test->object)
3243
0
        n = r->u.test->object;
3244
0
      }
3245
0
      break;
3246
0
  case FcRuleEdit:
3247
0
      if (n < r->u.edit->object)
3248
0
    n = r->u.edit->object;
3249
0
      break;
3250
0
  default:
3251
0
      break;
3252
0
  }
3253
0
    }
3254
0
    if (FcDebug() & FC_DBG_EDIT) {
3255
0
  printf ("Add Rule(kind:%d, name: %s) ", kind, rs->name);
3256
0
  FcRulePrint (rule);
3257
0
    }
3258
0
    ret = FC_OBJ_ID (n) - FC_MAX_BASE_OBJECT;
3259
3260
0
    return ret < 0 ? 0 : ret;
3261
0
}
3262
3263
void
3264
FcConfigFileInfoIterInit (FcConfig             *config,
3265
                          FcConfigFileInfoIter *iter)
3266
0
{
3267
0
    FcConfig      *c;
3268
0
    FcPtrListIter *i = (FcPtrListIter *)iter;
3269
3270
0
    if (!config)
3271
0
  c = FcConfigGetCurrent();
3272
0
    else
3273
0
  c = config;
3274
0
    FcPtrListIterInit (c->rulesetList, i);
3275
0
}
3276
3277
FcBool
3278
FcConfigFileInfoIterNext (FcConfig             *config,
3279
                          FcConfigFileInfoIter *iter)
3280
0
{
3281
0
    FcConfig      *c;
3282
0
    FcPtrListIter *i = (FcPtrListIter *)iter;
3283
3284
0
    if (!config)
3285
0
  c = FcConfigGetCurrent();
3286
0
    else
3287
0
  c = config;
3288
0
    if (FcPtrListIterIsValid (c->rulesetList, i)) {
3289
0
  FcPtrListIterNext (c->rulesetList, i);
3290
0
    } else
3291
0
  return FcFalse;
3292
3293
0
    return FcTrue;
3294
0
}
3295
3296
FcBool
3297
FcConfigFileInfoIterGet (FcConfig             *config,
3298
                         FcConfigFileInfoIter *iter,
3299
                         FcChar8             **name,
3300
                         FcChar8             **description,
3301
                         FcBool               *enabled)
3302
0
{
3303
0
    FcConfig      *c;
3304
0
    FcRuleSet     *r;
3305
0
    FcPtrListIter *i = (FcPtrListIter *)iter;
3306
3307
0
    if (!config)
3308
0
  c = FcConfigGetCurrent();
3309
0
    else
3310
0
  c = config;
3311
0
    if (!FcPtrListIterIsValid (c->rulesetList, i))
3312
0
  return FcFalse;
3313
0
    r = FcPtrListIterGetValue (c->rulesetList, i);
3314
0
    if (name)
3315
0
  *name = FcStrCopy (r->name && r->name[0] ? r->name : (const FcChar8 *)"fonts.conf");
3316
0
    if (description)
3317
0
  *description = FcStrCopy ((const FcChar8 *)(!r->description ? _ ("No description") : dgettext (r->domain ? (const char *)r->domain : GETTEXT_PACKAGE "-conf", (const char *)r->description)));
3318
0
    if (enabled)
3319
0
  *enabled = r->enabled;
3320
3321
0
    return FcTrue;
3322
0
}
3323
3324
#define __fccfg__
3325
#include "fcaliastail.h"
3326
#undef __fccfg__