Coverage Report

Created: 2026-08-14 10:22

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