Coverage Report

Created: 2026-06-30 11:14

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