Coverage Report

Created: 2025-07-07 10:01

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