Coverage Report

Created: 2026-01-25 07:18

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