fccfg.c revision ca08ab68
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#include "fcint.h"
26#include <dirent.h>
27#include <sys/types.h>
28
29#if defined (_WIN32) && (defined (PIC) || defined (DLL_EXPORT))
30#define STRICT
31#include <windows.h>
32#undef STRICT
33#endif
34
35#if defined (_WIN32) && !defined (R_OK)
36#define R_OK 4
37#endif
38
39FcConfig    *_fcConfig;
40
41FcConfig *
42FcConfigCreate (void)
43{
44    FcSetName	set;
45    FcConfig	*config;
46
47    config = malloc (sizeof (FcConfig));
48    if (!config)
49	goto bail0;
50    FcMemAlloc (FC_MEM_CONFIG, sizeof (FcConfig));
51
52    config->configDirs = FcStrSetCreate ();
53    if (!config->configDirs)
54	goto bail1;
55
56    config->configFiles = FcStrSetCreate ();
57    if (!config->configFiles)
58	goto bail2;
59
60    config->fontDirs = FcStrSetCreate ();
61    if (!config->fontDirs)
62	goto bail3;
63
64    config->acceptGlobs = FcStrSetCreate ();
65    if (!config->acceptGlobs)
66	goto bail4;
67
68    config->rejectGlobs = FcStrSetCreate ();
69    if (!config->rejectGlobs)
70	goto bail5;
71
72    config->acceptPatterns = FcFontSetCreate ();
73    if (!config->acceptPatterns)
74	goto bail6;
75
76    config->rejectPatterns = FcFontSetCreate ();
77    if (!config->rejectPatterns)
78	goto bail7;
79
80    config->cacheDirs = FcStrSetCreate ();
81    if (!config->cacheDirs)
82	goto bail8;
83
84    config->blanks = 0;
85
86    config->substPattern = 0;
87    config->substFont = 0;
88    config->substScan = 0;
89    config->maxObjects = 0;
90    for (set = FcSetSystem; set <= FcSetApplication; set++)
91	config->fonts[set] = 0;
92
93    config->rescanTime = time(0);
94    config->rescanInterval = 30;
95
96    config->expr_pool = NULL;
97
98    config->ref = 1;
99
100    return config;
101
102bail8:
103    FcFontSetDestroy (config->rejectPatterns);
104bail7:
105    FcFontSetDestroy (config->acceptPatterns);
106bail6:
107    FcStrSetDestroy (config->rejectGlobs);
108bail5:
109    FcStrSetDestroy (config->acceptGlobs);
110bail4:
111    FcStrSetDestroy (config->fontDirs);
112bail3:
113    FcStrSetDestroy (config->configFiles);
114bail2:
115    FcStrSetDestroy (config->configDirs);
116bail1:
117    free (config);
118    FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
119bail0:
120    return 0;
121}
122
123static FcFileTime
124FcConfigNewestFile (FcStrSet *files)
125{
126    FcStrList	    *list = FcStrListCreate (files);
127    FcFileTime	    newest = { 0, FcFalse };
128    FcChar8	    *file;
129    struct  stat    statb;
130
131    if (list)
132    {
133	while ((file = FcStrListNext (list)))
134	    if (FcStat (file, &statb) == 0)
135		if (!newest.set || statb.st_mtime - newest.time > 0)
136		{
137		    newest.set = FcTrue;
138		    newest.time = statb.st_mtime;
139		}
140	FcStrListDone (list);
141    }
142    return newest;
143}
144
145FcBool
146FcConfigUptoDate (FcConfig *config)
147{
148    FcFileTime	config_time, config_dir_time, font_time;
149    time_t	now = time(0);
150    if (!config)
151    {
152	config = FcConfigGetCurrent ();
153	if (!config)
154	    return FcFalse;
155    }
156    config_time = FcConfigNewestFile (config->configFiles);
157    config_dir_time = FcConfigNewestFile (config->configDirs);
158    font_time = FcConfigNewestFile (config->fontDirs);
159    if ((config_time.set && config_time.time - config->rescanTime > 0) ||
160	(config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) ||
161	(font_time.set && (font_time.time - config->rescanTime) > 0))
162    {
163	/* We need to check for potential clock problems here (OLPC ticket #6046) */
164	if ((config_time.set && (config_time.time - now) > 0) ||
165    	(config_dir_time.set && (config_dir_time.time - now) > 0) ||
166        (font_time.set && (font_time.time - now) > 0))
167	{
168	    fprintf (stderr,
169                    "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected\n");
170	    config->rescanTime = now;
171	    return FcTrue;
172	}
173	else
174	    return FcFalse;
175    }
176    config->rescanTime = now;
177    return FcTrue;
178}
179
180static void
181FcSubstDestroy (FcSubst *s)
182{
183    FcSubst *n;
184
185    while (s)
186    {
187	n = s->next;
188	if (s->test)
189	    FcTestDestroy (s->test);
190	if (s->edit)
191	    FcEditDestroy (s->edit);
192	free (s);
193	FcMemFree (FC_MEM_SUBST, sizeof (FcSubst));
194	s = n;
195    }
196}
197
198FcExpr *
199FcConfigAllocExpr (FcConfig *config)
200{
201  if (!config->expr_pool || config->expr_pool->next == config->expr_pool->end)
202  {
203    FcExprPage *new_page;
204
205    new_page = malloc (sizeof (FcExprPage));
206    if (!new_page)
207      return 0;
208    FcMemAlloc (FC_MEM_EXPR, sizeof (FcExprPage));
209
210    new_page->next_page = config->expr_pool;
211    new_page->next = new_page->exprs;
212    config->expr_pool = new_page;
213  }
214
215  return config->expr_pool->next++;
216}
217
218FcConfig *
219FcConfigReference (FcConfig *config)
220{
221    if (!config)
222    {
223	config = FcConfigGetCurrent ();
224	if (!config)
225	    return 0;
226    }
227
228    config->ref++;
229
230    return config;
231}
232
233void
234FcConfigDestroy (FcConfig *config)
235{
236    FcSetName	set;
237    FcExprPage	*page;
238
239    if (--config->ref > 0)
240	return;
241
242    if (config == _fcConfig)
243	_fcConfig = 0;
244
245    FcStrSetDestroy (config->configDirs);
246    FcStrSetDestroy (config->fontDirs);
247    FcStrSetDestroy (config->cacheDirs);
248    FcStrSetDestroy (config->configFiles);
249    FcStrSetDestroy (config->acceptGlobs);
250    FcStrSetDestroy (config->rejectGlobs);
251    FcFontSetDestroy (config->acceptPatterns);
252    FcFontSetDestroy (config->rejectPatterns);
253
254    if (config->blanks)
255	FcBlanksDestroy (config->blanks);
256
257    FcSubstDestroy (config->substPattern);
258    FcSubstDestroy (config->substFont);
259    FcSubstDestroy (config->substScan);
260    for (set = FcSetSystem; set <= FcSetApplication; set++)
261	if (config->fonts[set])
262	    FcFontSetDestroy (config->fonts[set]);
263
264    page = config->expr_pool;
265    while (page)
266    {
267      FcExprPage *next = page->next_page;
268      FcMemFree (FC_MEM_EXPR, sizeof (FcExprPage));
269      free (page);
270      page = next;
271    }
272
273    free (config);
274    FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
275}
276
277/*
278 * Add cache to configuration, adding fonts and directories
279 */
280
281FcBool
282FcConfigAddCache (FcConfig *config, FcCache *cache,
283		  FcSetName set, FcStrSet *dirSet)
284{
285    FcFontSet	*fs;
286    intptr_t	*dirs;
287    int		i;
288
289    /*
290     * Add fonts
291     */
292    fs = FcCacheSet (cache);
293    if (fs)
294    {
295	int	nref = 0;
296
297	for (i = 0; i < fs->nfont; i++)
298	{
299	    FcPattern	*font = FcFontSetFont (fs, i);
300	    FcChar8	*font_file;
301
302	    /*
303	     * Check to see if font is banned by filename
304	     */
305	    if (FcPatternObjectGetString (font, FC_FILE_OBJECT,
306					  0, &font_file) == FcResultMatch &&
307		!FcConfigAcceptFilename (config, font_file))
308	    {
309		continue;
310	    }
311
312	    /*
313	     * Check to see if font is banned by pattern
314	     */
315	    if (!FcConfigAcceptFont (config, font))
316		continue;
317
318	    nref++;
319	    FcFontSetAdd (config->fonts[set], font);
320	}
321	FcDirCacheReference (cache, nref);
322    }
323
324    /*
325     * Add directories
326     */
327    dirs = FcCacheDirs (cache);
328    if (dirs)
329    {
330	for (i = 0; i < cache->dirs_count; i++)
331	{
332	    FcChar8	*dir = FcOffsetToPtr (dirs, dirs[i], FcChar8);
333	    if (FcConfigAcceptFilename (config, dir))
334		FcStrSetAddFilename (dirSet, dir);
335	}
336    }
337    return FcTrue;
338}
339
340static FcBool
341FcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet)
342{
343    FcStrList	    *dirlist;
344    FcChar8	    *dir;
345    FcCache	    *cache;
346
347    dirlist = FcStrListCreate (dirSet);
348    if (!dirlist)
349        return FcFalse;
350
351    while ((dir = FcStrListNext (dirlist)))
352    {
353	if (FcDebug () & FC_DBG_FONTSET)
354	    printf ("adding fonts from%s\n", dir);
355	cache = FcDirCacheRead (dir, FcFalse, config);
356	if (!cache)
357	    continue;
358	FcConfigAddCache (config, cache, set, dirSet);
359	FcDirCacheUnload (cache);
360    }
361    FcStrListDone (dirlist);
362    return FcTrue;
363}
364
365/*
366 * Scan the current list of directories in the configuration
367 * and build the set of available fonts.
368 */
369
370FcBool
371FcConfigBuildFonts (FcConfig *config)
372{
373    FcFontSet	    *fonts;
374
375    if (!config)
376    {
377	config = FcConfigGetCurrent ();
378	if (!config)
379	    return FcFalse;
380    }
381
382    fonts = FcFontSetCreate ();
383    if (!fonts)
384	return FcFalse;
385
386    FcConfigSetFonts (config, fonts, FcSetSystem);
387
388    if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs))
389	return FcFalse;
390    if (FcDebug () & FC_DBG_FONTSET)
391	FcFontSetPrint (fonts);
392    return FcTrue;
393}
394
395FcBool
396FcConfigSetCurrent (FcConfig *config)
397{
398    if (config == _fcConfig)
399	return FcTrue;
400
401    if (!config->fonts)
402	if (!FcConfigBuildFonts (config))
403	    return FcFalse;
404
405    if (_fcConfig)
406	FcConfigDestroy (_fcConfig);
407    _fcConfig = config;
408    return FcTrue;
409}
410
411FcConfig *
412FcConfigGetCurrent (void)
413{
414    if (!_fcConfig)
415	if (!FcInit ())
416	    return 0;
417    return _fcConfig;
418}
419
420FcBool
421FcConfigAddConfigDir (FcConfig	    *config,
422		      const FcChar8 *d)
423{
424    return FcStrSetAddFilename (config->configDirs, d);
425}
426
427FcStrList *
428FcConfigGetConfigDirs (FcConfig   *config)
429{
430    if (!config)
431    {
432	config = FcConfigGetCurrent ();
433	if (!config)
434	    return 0;
435    }
436    return FcStrListCreate (config->configDirs);
437}
438
439FcBool
440FcConfigAddFontDir (FcConfig	    *config,
441		    const FcChar8   *d)
442{
443    return FcStrSetAddFilename (config->fontDirs, d);
444}
445
446FcBool
447FcConfigAddDir (FcConfig	    *config,
448		const FcChar8	    *d)
449{
450    return (FcConfigAddConfigDir (config, d) &&
451	    FcConfigAddFontDir (config, d));
452}
453
454FcStrList *
455FcConfigGetFontDirs (FcConfig	*config)
456{
457    if (!config)
458    {
459	config = FcConfigGetCurrent ();
460	if (!config)
461	    return 0;
462    }
463    return FcStrListCreate (config->fontDirs);
464}
465
466FcBool
467FcConfigAddCacheDir (FcConfig	    *config,
468		     const FcChar8  *d)
469{
470    return FcStrSetAddFilename (config->cacheDirs, d);
471}
472
473FcStrList *
474FcConfigGetCacheDirs (const FcConfig *config)
475{
476    if (!config)
477    {
478	config = FcConfigGetCurrent ();
479	if (!config)
480	    return 0;
481    }
482    return FcStrListCreate (config->cacheDirs);
483}
484
485FcBool
486FcConfigAddConfigFile (FcConfig	    *config,
487		       const FcChar8   *f)
488{
489    FcBool	ret;
490    FcChar8	*file = FcConfigFilename (f);
491
492    if (!file)
493	return FcFalse;
494
495    ret = FcStrSetAdd (config->configFiles, file);
496    FcStrFree (file);
497    return ret;
498}
499
500FcStrList *
501FcConfigGetConfigFiles (FcConfig    *config)
502{
503    if (!config)
504    {
505	config = FcConfigGetCurrent ();
506	if (!config)
507	    return 0;
508    }
509    return FcStrListCreate (config->configFiles);
510}
511
512FcChar8 *
513FcConfigGetCache (FcConfig  *config)
514{
515    return NULL;
516}
517
518FcFontSet *
519FcConfigGetFonts (FcConfig	*config,
520		  FcSetName	set)
521{
522    if (!config)
523    {
524	config = FcConfigGetCurrent ();
525	if (!config)
526	    return 0;
527    }
528    return config->fonts[set];
529}
530
531void
532FcConfigSetFonts (FcConfig	*config,
533		  FcFontSet	*fonts,
534		  FcSetName	set)
535{
536    if (config->fonts[set])
537	FcFontSetDestroy (config->fonts[set]);
538    config->fonts[set] = fonts;
539}
540
541FcBlanks *
542FcConfigGetBlanks (FcConfig	*config)
543{
544    if (!config)
545    {
546	config = FcConfigGetCurrent ();
547	if (!config)
548	    return 0;
549    }
550    return config->blanks;
551}
552
553FcBool
554FcConfigAddBlank (FcConfig	*config,
555		  FcChar32    	blank)
556{
557    FcBlanks	*b, *freeme = 0;
558
559    b = config->blanks;
560    if (!b)
561    {
562	freeme = b = FcBlanksCreate ();
563	if (!b)
564	    return FcFalse;
565    }
566    if (!FcBlanksAdd (b, blank))
567    {
568        if (freeme)
569            FcBlanksDestroy (freeme);
570	return FcFalse;
571    }
572    config->blanks = b;
573    return FcTrue;
574}
575
576int
577FcConfigGetRescanInterval (FcConfig *config)
578{
579    if (!config)
580    {
581	config = FcConfigGetCurrent ();
582	if (!config)
583	    return 0;
584    }
585    return config->rescanInterval;
586}
587
588FcBool
589FcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
590{
591    if (!config)
592    {
593	config = FcConfigGetCurrent ();
594	if (!config)
595	    return FcFalse;
596    }
597    config->rescanInterval = rescanInterval;
598    return FcTrue;
599}
600
601/*
602 * A couple of typos escaped into the library
603 */
604int
605FcConfigGetRescanInverval (FcConfig *config)
606{
607    return FcConfigGetRescanInterval (config);
608}
609
610FcBool
611FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
612{
613    return FcConfigSetRescanInterval (config, rescanInterval);
614}
615
616
617FcBool
618FcConfigAddEdit (FcConfig	*config,
619		 FcTest		*test,
620		 FcEdit		*edit,
621		 FcMatchKind	kind)
622{
623    FcSubst	*subst, **prev;
624    FcTest	*t;
625    int		num;
626
627    switch (kind) {
628    case FcMatchPattern:
629	prev = &config->substPattern;
630	break;
631    case FcMatchFont:
632	prev = &config->substFont;
633	break;
634    case FcMatchScan:
635	prev = &config->substScan;
636	break;
637    default:
638	return FcFalse;
639    }
640    subst = (FcSubst *) malloc (sizeof (FcSubst));
641    if (!subst)
642	return FcFalse;
643    FcMemAlloc (FC_MEM_SUBST, sizeof (FcSubst));
644    for (; *prev; prev = &(*prev)->next);
645    *prev = subst;
646    subst->next = 0;
647    subst->test = test;
648    subst->edit = edit;
649    num = 0;
650    for (t = test; t; t = t->next)
651    {
652	if (t->kind == FcMatchDefault)
653	    t->kind = kind;
654	num++;
655    }
656    if (config->maxObjects < num)
657	config->maxObjects = num;
658    if (FcDebug () & FC_DBG_EDIT)
659    {
660	printf ("Add Subst ");
661	FcSubstPrint (subst);
662    }
663    return FcTrue;
664}
665
666typedef struct _FcSubState {
667    FcPatternElt   *elt;
668    FcValueList    *value;
669} FcSubState;
670
671static FcValue
672FcConfigPromote (FcValue v, FcValue u)
673{
674    if (v.type == FcTypeInteger)
675    {
676	v.type = FcTypeDouble;
677	v.u.d = (double) v.u.i;
678    }
679    else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
680    {
681	v.u.m = &FcIdentityMatrix;
682	v.type = FcTypeMatrix;
683    }
684    else if (v.type == FcTypeString && u.type == FcTypeLangSet)
685    {
686	v.u.l = FcLangSetPromote (v.u.s);
687	v.type = FcTypeLangSet;
688    }
689    return v;
690}
691
692FcBool
693FcConfigCompareValue (const FcValue	*left_o,
694		      FcOp		op_,
695		      const FcValue	*right_o)
696{
697    FcValue	left = FcValueCanonicalize(left_o);
698    FcValue	right = FcValueCanonicalize(right_o);
699    FcBool	ret = FcFalse;
700    FcOp	op = FC_OP_GET_OP (op_);
701    int		flags = FC_OP_GET_FLAGS (op_);
702
703    left = FcConfigPromote (left, right);
704    right = FcConfigPromote (right, left);
705    if (left.type == right.type)
706    {
707	switch (left.type) {
708	case FcTypeInteger:
709	    break;	/* FcConfigPromote prevents this from happening */
710	case FcTypeDouble:
711	    switch (op) {
712	    case FcOpEqual:
713	    case FcOpContains:
714	    case FcOpListing:
715		ret = left.u.d == right.u.d;
716		break;
717	    case FcOpNotEqual:
718	    case FcOpNotContains:
719		ret = left.u.d != right.u.d;
720		break;
721	    case FcOpLess:
722		ret = left.u.d < right.u.d;
723		break;
724	    case FcOpLessEqual:
725		ret = left.u.d <= right.u.d;
726		break;
727	    case FcOpMore:
728		ret = left.u.d > right.u.d;
729		break;
730	    case FcOpMoreEqual:
731		ret = left.u.d >= right.u.d;
732		break;
733	    default:
734		break;
735	    }
736	    break;
737	case FcTypeBool:
738	    switch (op) {
739	    case FcOpEqual:
740	    case FcOpContains:
741	    case FcOpListing:
742		ret = left.u.b == right.u.b;
743		break;
744	    case FcOpNotEqual:
745	    case FcOpNotContains:
746		ret = left.u.b != right.u.b;
747		break;
748	    default:
749		break;
750	    }
751	    break;
752	case FcTypeString:
753	    switch (op) {
754	    case FcOpEqual:
755	    case FcOpListing:
756		if (flags & FcOpFlagIgnoreBlanks)
757		    ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) == 0;
758		else
759		    ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
760		break;
761	    case FcOpContains:
762		ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
763		break;
764	    case FcOpNotEqual:
765		if (flags & FcOpFlagIgnoreBlanks)
766		    ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) != 0;
767		else
768		    ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
769		break;
770	    case FcOpNotContains:
771		ret = FcStrStrIgnoreCase (left.u.s, right.u.s) == 0;
772		break;
773	    default:
774		break;
775	    }
776	    break;
777	case FcTypeMatrix:
778	    switch (op) {
779	    case FcOpEqual:
780	    case FcOpContains:
781	    case FcOpListing:
782		ret = FcMatrixEqual (left.u.m, right.u.m);
783		break;
784	    case FcOpNotEqual:
785	    case FcOpNotContains:
786		ret = !FcMatrixEqual (left.u.m, right.u.m);
787		break;
788	    default:
789		break;
790	    }
791	    break;
792	case FcTypeCharSet:
793	    switch (op) {
794	    case FcOpContains:
795	    case FcOpListing:
796		/* left contains right if right is a subset of left */
797		ret = FcCharSetIsSubset (right.u.c, left.u.c);
798		break;
799	    case FcOpNotContains:
800		/* left contains right if right is a subset of left */
801		ret = !FcCharSetIsSubset (right.u.c, left.u.c);
802		break;
803	    case FcOpEqual:
804		ret = FcCharSetEqual (left.u.c, right.u.c);
805		break;
806	    case FcOpNotEqual:
807		ret = !FcCharSetEqual (left.u.c, right.u.c);
808		break;
809	    default:
810		break;
811	    }
812	    break;
813	case FcTypeLangSet:
814	    switch (op) {
815	    case FcOpContains:
816	    case FcOpListing:
817		ret = FcLangSetContains (left.u.l, right.u.l);
818		break;
819	    case FcOpNotContains:
820		ret = !FcLangSetContains (left.u.l, right.u.l);
821		break;
822	    case FcOpEqual:
823		ret = FcLangSetEqual (left.u.l, right.u.l);
824		break;
825	    case FcOpNotEqual:
826		ret = !FcLangSetEqual (left.u.l, right.u.l);
827		break;
828	    default:
829		break;
830	    }
831	    break;
832	case FcTypeVoid:
833	    switch (op) {
834	    case FcOpEqual:
835	    case FcOpContains:
836	    case FcOpListing:
837		ret = FcTrue;
838		break;
839	    default:
840		break;
841	    }
842	    break;
843	case FcTypeFTFace:
844	    switch (op) {
845	    case FcOpEqual:
846	    case FcOpContains:
847	    case FcOpListing:
848		ret = left.u.f == right.u.f;
849		break;
850	    case FcOpNotEqual:
851	    case FcOpNotContains:
852		ret = left.u.f != right.u.f;
853		break;
854	    default:
855		break;
856	    }
857	    break;
858	}
859    }
860    else
861    {
862	if (op == FcOpNotEqual || op == FcOpNotContains)
863	    ret = FcTrue;
864    }
865    return ret;
866}
867
868
869#define _FcDoubleFloor(d)	((int) (d))
870#define _FcDoubleCeil(d)	((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
871#define FcDoubleFloor(d)	((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
872#define FcDoubleCeil(d)		((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
873#define FcDoubleRound(d)	FcDoubleFloor ((d) + 0.5)
874#define FcDoubleTrunc(d)	((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
875
876static FcValue
877FcConfigEvaluate (FcPattern *p, FcExpr *e)
878{
879    FcValue	v, vl, vr;
880    FcResult	r;
881    FcMatrix	*m;
882    FcChar8     *str;
883    FcOp	op = FC_OP_GET_OP (e->op);
884
885    switch (op) {
886    case FcOpInteger:
887	v.type = FcTypeInteger;
888	v.u.i = e->u.ival;
889	break;
890    case FcOpDouble:
891	v.type = FcTypeDouble;
892	v.u.d = e->u.dval;
893	break;
894    case FcOpString:
895	v.type = FcTypeString;
896	v.u.s = e->u.sval;
897	v = FcValueSave (v);
898	break;
899    case FcOpMatrix:
900	v.type = FcTypeMatrix;
901	v.u.m = e->u.mval;
902	v = FcValueSave (v);
903	break;
904    case FcOpCharSet:
905	v.type = FcTypeCharSet;
906	v.u.c = e->u.cval;
907	v = FcValueSave (v);
908	break;
909    case FcOpLangSet:
910	v.type = FcTypeLangSet;
911	v.u.l = e->u.lval;
912	v = FcValueSave (v);
913	break;
914    case FcOpBool:
915	v.type = FcTypeBool;
916	v.u.b = e->u.bval;
917	break;
918    case FcOpField:
919	r = FcPatternObjectGet (p, e->u.object, 0, &v);
920	if (r != FcResultMatch)
921	    v.type = FcTypeVoid;
922	v = FcValueSave (v);
923	break;
924    case FcOpConst:
925	if (FcNameConstant (e->u.constant, &v.u.i))
926	    v.type = FcTypeInteger;
927	else
928	    v.type = FcTypeVoid;
929	break;
930    case FcOpQuest:
931	vl = FcConfigEvaluate (p, e->u.tree.left);
932	if (vl.type == FcTypeBool)
933	{
934	    if (vl.u.b)
935		v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
936	    else
937		v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
938	}
939	else
940	    v.type = FcTypeVoid;
941	FcValueDestroy (vl);
942	break;
943    case FcOpEqual:
944    case FcOpNotEqual:
945    case FcOpLess:
946    case FcOpLessEqual:
947    case FcOpMore:
948    case FcOpMoreEqual:
949    case FcOpContains:
950    case FcOpNotContains:
951    case FcOpListing:
952	vl = FcConfigEvaluate (p, e->u.tree.left);
953	vr = FcConfigEvaluate (p, e->u.tree.right);
954	v.type = FcTypeBool;
955	v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
956	FcValueDestroy (vl);
957	FcValueDestroy (vr);
958	break;
959    case FcOpOr:
960    case FcOpAnd:
961    case FcOpPlus:
962    case FcOpMinus:
963    case FcOpTimes:
964    case FcOpDivide:
965	vl = FcConfigEvaluate (p, e->u.tree.left);
966	vr = FcConfigEvaluate (p, e->u.tree.right);
967	vl = FcConfigPromote (vl, vr);
968	vr = FcConfigPromote (vr, vl);
969	if (vl.type == vr.type)
970	{
971	    switch (vl.type) {
972	    case FcTypeDouble:
973		switch (op) {
974		case FcOpPlus:
975		    v.type = FcTypeDouble;
976		    v.u.d = vl.u.d + vr.u.d;
977		    break;
978		case FcOpMinus:
979		    v.type = FcTypeDouble;
980		    v.u.d = vl.u.d - vr.u.d;
981		    break;
982		case FcOpTimes:
983		    v.type = FcTypeDouble;
984		    v.u.d = vl.u.d * vr.u.d;
985		    break;
986		case FcOpDivide:
987		    v.type = FcTypeDouble;
988		    v.u.d = vl.u.d / vr.u.d;
989		    break;
990		default:
991		    v.type = FcTypeVoid;
992		    break;
993		}
994		if (v.type == FcTypeDouble &&
995		    v.u.d == (double) (int) v.u.d)
996		{
997		    v.type = FcTypeInteger;
998		    v.u.i = (int) v.u.d;
999		}
1000		break;
1001	    case FcTypeBool:
1002		switch (op) {
1003		case FcOpOr:
1004		    v.type = FcTypeBool;
1005		    v.u.b = vl.u.b || vr.u.b;
1006		    break;
1007		case FcOpAnd:
1008		    v.type = FcTypeBool;
1009		    v.u.b = vl.u.b && vr.u.b;
1010		    break;
1011		default:
1012		    v.type = FcTypeVoid;
1013		    break;
1014		}
1015		break;
1016	    case FcTypeString:
1017		switch (op) {
1018		case FcOpPlus:
1019		    v.type = FcTypeString;
1020		    str = FcStrPlus (vl.u.s, vr.u.s);
1021		    v.u.s = FcSharedStr (str);
1022		    FcStrFree (str);
1023
1024		    if (!v.u.s)
1025			v.type = FcTypeVoid;
1026		    break;
1027		default:
1028		    v.type = FcTypeVoid;
1029		    break;
1030		}
1031		break;
1032	    case FcTypeMatrix:
1033		switch (op) {
1034		case FcOpTimes:
1035		    v.type = FcTypeMatrix;
1036		    m = malloc (sizeof (FcMatrix));
1037		    if (m)
1038		    {
1039			FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
1040			FcMatrixMultiply (m, vl.u.m, vr.u.m);
1041			v.u.m = m;
1042		    }
1043		    else
1044		    {
1045			v.type = FcTypeVoid;
1046		    }
1047		    break;
1048		default:
1049		    v.type = FcTypeVoid;
1050		    break;
1051		}
1052		break;
1053	    case FcTypeCharSet:
1054		switch (op) {
1055		case FcOpPlus:
1056		    v.type = FcTypeCharSet;
1057		    v.u.c = FcCharSetUnion (vl.u.c, vr.u.c);
1058		    if (!v.u.c)
1059			v.type = FcTypeVoid;
1060		    break;
1061		case FcOpMinus:
1062		    v.type = FcTypeCharSet;
1063		    v.u.c = FcCharSetSubtract (vl.u.c, vr.u.c);
1064		    if (!v.u.c)
1065			v.type = FcTypeVoid;
1066		    break;
1067		default:
1068		    v.type = FcTypeVoid;
1069		    break;
1070		}
1071		break;
1072	    case FcTypeLangSet:
1073		switch (op) {
1074		case FcOpPlus:
1075		    v.type = FcTypeLangSet;
1076		    v.u.l = FcLangSetUnion (vl.u.l, vr.u.l);
1077		    if (!v.u.l)
1078			v.type = FcTypeVoid;
1079		    break;
1080		case FcOpMinus:
1081		    v.type = FcTypeLangSet;
1082		    v.u.l = FcLangSetSubtract (vl.u.l, vr.u.l);
1083		    if (!v.u.l)
1084			v.type = FcTypeVoid;
1085		    break;
1086		default:
1087		    v.type = FcTypeVoid;
1088		    break;
1089		}
1090		break;
1091	    default:
1092		v.type = FcTypeVoid;
1093		break;
1094	    }
1095	}
1096	else
1097	    v.type = FcTypeVoid;
1098	FcValueDestroy (vl);
1099	FcValueDestroy (vr);
1100	break;
1101    case FcOpNot:
1102	vl = FcConfigEvaluate (p, e->u.tree.left);
1103	switch (vl.type) {
1104	case FcTypeBool:
1105	    v.type = FcTypeBool;
1106	    v.u.b = !vl.u.b;
1107	    break;
1108	default:
1109	    v.type = FcTypeVoid;
1110	    break;
1111	}
1112	FcValueDestroy (vl);
1113	break;
1114    case FcOpFloor:
1115	vl = FcConfigEvaluate (p, e->u.tree.left);
1116	switch (vl.type) {
1117	case FcTypeInteger:
1118	    v = vl;
1119	    break;
1120	case FcTypeDouble:
1121	    v.type = FcTypeInteger;
1122	    v.u.i = FcDoubleFloor (vl.u.d);
1123	    break;
1124	default:
1125	    v.type = FcTypeVoid;
1126	    break;
1127	}
1128	FcValueDestroy (vl);
1129	break;
1130    case FcOpCeil:
1131	vl = FcConfigEvaluate (p, e->u.tree.left);
1132	switch (vl.type) {
1133	case FcTypeInteger:
1134	    v = vl;
1135	    break;
1136	case FcTypeDouble:
1137	    v.type = FcTypeInteger;
1138	    v.u.i = FcDoubleCeil (vl.u.d);
1139	    break;
1140	default:
1141	    v.type = FcTypeVoid;
1142	    break;
1143	}
1144	FcValueDestroy (vl);
1145	break;
1146    case FcOpRound:
1147	vl = FcConfigEvaluate (p, e->u.tree.left);
1148	switch (vl.type) {
1149	case FcTypeInteger:
1150	    v = vl;
1151	    break;
1152	case FcTypeDouble:
1153	    v.type = FcTypeInteger;
1154	    v.u.i = FcDoubleRound (vl.u.d);
1155	    break;
1156	default:
1157	    v.type = FcTypeVoid;
1158	    break;
1159	}
1160	FcValueDestroy (vl);
1161	break;
1162    case FcOpTrunc:
1163	vl = FcConfigEvaluate (p, e->u.tree.left);
1164	switch (vl.type) {
1165	case FcTypeInteger:
1166	    v = vl;
1167	    break;
1168	case FcTypeDouble:
1169	    v.type = FcTypeInteger;
1170	    v.u.i = FcDoubleTrunc (vl.u.d);
1171	    break;
1172	default:
1173	    v.type = FcTypeVoid;
1174	    break;
1175	}
1176	FcValueDestroy (vl);
1177	break;
1178    default:
1179	v.type = FcTypeVoid;
1180	break;
1181    }
1182    return v;
1183}
1184
1185static FcValueList *
1186FcConfigMatchValueList (FcPattern	*p,
1187			FcTest		*t,
1188			FcValueList	*values)
1189{
1190    FcValueList	    *ret = 0;
1191    FcExpr	    *e = t->expr;
1192    FcValue	    value;
1193    FcValueList	    *v;
1194
1195    while (e)
1196    {
1197	/* Compute the value of the match expression */
1198	if (FC_OP_GET_OP (e->op) == FcOpComma)
1199	{
1200	    value = FcConfigEvaluate (p, e->u.tree.left);
1201	    e = e->u.tree.right;
1202	}
1203	else
1204	{
1205	    value = FcConfigEvaluate (p, e);
1206	    e = 0;
1207	}
1208
1209	for (v = values; v; v = FcValueListNext(v))
1210	{
1211	    /* Compare the pattern value to the match expression value */
1212	    if (FcConfigCompareValue (&v->value, t->op, &value))
1213	    {
1214		if (!ret)
1215		    ret = v;
1216	    }
1217	    else
1218	    {
1219		if (t->qual == FcQualAll)
1220		{
1221		    ret = 0;
1222		    break;
1223		}
1224	    }
1225	}
1226	FcValueDestroy (value);
1227    }
1228    return ret;
1229}
1230
1231static FcValueList *
1232FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
1233{
1234    FcValueList	*l;
1235
1236    if (!e)
1237	return 0;
1238    l = (FcValueList *) malloc (sizeof (FcValueList));
1239    if (!l)
1240	return 0;
1241    FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
1242    if (FC_OP_GET_OP (e->op) == FcOpComma)
1243    {
1244	l->value = FcConfigEvaluate (p, e->u.tree.left);
1245	l->next = FcConfigValues (p, e->u.tree.right, binding);
1246    }
1247    else
1248    {
1249	l->value = FcConfigEvaluate (p, e);
1250	l->next = NULL;
1251    }
1252    l->binding = binding;
1253    if (l->value.type == FcTypeVoid)
1254    {
1255	FcValueList  *next = FcValueListNext(l);
1256
1257	FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
1258	free (l);
1259	l = next;
1260    }
1261
1262    return l;
1263}
1264
1265static FcBool
1266FcConfigAdd (FcValueListPtr *head,
1267	     FcValueList    *position,
1268	     FcBool	    append,
1269	     FcValueList    *new)
1270{
1271    FcValueListPtr  *prev, last, v;
1272    FcValueBinding  sameBinding;
1273
1274    if (position)
1275	sameBinding = position->binding;
1276    else
1277	sameBinding = FcValueBindingWeak;
1278    for (v = new; v != NULL; v = FcValueListNext(v))
1279	if (v->binding == FcValueBindingSame)
1280	    v->binding = sameBinding;
1281    if (append)
1282    {
1283	if (position)
1284	    prev = &position->next;
1285	else
1286	    for (prev = head; *prev != NULL;
1287		 prev = &(*prev)->next)
1288		;
1289    }
1290    else
1291    {
1292	if (position)
1293	{
1294	    for (prev = head; *prev != NULL;
1295		 prev = &(*prev)->next)
1296	    {
1297		if (*prev == position)
1298		    break;
1299	    }
1300	}
1301	else
1302	    prev = head;
1303
1304	if (FcDebug () & FC_DBG_EDIT)
1305	{
1306	    if (*prev == NULL)
1307		printf ("position not on list\n");
1308	}
1309    }
1310
1311    if (FcDebug () & FC_DBG_EDIT)
1312    {
1313	printf ("%s list before ", append ? "Append" : "Prepend");
1314	FcValueListPrintWithPosition (*head, *prev);
1315	printf ("\n");
1316    }
1317
1318    if (new)
1319    {
1320	last = new;
1321	while (last->next != NULL)
1322	    last = last->next;
1323
1324	last->next = *prev;
1325	*prev = new;
1326    }
1327
1328    if (FcDebug () & FC_DBG_EDIT)
1329    {
1330	printf ("%s list after ", append ? "Append" : "Prepend");
1331	FcValueListPrint (*head);
1332	printf ("\n");
1333    }
1334
1335    return FcTrue;
1336}
1337
1338static void
1339FcConfigDel (FcValueListPtr *head,
1340	     FcValueList    *position)
1341{
1342    FcValueListPtr *prev;
1343
1344    for (prev = head; *prev != NULL; prev = &(*prev)->next)
1345    {
1346	if (*prev == position)
1347	{
1348	    *prev = position->next;
1349	    position->next = NULL;
1350	    FcValueListDestroy (position);
1351	    break;
1352	}
1353    }
1354}
1355
1356static void
1357FcConfigPatternAdd (FcPattern	*p,
1358		    FcObject	object,
1359		    FcValueList	*list,
1360		    FcBool	append)
1361{
1362    if (list)
1363    {
1364	FcPatternElt    *e = FcPatternObjectInsertElt (p, object);
1365
1366	if (!e)
1367	    return;
1368	FcConfigAdd (&e->values, 0, append, list);
1369    }
1370}
1371
1372/*
1373 * Delete all values associated with a field
1374 */
1375static void
1376FcConfigPatternDel (FcPattern	*p,
1377		    FcObject	object)
1378{
1379    FcPatternElt    *e = FcPatternObjectFindElt (p, object);
1380    if (!e)
1381	return;
1382    while (e->values != NULL)
1383	FcConfigDel (&e->values, e->values);
1384}
1385
1386static void
1387FcConfigPatternCanon (FcPattern	    *p,
1388		      FcObject	    object)
1389{
1390    FcPatternElt    *e = FcPatternObjectFindElt (p, object);
1391    if (!e)
1392	return;
1393    if (e->values == NULL)
1394	FcPatternObjectDel (p, object);
1395}
1396
1397FcBool
1398FcConfigSubstituteWithPat (FcConfig    *config,
1399			   FcPattern   *p,
1400			   FcPattern   *p_pat,
1401			   FcMatchKind kind)
1402{
1403    FcSubst	    *s;
1404    FcSubState	    *st;
1405    int		    i;
1406    FcTest	    *t;
1407    FcEdit	    *e;
1408    FcValueList	    *l;
1409    FcPattern	    *m;
1410    FcStrSet	    *strs;
1411
1412    if (!config)
1413    {
1414	config = FcConfigGetCurrent ();
1415	if (!config)
1416	    return FcFalse;
1417    }
1418
1419    switch (kind) {
1420    case FcMatchPattern:
1421	s = config->substPattern;
1422	strs = FcGetDefaultLangs ();
1423	if (strs)
1424	{
1425	    FcStrList *l = FcStrListCreate (strs);
1426	    FcChar8 *lang;
1427	    FcValue v;
1428
1429	    FcStrSetDestroy (strs);
1430	    while (l && (lang = FcStrListNext (l)))
1431	    {
1432		v.type = FcTypeString;
1433		v.u.s = lang;
1434		FcPatternObjectAddWithBinding (p, FC_LANG_OBJECT, v, FcValueBindingWeak, FcTrue);
1435	    }
1436	    FcStrListDone (l);
1437	}
1438	break;
1439    case FcMatchFont:
1440	s = config->substFont;
1441	break;
1442    case FcMatchScan:
1443	s = config->substScan;
1444	break;
1445    default:
1446	return FcFalse;
1447    }
1448
1449    st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1450    if (!st && config->maxObjects)
1451	return FcFalse;
1452    FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1453
1454    if (FcDebug () & FC_DBG_EDIT)
1455    {
1456	printf ("FcConfigSubstitute ");
1457	FcPatternPrint (p);
1458    }
1459    for (; s; s = s->next)
1460    {
1461	/*
1462	 * Check the tests to see if
1463	 * they all match the pattern
1464	 */
1465	for (t = s->test, i = 0; t; t = t->next, i++)
1466	{
1467	    if (FcDebug () & FC_DBG_EDIT)
1468	    {
1469		printf ("FcConfigSubstitute test ");
1470		FcTestPrint (t);
1471	    }
1472	    st[i].elt = 0;
1473	    if (kind == FcMatchFont && t->kind == FcMatchPattern)
1474		m = p_pat;
1475	    else
1476		m = p;
1477	    if (m)
1478		st[i].elt = FcPatternObjectFindElt (m, t->object);
1479	    else
1480		st[i].elt = 0;
1481	    /*
1482	     * If there's no such field in the font,
1483	     * then FcQualAll matches while FcQualAny does not
1484	     */
1485	    if (!st[i].elt)
1486	    {
1487		if (t->qual == FcQualAll)
1488		{
1489		    st[i].value = 0;
1490		    continue;
1491		}
1492		else
1493		    break;
1494	    }
1495	    /*
1496	     * Check to see if there is a match, mark the location
1497	     * to apply match-relative edits
1498	     */
1499	    st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1500	    if (!st[i].value)
1501		break;
1502	    if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1503		break;
1504	    if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1505		break;
1506	}
1507	if (t)
1508	{
1509	    if (FcDebug () & FC_DBG_EDIT)
1510		printf ("No match\n");
1511	    continue;
1512	}
1513	if (FcDebug () & FC_DBG_EDIT)
1514	{
1515	    printf ("Substitute ");
1516	    FcSubstPrint (s);
1517	}
1518	for (e = s->edit; e; e = e->next)
1519	{
1520	    /*
1521	     * Evaluate the list of expressions
1522	     */
1523	    l = FcConfigValues (p, e->expr, e->binding);
1524	    /*
1525	     * Locate any test associated with this field, skipping
1526	     * tests associated with the pattern when substituting in
1527	     * the font
1528	     */
1529	    for (t = s->test, i = 0; t; t = t->next, i++)
1530	    {
1531		if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1532		    t->object == e->object)
1533		{
1534		    /*
1535		     * KLUDGE - the pattern may have been reallocated or
1536		     * things may have been inserted or deleted above
1537		     * this element by other edits.  Go back and find
1538		     * the element again
1539		     */
1540		    if (e != s->edit && st[i].elt)
1541			st[i].elt = FcPatternObjectFindElt (p, t->object);
1542		    if (!st[i].elt)
1543			t = 0;
1544		    break;
1545		}
1546	    }
1547	    switch (FC_OP_GET_OP (e->op)) {
1548	    case FcOpAssign:
1549		/*
1550		 * If there was a test, then replace the matched
1551		 * value with the new list of values
1552		 */
1553		if (t)
1554		{
1555		    FcValueList	*thisValue = st[i].value;
1556		    FcValueList	*nextValue = thisValue;
1557
1558		    /*
1559		     * Append the new list of values after the current value
1560		     */
1561		    FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1562		    /*
1563		     * Delete the marked value
1564		     */
1565                    if (thisValue)
1566			FcConfigDel (&st[i].elt->values, thisValue);
1567		    /*
1568		     * Adjust any pointers into the value list to ensure
1569		     * future edits occur at the same place
1570		     */
1571		    for (t = s->test, i = 0; t; t = t->next, i++)
1572		    {
1573			if (st[i].value == thisValue)
1574			    st[i].value = nextValue;
1575		    }
1576		    break;
1577		}
1578		/* fall through ... */
1579	    case FcOpAssignReplace:
1580		/*
1581		 * Delete all of the values and insert
1582		 * the new set
1583		 */
1584		FcConfigPatternDel (p, e->object);
1585		FcConfigPatternAdd (p, e->object, l, FcTrue);
1586		/*
1587		 * Adjust any pointers into the value list as they no
1588		 * longer point to anything valid
1589		 */
1590		if (t)
1591		{
1592		    FcPatternElt    *thisElt = st[i].elt;
1593		    for (t = s->test, i = 0; t; t = t->next, i++)
1594		    {
1595			if (st[i].elt == thisElt)
1596			    st[i].value = 0;
1597		    }
1598		}
1599		break;
1600	    case FcOpPrepend:
1601		if (t)
1602		{
1603		    FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1604		    break;
1605		}
1606		/* fall through ... */
1607	    case FcOpPrependFirst:
1608		FcConfigPatternAdd (p, e->object, l, FcFalse);
1609		break;
1610	    case FcOpAppend:
1611		if (t)
1612		{
1613		    FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1614		    break;
1615		}
1616		/* fall through ... */
1617	    case FcOpAppendLast:
1618		FcConfigPatternAdd (p, e->object, l, FcTrue);
1619		break;
1620	    default:
1621                FcValueListDestroy (l);
1622		break;
1623	    }
1624	}
1625	/*
1626	 * Now go through the pattern and eliminate
1627	 * any properties without data
1628	 */
1629	for (e = s->edit; e; e = e->next)
1630	    FcConfigPatternCanon (p, e->object);
1631
1632	if (FcDebug () & FC_DBG_EDIT)
1633	{
1634	    printf ("FcConfigSubstitute edit");
1635	    FcPatternPrint (p);
1636	}
1637    }
1638    FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1639    free (st);
1640    if (FcDebug () & FC_DBG_EDIT)
1641    {
1642	printf ("FcConfigSubstitute done");
1643	FcPatternPrint (p);
1644    }
1645    return FcTrue;
1646}
1647
1648FcBool
1649FcConfigSubstitute (FcConfig	*config,
1650		    FcPattern	*p,
1651		    FcMatchKind	kind)
1652{
1653    return FcConfigSubstituteWithPat (config, p, 0, kind);
1654}
1655
1656#if defined (_WIN32)
1657
1658#  define WIN32_LEAN_AND_MEAN
1659#  define WIN32_EXTRA_LEAN
1660#  include <windows.h>
1661
1662static FcChar8 fontconfig_path[1000] = "";
1663
1664#  if (defined (PIC) || defined (DLL_EXPORT))
1665
1666BOOL WINAPI
1667DllMain (HINSTANCE hinstDLL,
1668	 DWORD     fdwReason,
1669	 LPVOID    lpvReserved)
1670{
1671  FcChar8 *p;
1672
1673  switch (fdwReason) {
1674  case DLL_PROCESS_ATTACH:
1675      if (!GetModuleFileName ((HMODULE) hinstDLL, (LPCH) fontconfig_path,
1676			      sizeof (fontconfig_path)))
1677	  break;
1678
1679      /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
1680       * assume it's a Unix-style installation tree, and use
1681       * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
1682       * folder where the DLL is as FONTCONFIG_PATH.
1683       */
1684      p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
1685      if (p)
1686      {
1687	  *p = '\0';
1688	  p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
1689	  if (p && (FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "bin") == 0 ||
1690		    FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "lib") == 0))
1691	      *p = '\0';
1692	  strcat ((char *) fontconfig_path, "\\etc\\fonts");
1693      }
1694      else
1695          fontconfig_path[0] = '\0';
1696
1697      break;
1698  }
1699
1700  return TRUE;
1701}
1702
1703#  endif /* !PIC */
1704
1705#undef FONTCONFIG_PATH
1706#define FONTCONFIG_PATH fontconfig_path
1707
1708#endif /* !_WIN32 */
1709
1710#ifndef FONTCONFIG_FILE
1711#define FONTCONFIG_FILE	"fonts.conf"
1712#endif
1713
1714static FcChar8 *
1715FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1716{
1717    FcChar8    *path;
1718    int         size, osize;
1719
1720    if (!dir)
1721	dir = (FcChar8 *) "";
1722
1723    osize = strlen ((char *) dir) + 1 + strlen ((char *) file) + 1;
1724    /*
1725     * workaround valgrind warning because glibc takes advantage of how it knows memory is
1726     * allocated to implement strlen by reading in groups of 4
1727     */
1728    size = (osize + 3) & ~3;
1729
1730    path = malloc (size);
1731    if (!path)
1732	return 0;
1733
1734    strcpy ((char *) path, (const char *) dir);
1735    /* make sure there's a single separator */
1736#ifdef _WIN32
1737    if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
1738		      path[strlen((char *) path)-1] != '\\')) &&
1739	!(file[0] == '/' ||
1740	  file[0] == '\\' ||
1741	  (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
1742	strcat ((char *) path, "\\");
1743#else
1744    if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1745	strcat ((char *) path, "/");
1746#endif
1747    strcat ((char *) path, (char *) file);
1748
1749    FcMemAlloc (FC_MEM_STRING, osize);
1750    if (access ((char *) path, R_OK) == 0)
1751	return path;
1752
1753    FcStrFree (path);
1754
1755    return 0;
1756}
1757
1758static FcChar8 **
1759FcConfigGetPath (void)
1760{
1761    FcChar8    **path;
1762    FcChar8    *env, *e, *colon;
1763    FcChar8    *dir;
1764    int	    npath;
1765    int	    i;
1766
1767    npath = 2;	/* default dir + null */
1768    env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1769    if (env)
1770    {
1771	e = env;
1772	npath++;
1773	while (*e)
1774	    if (*e++ == FC_SEARCH_PATH_SEPARATOR)
1775		npath++;
1776    }
1777    path = calloc (npath, sizeof (FcChar8 *));
1778    if (!path)
1779	goto bail0;
1780    i = 0;
1781
1782    if (env)
1783    {
1784	e = env;
1785	while (*e)
1786	{
1787	    colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
1788	    if (!colon)
1789		colon = e + strlen ((char *) e);
1790	    path[i] = malloc (colon - e + 1);
1791	    if (!path[i])
1792		goto bail1;
1793	    strncpy ((char *) path[i], (const char *) e, colon - e);
1794	    path[i][colon - e] = '\0';
1795	    if (*colon)
1796		e = colon + 1;
1797	    else
1798		e = colon;
1799	    i++;
1800	}
1801    }
1802
1803#ifdef _WIN32
1804	if (fontconfig_path[0] == '\0')
1805	{
1806		char *p;
1807		if(!GetModuleFileName(NULL, (LPCH) fontconfig_path, sizeof(fontconfig_path)))
1808			goto bail1;
1809		p = strrchr ((const char *) fontconfig_path, '\\');
1810		if (p) *p = '\0';
1811		strcat ((char *) fontconfig_path, "\\fonts");
1812	}
1813#endif
1814    dir = (FcChar8 *) FONTCONFIG_PATH;
1815    path[i] = malloc (strlen ((char *) dir) + 1);
1816    if (!path[i])
1817	goto bail1;
1818    strcpy ((char *) path[i], (const char *) dir);
1819    return path;
1820
1821bail1:
1822    for (i = 0; path[i]; i++)
1823	free (path[i]);
1824    free (path);
1825bail0:
1826    return 0;
1827}
1828
1829static void
1830FcConfigFreePath (FcChar8 **path)
1831{
1832    FcChar8    **p;
1833
1834    for (p = path; *p; p++)
1835	free (*p);
1836    free (path);
1837}
1838
1839static FcBool	_FcConfigHomeEnabled = FcTrue;
1840
1841FcChar8 *
1842FcConfigHome (void)
1843{
1844    if (_FcConfigHomeEnabled)
1845    {
1846        char *home = getenv ("HOME");
1847
1848#ifdef _WIN32
1849	if (home == NULL)
1850	    home = getenv ("USERPROFILE");
1851#endif
1852
1853	return (FcChar8 *) home;
1854    }
1855    return 0;
1856}
1857
1858FcChar8 *
1859FcConfigXdgCacheHome (void)
1860{
1861    const char *env = getenv ("XDG_CACHE_HOME");
1862    FcChar8 *ret = NULL;
1863
1864    if (env)
1865	ret = FcStrCopy ((const FcChar8 *)env);
1866    else
1867    {
1868	const FcChar8 *home = FcConfigHome ();
1869	size_t len = home ? strlen ((const char *)home) : 0;
1870
1871	ret = malloc (len + 7 + 1);
1872	if (ret)
1873	{
1874	    FcMemAlloc (FC_MEM_STRING, len + 7 + 1);
1875	    memcpy (ret, home, len);
1876	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".cache", 7);
1877	    ret[len + 7] = 0;
1878	}
1879    }
1880
1881    return ret;
1882}
1883
1884FcChar8 *
1885FcConfigXdgConfigHome (void)
1886{
1887    const char *env = getenv ("XDG_CONFIG_HOME");
1888    FcChar8 *ret = NULL;
1889
1890    if (env)
1891	ret = FcStrCopy ((const FcChar8 *)env);
1892    else
1893    {
1894	const FcChar8 *home = FcConfigHome ();
1895	size_t len = home ? strlen ((const char *)home) : 0;
1896
1897	ret = malloc (len + 8 + 1);
1898	if (ret)
1899	{
1900	    FcMemAlloc (FC_MEM_STRING, len + 8 + 1);
1901	    memcpy (ret, home, len);
1902	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".config", 8);
1903	    ret[len + 8] = 0;
1904	}
1905    }
1906
1907    return ret;
1908}
1909
1910FcChar8 *
1911FcConfigXdgDataHome (void)
1912{
1913    const char *env = getenv ("XDG_DATA_HOME");
1914    FcChar8 *ret = NULL;
1915
1916    if (env)
1917	ret = FcStrCopy ((const FcChar8 *)env);
1918    else
1919    {
1920	const FcChar8 *home = FcConfigHome ();
1921	size_t len = home ? strlen ((const char *)home) : 0;
1922
1923	ret = malloc (len + 13 + 1);
1924	if (ret)
1925	{
1926	    FcMemAlloc (FC_MEM_STRING, len + 13 + 1);
1927	    memcpy (ret, home, len);
1928	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".local" FC_DIR_SEPARATOR_S "share", 13);
1929	    ret[len + 13] = 0;
1930	}
1931    }
1932
1933    return ret;
1934}
1935
1936FcBool
1937FcConfigEnableHome (FcBool enable)
1938{
1939    FcBool  prev = _FcConfigHomeEnabled;
1940    _FcConfigHomeEnabled = enable;
1941    return prev;
1942}
1943
1944FcChar8 *
1945FcConfigFilename (const FcChar8 *url)
1946{
1947    FcChar8    *file, *dir, **path, **p;
1948
1949    if (!url || !*url)
1950    {
1951	url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1952	if (!url)
1953	    url = (FcChar8 *) FONTCONFIG_FILE;
1954    }
1955    file = 0;
1956
1957#ifdef _WIN32
1958    if (isalpha (*url) &&
1959	url[1] == ':' &&
1960	(url[2] == '/' || url[2] == '\\'))
1961	goto absolute_path;
1962#endif
1963
1964    switch (*url) {
1965    case '~':
1966	dir = FcConfigHome ();
1967	if (dir)
1968	    file = FcConfigFileExists (dir, url + 1);
1969	else
1970	    file = 0;
1971	break;
1972#ifdef _WIN32
1973    case '\\':
1974    absolute_path:
1975#endif
1976    case '/':
1977	file = FcConfigFileExists (0, url);
1978	break;
1979    default:
1980	path = FcConfigGetPath ();
1981	if (!path)
1982	    return NULL;
1983	for (p = path; *p; p++)
1984	{
1985	    file = FcConfigFileExists (*p, url);
1986	    if (file)
1987		break;
1988	}
1989	FcConfigFreePath (path);
1990	break;
1991    }
1992
1993    return file;
1994}
1995
1996/*
1997 * Manage the application-specific fonts
1998 */
1999
2000FcBool
2001FcConfigAppFontAddFile (FcConfig    *config,
2002			const FcChar8  *file)
2003{
2004    FcFontSet	*set;
2005    FcStrSet	*subdirs;
2006    FcStrList	*sublist;
2007    FcChar8	*subdir;
2008
2009    if (!config)
2010    {
2011	config = FcConfigGetCurrent ();
2012	if (!config)
2013	    return FcFalse;
2014    }
2015
2016    subdirs = FcStrSetCreate ();
2017    if (!subdirs)
2018	return FcFalse;
2019
2020    set = FcConfigGetFonts (config, FcSetApplication);
2021    if (!set)
2022    {
2023	set = FcFontSetCreate ();
2024	if (!set)
2025	{
2026	    FcStrSetDestroy (subdirs);
2027	    return FcFalse;
2028	}
2029	FcConfigSetFonts (config, set, FcSetApplication);
2030    }
2031
2032    if (!FcFileScanConfig (set, subdirs, config->blanks, file, config))
2033    {
2034	FcStrSetDestroy (subdirs);
2035	return FcFalse;
2036    }
2037    if ((sublist = FcStrListCreate (subdirs)))
2038    {
2039	while ((subdir = FcStrListNext (sublist)))
2040	{
2041	    FcConfigAppFontAddDir (config, subdir);
2042	}
2043	FcStrListDone (sublist);
2044    }
2045    FcStrSetDestroy (subdirs);
2046    return FcTrue;
2047}
2048
2049FcBool
2050FcConfigAppFontAddDir (FcConfig	    *config,
2051		       const FcChar8   *dir)
2052{
2053    FcFontSet	*set;
2054    FcStrSet	*dirs;
2055
2056    if (!config)
2057    {
2058	config = FcConfigGetCurrent ();
2059	if (!config)
2060	    return FcFalse;
2061    }
2062
2063    dirs = FcStrSetCreate ();
2064    if (!dirs)
2065	return FcFalse;
2066
2067    set = FcConfigGetFonts (config, FcSetApplication);
2068    if (!set)
2069    {
2070	set = FcFontSetCreate ();
2071	if (!set)
2072	{
2073	    FcStrSetDestroy (dirs);
2074	    return FcFalse;
2075	}
2076	FcConfigSetFonts (config, set, FcSetApplication);
2077    }
2078
2079    FcStrSetAddFilename (dirs, dir);
2080
2081    if (!FcConfigAddDirList (config, FcSetApplication, dirs))
2082    {
2083	FcStrSetDestroy (dirs);
2084	return FcFalse;
2085    }
2086    FcStrSetDestroy (dirs);
2087    return FcTrue;
2088}
2089
2090void
2091FcConfigAppFontClear (FcConfig	    *config)
2092{
2093    if (!config)
2094    {
2095	config = FcConfigGetCurrent ();
2096	if (!config)
2097	    return;
2098    }
2099
2100    FcConfigSetFonts (config, 0, FcSetApplication);
2101}
2102
2103/*
2104 * Manage filename-based font source selectors
2105 */
2106
2107FcBool
2108FcConfigGlobAdd (FcConfig	*config,
2109		 const FcChar8  *glob,
2110		 FcBool		accept)
2111{
2112    FcStrSet	*set = accept ? config->acceptGlobs : config->rejectGlobs;
2113
2114    return FcStrSetAdd (set, glob);
2115}
2116
2117static FcBool
2118FcConfigGlobMatch (const FcChar8    *glob,
2119		   const FcChar8    *string)
2120{
2121    FcChar8	c;
2122
2123    while ((c = *glob++))
2124    {
2125	switch (c) {
2126	case '*':
2127	    /* short circuit common case */
2128	    if (!*glob)
2129		return FcTrue;
2130	    /* short circuit another common case */
2131	    if (strchr ((char *) glob, '*') == 0)
2132	    {
2133		size_t l1, l2;
2134
2135		l1 = strlen ((char *) string);
2136		l2 = strlen ((char *) glob);
2137		if (l1 < l2)
2138		    return FcFalse;
2139		string += (l1 - l2);
2140	    }
2141	    while (*string)
2142	    {
2143		if (FcConfigGlobMatch (glob, string))
2144		    return FcTrue;
2145		string++;
2146	    }
2147	    return FcFalse;
2148	case '?':
2149	    if (*string++ == '\0')
2150		return FcFalse;
2151	    break;
2152	default:
2153	    if (*string++ != c)
2154		return FcFalse;
2155	    break;
2156	}
2157    }
2158    return *string == '\0';
2159}
2160
2161static FcBool
2162FcConfigGlobsMatch (const FcStrSet	*globs,
2163		    const FcChar8	*string)
2164{
2165    int	i;
2166
2167    for (i = 0; i < globs->num; i++)
2168	if (FcConfigGlobMatch (globs->strs[i], string))
2169	    return FcTrue;
2170    return FcFalse;
2171}
2172
2173FcBool
2174FcConfigAcceptFilename (FcConfig	*config,
2175			const FcChar8	*filename)
2176{
2177    if (FcConfigGlobsMatch (config->acceptGlobs, filename))
2178	return FcTrue;
2179    if (FcConfigGlobsMatch (config->rejectGlobs, filename))
2180	return FcFalse;
2181    return FcTrue;
2182}
2183
2184/*
2185 * Manage font-pattern based font source selectors
2186 */
2187
2188FcBool
2189FcConfigPatternsAdd (FcConfig	*config,
2190		     FcPattern	*pattern,
2191		     FcBool	accept)
2192{
2193    FcFontSet	*set = accept ? config->acceptPatterns : config->rejectPatterns;
2194
2195    return FcFontSetAdd (set, pattern);
2196}
2197
2198static FcBool
2199FcConfigPatternsMatch (const FcFontSet	*patterns,
2200		       const FcPattern	*font)
2201{
2202    int i;
2203
2204    for (i = 0; i < patterns->nfont; i++)
2205	if (FcListPatternMatchAny (patterns->fonts[i], font))
2206	    return FcTrue;
2207    return FcFalse;
2208}
2209
2210FcBool
2211FcConfigAcceptFont (FcConfig	    *config,
2212		    const FcPattern *font)
2213{
2214    if (FcConfigPatternsMatch (config->acceptPatterns, font))
2215	return FcTrue;
2216    if (FcConfigPatternsMatch (config->rejectPatterns, font))
2217	return FcFalse;
2218    return FcTrue;
2219}
2220#define __fccfg__
2221#include "fcaliastail.h"
2222#undef __fccfg__
2223