fccfg.c 56.4 KB
Newer Older
Keith Packard's avatar
Keith Packard committed
1
/*
2
 * fontconfig/src/fccfg.c
Keith Packard's avatar
Keith Packard committed
3
 *
4
 * Copyright © 2000 Keith Packard
Keith Packard's avatar
Keith Packard committed
5 6 7 8 9
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
10
 * documentation, and that the name of the author(s) not be used in
Keith Packard's avatar
Keith Packard committed
11
 * advertising or publicity pertaining to distribution of the software without
12
 * specific, written prior permission.  The authors make no
Keith Packard's avatar
Keith Packard committed
13 14 15
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
16
 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
Keith Packard's avatar
Keith Packard committed
17
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18
 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
Keith Packard's avatar
Keith Packard committed
19 20 21 22 23 24
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

25 26
/* Objects MT-safe for readonly access. */

27
#include "fcint.h"
28 29
#include <dirent.h>
#include <sys/types.h>
Keith Packard's avatar
Keith Packard committed
30

31 32 33 34
#if defined (_WIN32) && !defined (R_OK)
#define R_OK 4
#endif

35
static FcConfig    *_fcConfig; /* MT-safe */
36

37 38
static FcConfig *
FcConfigEnsure (void)
39 40
{
    FcConfig	*config;
41
retry:
42
    config = fc_atomic_ptr_get (&_fcConfig);
43
    if (!config)
44 45 46 47 48
    {
	config = FcInitLoadConfigAndFonts ();

	if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
	    FcConfigDestroy (config);
49
	    goto retry;
50 51 52 53 54
	}
    }
    return config;
}

Tom Anderson's avatar
Tom Anderson committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
static FcChar32
FcHashAsStrIgnoreCase (const void *data)
{
    return FcStrHashIgnoreCase (data);
}

static int
FcCompareAsStr (const void *v1, const void *v2)
{
    return FcStrCmp (v1, v2);
}

static void
FcDestroyAsRule (void *data)
{
    FcRuleDestroy (data);
}

static void
FcDestroyAsRuleSet (void *data)
{
    FcRuleSetDestroy (data);
}

static void
FcDestroyAsStr (void *data)
{
    FcStrFree (data);
}

85 86 87 88
FcBool
FcConfigInit (void)
{
  return FcConfigEnsure () ? FcTrue : FcFalse;
89 90 91 92 93
}

void
FcConfigFini (void)
{
94 95 96
    FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
    if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
	FcConfigDestroy (cfg);
97 98
}

Keith Packard's avatar
Keith Packard committed
99 100 101 102 103 104

FcConfig *
FcConfigCreate (void)
{
    FcSetName	set;
    FcConfig	*config;
105 106
    FcMatchKind	k;
    FcBool	err = FcFalse;
Keith Packard's avatar
Keith Packard committed
107 108 109 110

    config = malloc (sizeof (FcConfig));
    if (!config)
	goto bail0;
111

112 113
    config->configDirs = FcStrSetCreate ();
    if (!config->configDirs)
Keith Packard's avatar
Keith Packard committed
114
	goto bail1;
115

116
    config->configFiles = FcStrSetCreate ();
Keith Packard's avatar
Keith Packard committed
117 118
    if (!config->configFiles)
	goto bail2;
119

120 121 122
    config->fontDirs = FcStrSetCreate ();
    if (!config->fontDirs)
	goto bail3;
123

124 125 126 127 128 129 130 131
    config->acceptGlobs = FcStrSetCreate ();
    if (!config->acceptGlobs)
	goto bail4;

    config->rejectGlobs = FcStrSetCreate ();
    if (!config->rejectGlobs)
	goto bail5;

132 133 134
    config->acceptPatterns = FcFontSetCreate ();
    if (!config->acceptPatterns)
	goto bail6;
135

136 137 138 139
    config->rejectPatterns = FcFontSetCreate ();
    if (!config->rejectPatterns)
	goto bail7;

140 141
    config->cacheDirs = FcStrSetCreate ();
    if (!config->cacheDirs)
142
	goto bail8;
143

144 145
    for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
    {
Tom Anderson's avatar
Tom Anderson committed
146
	config->subst[k] = FcPtrListCreate (FcDestroyAsRuleSet);
147 148 149 150 151 152
	if (!config->subst[k])
	    err = FcTrue;
    }
    if (err)
	goto bail9;

Keith Packard's avatar
Keith Packard committed
153 154 155
    config->maxObjects = 0;
    for (set = FcSetSystem; set <= FcSetApplication; set++)
	config->fonts[set] = 0;
156 157

    config->rescanTime = time(0);
158
    config->rescanInterval = 30;
159

160 161
    config->expr_pool = NULL;

162 163
    config->sysRoot = NULL;

Tom Anderson's avatar
Tom Anderson committed
164
    config->rulesetList = FcPtrListCreate (FcDestroyAsRuleSet);
165 166 167 168 169 170
    if (!config->rulesetList)
	goto bail9;
    config->availConfigFiles = FcStrSetCreate ();
    if (!config->availConfigFiles)
	goto bail10;

Tom Anderson's avatar
Tom Anderson committed
171 172
    config->uuid_table = FcHashTableCreate (FcHashAsStrIgnoreCase,
					    FcCompareAsStr,
173 174
					    FcHashStrCopy,
					    FcHashUuidCopy,
Tom Anderson's avatar
Tom Anderson committed
175
					    FcDestroyAsStr,
176 177
					    FcHashUuidFree);

178
    FcRefInit (&config->ref, 1);
179

Keith Packard's avatar
Keith Packard committed
180 181
    return config;

182 183 184 185 186 187 188
bail10:
    FcPtrListDestroy (config->rulesetList);
bail9:
    for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
	if (config->subst[k])
	    FcPtrListDestroy (config->subst[k]);
    FcStrSetDestroy (config->cacheDirs);
189 190 191 192
bail8:
    FcFontSetDestroy (config->rejectPatterns);
bail7:
    FcFontSetDestroy (config->acceptPatterns);
193 194 195 196
bail6:
    FcStrSetDestroy (config->rejectGlobs);
bail5:
    FcStrSetDestroy (config->acceptGlobs);
197 198
bail4:
    FcStrSetDestroy (config->fontDirs);
Keith Packard's avatar
Keith Packard committed
199
bail3:
200
    FcStrSetDestroy (config->configFiles);
Keith Packard's avatar
Keith Packard committed
201
bail2:
202
    FcStrSetDestroy (config->configDirs);
Keith Packard's avatar
Keith Packard committed
203 204 205 206 207 208
bail1:
    free (config);
bail0:
    return 0;
}

209
static FcFileTime
210 211 212
FcConfigNewestFile (FcStrSet *files)
{
    FcStrList	    *list = FcStrListCreate (files);
213
    FcFileTime	    newest = { 0, FcFalse };
214 215 216 217 218 219
    FcChar8	    *file;
    struct  stat    statb;

    if (list)
    {
	while ((file = FcStrListNext (list)))
220
	    if (FcStat (file, &statb) == 0)
221
		if (!newest.set || statb.st_mtime - newest.time > 0)
222 223
		{
		    newest.set = FcTrue;
224
		    newest.time = statb.st_mtime;
225
		}
226 227 228 229 230 231 232 233
	FcStrListDone (list);
    }
    return newest;
}

FcBool
FcConfigUptoDate (FcConfig *config)
{
234
    FcFileTime	config_time, config_dir_time, font_time;
235
    time_t	now = time(0);
236 237 238 239 240 241 242
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return FcFalse;
    }
    config_time = FcConfigNewestFile (config->configFiles);
243
    config_dir_time = FcConfigNewestFile (config->configDirs);
244
    font_time = FcConfigNewestFile (config->fontDirs);
245
    if ((config_time.set && config_time.time - config->rescanTime > 0) ||
246
	(config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) ||
247
	(font_time.set && (font_time.time - config->rescanTime) > 0))
248
    {
249 250 251 252 253 254
	/* We need to check for potential clock problems here (OLPC ticket #6046) */
	if ((config_time.set && (config_time.time - now) > 0) ||
    	(config_dir_time.set && (config_dir_time.time - now) > 0) ||
        (font_time.set && (font_time.time - now) > 0))
	{
	    fprintf (stderr,
255
                    "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n");
256 257 258 259 260
	    config->rescanTime = now;
	    return FcTrue;
	}
	else
	    return FcFalse;
261 262 263 264 265
    }
    config->rescanTime = now;
    return FcTrue;
}

266 267 268
FcExpr *
FcConfigAllocExpr (FcConfig *config)
{
269 270 271 272 273 274 275 276 277 278 279 280
    if (!config->expr_pool || config->expr_pool->next == config->expr_pool->end)
    {
	FcExprPage *new_page;

	new_page = malloc (sizeof (FcExprPage));
	if (!new_page)
	    return 0;

	new_page->next_page = config->expr_pool;
	new_page->next = new_page->exprs;
	config->expr_pool = new_page;
    }
281

282
    return config->expr_pool->next++;
283 284
}

285 286 287 288 289 290 291 292 293 294
FcConfig *
FcConfigReference (FcConfig *config)
{
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return 0;
    }

295
    FcRefInc (&config->ref);
296 297 298 299

    return config;
}

Keith Packard's avatar
Keith Packard committed
300 301 302 303
void
FcConfigDestroy (FcConfig *config)
{
    FcSetName	set;
304
    FcExprPage	*page;
305
    FcMatchKind	k;
Keith Packard's avatar
Keith Packard committed
306

307
    if (FcRefDec (&config->ref) != 1)
308 309
	return;

310
    (void) fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
311 312 313

    FcStrSetDestroy (config->configDirs);
    FcStrSetDestroy (config->fontDirs);
314
    FcStrSetDestroy (config->cacheDirs);
315
    FcStrSetDestroy (config->configFiles);
316 317
    FcStrSetDestroy (config->acceptGlobs);
    FcStrSetDestroy (config->rejectGlobs);
318 319
    FcFontSetDestroy (config->acceptPatterns);
    FcFontSetDestroy (config->rejectPatterns);
320

321 322 323 324
    for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
	FcPtrListDestroy (config->subst[k]);
    FcPtrListDestroy (config->rulesetList);
    FcStrSetDestroy (config->availConfigFiles);
Keith Packard's avatar
Keith Packard committed
325 326 327
    for (set = FcSetSystem; set <= FcSetApplication; set++)
	if (config->fonts[set])
	    FcFontSetDestroy (config->fonts[set]);
328

329 330 331 332 333 334 335
    page = config->expr_pool;
    while (page)
    {
      FcExprPage *next = page->next_page;
      free (page);
      page = next;
    }
336 337
    if (config->sysRoot)
	FcStrFree (config->sysRoot);
338

339 340
    FcHashTableDestroy (config->uuid_table);

341
    free (config);
Keith Packard's avatar
Keith Packard committed
342 343 344
}

/*
345
 * Add cache to configuration, adding fonts and directories
Keith Packard's avatar
Keith Packard committed
346 347 348
 */

FcBool
349
FcConfigAddCache (FcConfig *config, FcCache *cache,
350
		  FcSetName set, FcStrSet *dirSet, FcChar8 *forDir)
Keith Packard's avatar
Keith Packard committed
351
{
352 353 354
    FcFontSet	*fs;
    intptr_t	*dirs;
    int		i;
355 356 357 358
    FcBool      relocated = FcFalse;

    if (strcmp ((char *)FcCacheDir(cache), (char *)forDir) != 0)
      relocated = FcTrue;
Keith Packard's avatar
Keith Packard committed
359

360 361 362 363 364
    /*
     * Add fonts
     */
    fs = FcCacheSet (cache);
    if (fs)
365
    {
366 367
	int	nref = 0;
	
368
	for (i = 0; i < fs->nfont; i++)
369
	{
370 371
	    FcPattern	*font = FcFontSetFont (fs, i);
	    FcChar8	*font_file;
372
	    FcChar8	*relocated_font_file = NULL;
373 374

	    if (FcPatternObjectGetString (font, FC_FILE_OBJECT,
375
					  0, &font_file) == FcResultMatch)
376
	    {
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
		if (relocated)
		  {
		    FcChar8 *slash = FcStrLastSlash (font_file);
		    relocated_font_file = FcStrBuildFilename (forDir, slash + 1, NULL);
		    font_file = relocated_font_file;
		  }

		/*
		 * Check to see if font is banned by filename
		 */
		if (!FcConfigAcceptFilename (config, font_file))
		{
		    free (relocated_font_file);
		    continue;
		}
392
	    }
393

394 395 396 397
	    /*
	     * Check to see if font is banned by pattern
	     */
	    if (!FcConfigAcceptFont (config, font))
398 399
	    {
		free (relocated_font_file);
400
		continue;
401 402 403 404 405 406 407 408
	    }

	    if (relocated_font_file)
	    {
	      font = FcPatternCacheRewriteFile (font, cache, relocated_font_file);
	      free (relocated_font_file);
	    }

409 410
	    if (FcFontSetAdd (config->fonts[set], font))
		nref++;
411
	}
412
	FcDirCacheReference (cache, nref);
413
    }
414 415 416 417 418 419

    /*
     * Add directories
     */
    dirs = FcCacheDirs (cache);
    if (dirs)
Keith Packard's avatar
Keith Packard committed
420
    {
421 422
	for (i = 0; i < cache->dirs_count; i++)
	{
423 424 425
	    const FcChar8 *dir = FcCacheSubdir (cache, i);
	    FcChar8 *s = NULL;

426
	    if (relocated)
427
	    {
428
		FcChar8 *base = FcStrBasename (dir);
429
		dir = s = FcStrBuildFilename (forDir, base, NULL);
430
		FcStrFree (base);
431
	    }
432
	    if (FcConfigAcceptFilename (config, dir))
433
		FcStrSetAddFilename (dirSet, dir);
434 435
	    if (s)
		FcStrFree (s);
436
	}
437 438 439
    }
    return FcTrue;
}
440

441 442 443 444 445 446
static FcBool
FcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet)
{
    FcStrList	    *dirlist;
    FcChar8	    *dir;
    FcCache	    *cache;
447

448 449 450 451 452 453 454
    dirlist = FcStrListCreate (dirSet);
    if (!dirlist)
        return FcFalse;
	
    while ((dir = FcStrListNext (dirlist)))
    {
	if (FcDebug () & FC_DBG_FONTSET)
455
	    printf ("adding fonts from %s\n", dir);
456 457 458
	cache = FcDirCacheRead (dir, FcFalse, config);
	if (!cache)
	    continue;
459
	FcConfigAddCache (config, cache, set, dirSet, dir);
460 461 462
	FcDirCacheUnload (cache);
    }
    FcStrListDone (dirlist);
463
    return FcTrue;
464 465
}

466 467 468 469
/*
 * Scan the current list of directories in the configuration
 * and build the set of available fonts.
 */
Patrick Lam's avatar
Patrick Lam committed
470

471 472 473 474
FcBool
FcConfigBuildFonts (FcConfig *config)
{
    FcFontSet	    *fonts;
475

476 477 478 479 480
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return FcFalse;
Keith Packard's avatar
Keith Packard committed
481
    }
482 483 484
	
    fonts = FcFontSetCreate ();
    if (!fonts)
485
	return FcFalse;
486

487
    FcConfigSetFonts (config, fonts, FcSetSystem);
488

489 490
    if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs))
	return FcFalse;
Keith Packard's avatar
Keith Packard committed
491 492 493 494 495 496 497 498
    if (FcDebug () & FC_DBG_FONTSET)
	FcFontSetPrint (fonts);
    return FcTrue;
}

FcBool
FcConfigSetCurrent (FcConfig *config)
{
499 500 501 502 503 504
    FcConfig *cfg;

retry:
    cfg = fc_atomic_ptr_get (&_fcConfig);

    if (config == cfg)
505 506
	return FcTrue;

507
    if (config && !config->fonts[FcSetSystem])
Keith Packard's avatar
Keith Packard committed
508 509 510
	if (!FcConfigBuildFonts (config))
	    return FcFalse;

511 512 513
    if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
	goto retry;

514
    FcConfigReference (config);
515 516 517
    if (cfg)
	FcConfigDestroy (cfg);

Keith Packard's avatar
Keith Packard committed
518 519 520 521 522 523
    return FcTrue;
}

FcConfig *
FcConfigGetCurrent (void)
{
524
    return FcConfigEnsure ();
Keith Packard's avatar
Keith Packard committed
525 526 527
}

FcBool
528 529
FcConfigAddConfigDir (FcConfig	    *config,
		      const FcChar8 *d)
Keith Packard's avatar
Keith Packard committed
530
{
531 532
    return FcStrSetAddFilename (config->configDirs, d);
}
Keith Packard's avatar
Keith Packard committed
533

534 535 536 537
FcStrList *
FcConfigGetConfigDirs (FcConfig   *config)
{
    if (!config)
Keith Packard's avatar
Keith Packard committed
538
    {
539 540 541
	config = FcConfigGetCurrent ();
	if (!config)
	    return 0;
Keith Packard's avatar
Keith Packard committed
542
    }
543 544 545 546 547 548 549 550
    return FcStrListCreate (config->configDirs);
}

FcBool
FcConfigAddFontDir (FcConfig	    *config,
		    const FcChar8   *d)
{
    return FcStrSetAddFilename (config->fontDirs, d);
Keith Packard's avatar
Keith Packard committed
551 552
}

553 554
FcStrList *
FcConfigGetFontDirs (FcConfig	*config)
Keith Packard's avatar
Keith Packard committed
555 556 557 558 559 560 561
{
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return 0;
    }
562
    return FcStrListCreate (config->fontDirs);
Keith Packard's avatar
Keith Packard committed
563 564
}

565 566 567 568 569 570 571 572
FcBool
FcConfigAddCacheDir (FcConfig	    *config,
		     const FcChar8  *d)
{
    return FcStrSetAddFilename (config->cacheDirs, d);
}

FcStrList *
573
FcConfigGetCacheDirs (const FcConfig *config)
574 575 576 577 578 579 580 581 582
{
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return 0;
    }
    return FcStrListCreate (config->cacheDirs);
}
583

Keith Packard's avatar
Keith Packard committed
584 585
FcBool
FcConfigAddConfigFile (FcConfig	    *config,
586
		       const FcChar8   *f)
Keith Packard's avatar
Keith Packard committed
587
{
588 589
    FcBool	ret;
    FcChar8	*file = FcConfigFilename (f);
590

Keith Packard's avatar
Keith Packard committed
591 592
    if (!file)
	return FcFalse;
593

594 595 596
    ret = FcStrSetAdd (config->configFiles, file);
    FcStrFree (file);
    return ret;
Keith Packard's avatar
Keith Packard committed
597 598
}

599
FcStrList *
Keith Packard's avatar
Keith Packard committed
600 601 602 603 604 605 606 607
FcConfigGetConfigFiles (FcConfig    *config)
{
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return 0;
    }
608
    return FcStrListCreate (config->configFiles);
Keith Packard's avatar
Keith Packard committed
609 610
}

611
FcChar8 *
612
FcConfigGetCache (FcConfig  *config FC_UNUSED)
Keith Packard's avatar
Keith Packard committed
613
{
614
    return NULL;
Keith Packard's avatar
Keith Packard committed
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
}

FcFontSet *
FcConfigGetFonts (FcConfig	*config,
		  FcSetName	set)
{
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return 0;
    }
    return config->fonts[set];
}

void
FcConfigSetFonts (FcConfig	*config,
		  FcFontSet	*fonts,
		  FcSetName	set)
{
    if (config->fonts[set])
	FcFontSetDestroy (config->fonts[set]);
    config->fonts[set] = fonts;
}

640 641 642 643 644 645 646 647 648

FcBlanks *
FcBlanksCreate (void)
{
    /* Deprecated. */
    return NULL;
}

void
649
FcBlanksDestroy (FcBlanks *b FC_UNUSED)
650 651 652 653 654
{
    /* Deprecated. */
}

FcBool
655
FcBlanksAdd (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
656 657 658 659 660 661
{
    /* Deprecated. */
    return FcFalse;
}

FcBool
662
FcBlanksIsMember (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
663 664 665 666 667
{
    /* Deprecated. */
    return FcFalse;
}

Keith Packard's avatar
Keith Packard committed
668
FcBlanks *
669
FcConfigGetBlanks (FcConfig	*config FC_UNUSED)
Keith Packard's avatar
Keith Packard committed
670
{
671 672
    /* Deprecated. */
    return NULL;
Keith Packard's avatar
Keith Packard committed
673 674 675
}

FcBool
676 677
FcConfigAddBlank (FcConfig	*config FC_UNUSED,
		  FcChar32    	blank FC_UNUSED)
Keith Packard's avatar
Keith Packard committed
678
{
679 680
    /* Deprecated. */
    return FcFalse;
Keith Packard's avatar
Keith Packard committed
681 682
}

683

684
int
685
FcConfigGetRescanInterval (FcConfig *config)
686 687 688 689 690 691 692 693 694 695 696
{
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return 0;
    }
    return config->rescanInterval;
}

FcBool
697
FcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
698 699 700 701 702 703 704 705 706 707 708
{
    if (!config)
    {
	config = FcConfigGetCurrent ();
	if (!config)
	    return FcFalse;
    }
    config->rescanInterval = rescanInterval;
    return FcTrue;
}

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
/*
 * A couple of typos escaped into the library
 */
int
FcConfigGetRescanInverval (FcConfig *config)
{
    return FcConfigGetRescanInterval (config);
}

FcBool
FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
{
    return FcConfigSetRescanInterval (config, rescanInterval);
}

Keith Packard's avatar
Keith Packard committed
724
FcBool
725 726
FcConfigAddRule (FcConfig	*config,
		 FcRule		*rule,
Keith Packard's avatar
Keith Packard committed
727 728
		 FcMatchKind	kind)
{
729 730
    /* deprecated */
    return FcFalse;
Keith Packard's avatar
Keith Packard committed
731 732 733
}

static FcValue
734
FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
Keith Packard's avatar
Keith Packard committed
735 736 737 738 739 740 741 742
{
    if (v.type == FcTypeInteger)
    {
	v.type = FcTypeDouble;
	v.u.d = (double) v.u.i;
    }
    else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
    {
743
	v.u.m = &FcIdentityMatrix;
744
	v.type = FcTypeMatrix;
Keith Packard's avatar
Keith Packard committed
745
    }
746
    else if (buf && v.type == FcTypeString && u.type == FcTypeLangSet)
747
    {
748
	v.u.l = FcLangSetPromote (v.u.s, buf);
749 750
	v.type = FcTypeLangSet;
    }
751
    else if (buf && v.type == FcTypeVoid && u.type == FcTypeLangSet)
752 753 754 755
    {
	v.u.l = FcLangSetPromote (NULL, buf);
	v.type = FcTypeLangSet;
    }
756
    else if (buf && v.type == FcTypeVoid && u.type == FcTypeCharSet)
757 758 759 760
    {
	v.u.c = FcCharSetPromote (buf);
	v.type = FcTypeCharSet;
    }
761 762 763 764 765
    if (buf && v.type == FcTypeDouble && u.type == FcTypeRange)
    {
	v.u.r = FcRangePromote (v.u.d, buf);
	v.type = FcTypeRange;
    }
Keith Packard's avatar
Keith Packard committed
766 767 768 769
    return v;
}

FcBool
770
FcConfigCompareValue (const FcValue	*left_o,
771
		      unsigned int      op_,
772
		      const FcValue	*right_o)
Keith Packard's avatar
Keith Packard committed
773
{
774 775
    FcValue	left = FcValueCanonicalize(left_o);
    FcValue	right = FcValueCanonicalize(right_o);
776
    FcBool	ret = FcFalse;
777 778
    FcOp	op = FC_OP_GET_OP (op_);
    int		flags = FC_OP_GET_FLAGS (op_);
779
    FcValuePromotionBuffer buf1, buf2;
780

781 782
    left = FcConfigPromote (left, right, &buf1);
    right = FcConfigPromote (right, left, &buf2);
783
    if (left.type == right.type)
Keith Packard's avatar
Keith Packard committed
784
    {
785
	switch (left.type) {
786 787
	case FcTypeUnknown:
	    break;	/* No way to guess how to compare for this object */
Keith Packard's avatar
Keith Packard committed
788 789 790
	case FcTypeInteger:
	    break;	/* FcConfigPromote prevents this from happening */
	case FcTypeDouble:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
791
	    switch ((int) op) {
Keith Packard's avatar
Keith Packard committed
792 793
	    case FcOpEqual:
	    case FcOpContains:
794 795
	    case FcOpListing:
		ret = left.u.d == right.u.d;
Keith Packard's avatar
Keith Packard committed
796
		break;
797 798
	    case FcOpNotEqual:
	    case FcOpNotContains:
799
		ret = left.u.d != right.u.d;
Keith Packard's avatar
Keith Packard committed
800
		break;
801
	    case FcOpLess:
802
		ret = left.u.d < right.u.d;
Keith Packard's avatar
Keith Packard committed
803
		break;
804
	    case FcOpLessEqual:
805
		ret = left.u.d <= right.u.d;
Keith Packard's avatar
Keith Packard committed
806
		break;
807
	    case FcOpMore:
808
		ret = left.u.d > right.u.d;
Keith Packard's avatar
Keith Packard committed
809
		break;
810
	    case FcOpMoreEqual:
811
		ret = left.u.d >= right.u.d;
Keith Packard's avatar
Keith Packard committed
812 813 814 815 816 817
		break;
	    default:
		break;
	    }
	    break;
	case FcTypeBool:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
818
	    switch ((int) op) {
819
	    case FcOpEqual:
820 821
		ret = left.u.b == right.u.b;
		break;
Keith Packard's avatar
Keith Packard committed
822
	    case FcOpContains:
823
	    case FcOpListing:
824
		ret = left.u.b == right.u.b || left.u.b == FcDontCare;
Keith Packard's avatar
Keith Packard committed
825
		break;
826
	    case FcOpNotEqual:
827
		ret = left.u.b != right.u.b;
Keith Packard's avatar
Keith Packard committed
828
		break;
829 830 831
	    case FcOpNotContains:
		ret = !(left.u.b == right.u.b || left.u.b == FcDontCare);
		break;
832 833 834 835 836 837 838 839 840 841 842 843
	    case FcOpLess:
		ret = left.u.b != right.u.b && right.u.b == FcDontCare;
		break;
	    case FcOpLessEqual:
		ret = left.u.b == right.u.b || right.u.b == FcDontCare;
		break;
	    case FcOpMore:
		ret = left.u.b != right.u.b && left.u.b == FcDontCare;
		break;
	    case FcOpMoreEqual:
		ret = left.u.b == right.u.b || left.u.b == FcDontCare;
		break;
Keith Packard's avatar
Keith Packard committed
844 845 846 847 848
	    default:
		break;
	    }
	    break;
	case FcTypeString:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
849
	    switch ((int) op) {
850
	    case FcOpEqual:
851
	    case FcOpListing:
852 853 854 855
		if (flags & FcOpFlagIgnoreBlanks)
		    ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) == 0;
		else
		    ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
Keith Packard's avatar
Keith Packard committed
856
		break;
857
	    case FcOpContains:
858
		ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
859
		break;
860
	    case FcOpNotEqual:
861 862 863 864
		if (flags & FcOpFlagIgnoreBlanks)
		    ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) != 0;
		else
		    ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
865
		break;
866
	    case FcOpNotContains:
867
		ret = FcStrStrIgnoreCase (left.u.s, right.u.s) == 0;
868
		break;
Keith Packard's avatar
Keith Packard committed
869 870 871 872 873
	    default:
		break;
	    }
	    break;
	case FcTypeMatrix:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
874
	    switch ((int) op) {
Keith Packard's avatar
Keith Packard committed
875 876
	    case FcOpEqual:
	    case FcOpContains:
877
	    case FcOpListing:
878
		ret = FcMatrixEqual (left.u.m, right.u.m);
Keith Packard's avatar
Keith Packard committed
879 880
		break;
	    case FcOpNotEqual:
881
	    case FcOpNotContains:
882
		ret = !FcMatrixEqual (left.u.m, right.u.m);
Keith Packard's avatar
Keith Packard committed
883 884 885 886 887 888
		break;
	    default:
		break;
	    }
	    break;
	case FcTypeCharSet:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
889
	    switch ((int) op) {
Keith Packard's avatar
Keith Packard committed
890
	    case FcOpContains:
891 892
	    case FcOpListing:
		/* left contains right if right is a subset of left */
893
		ret = FcCharSetIsSubset (right.u.c, left.u.c);
Keith Packard's avatar
Keith Packard committed
894
		break;
895
	    case FcOpNotContains:
896
		/* left contains right if right is a subset of left */
897
		ret = !FcCharSetIsSubset (right.u.c, left.u.c);
898
		break;
Keith Packard's avatar
Keith Packard committed
899
	    case FcOpEqual:
900
		ret = FcCharSetEqual (left.u.c, right.u.c);
Keith Packard's avatar
Keith Packard committed
901 902
		break;
	    case FcOpNotEqual:
903
		ret = !FcCharSetEqual (left.u.c, right.u.c);
Keith Packard's avatar
Keith Packard committed
904 905 906 907 908
		break;
	    default:
		break;
	    }
	    break;
909
	case FcTypeLangSet:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
910
	    switch ((int) op) {
911
	    case FcOpContains:
912
	    case FcOpListing:
913
		ret = FcLangSetContains (left.u.l, right.u.l);
914
		break;
915
	    case FcOpNotContains:
916
		ret = !FcLangSetContains (left.u.l, right.u.l);
917
		break;
918
	    case FcOpEqual:
919
		ret = FcLangSetEqual (left.u.l, right.u.l);
920 921
		break;
	    case FcOpNotEqual:
922
		ret = !FcLangSetEqual (left.u.l, right.u.l);
923 924 925 926 927
		break;
	    default:
		break;
	    }
	    break;
Keith Packard's avatar
Keith Packard committed
928
	case FcTypeVoid:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
929
	    switch ((int) op) {
Keith Packard's avatar
Keith Packard committed
930 931
	    case FcOpEqual:
	    case FcOpContains:
932
	    case FcOpListing:
Keith Packard's avatar
Keith Packard committed
933 934 935 936 937 938
		ret = FcTrue;
		break;
	    default:
		break;
	    }
	    break;
939
	case FcTypeFTFace:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
940
	    switch ((int) op) {
941
	    case FcOpEqual:
942
	    case FcOpContains:
943 944
	    case FcOpListing:
		ret = left.u.f == right.u.f;
945 946
		break;
	    case FcOpNotEqual:
947
	    case FcOpNotContains:
948
		ret = left.u.f != right.u.f;
949 950 951 952
		break;
	    default:
		break;
	    }
953
	    break;
954 955 956
	case FcTypeRange:
	    ret = FcRangeCompare (op, left.u.r, right.u.r);
	    break;
Keith Packard's avatar
Keith Packard committed
957 958 959 960
	}
    }
    else
    {
961
	if (op == FcOpNotEqual || op == FcOpNotContains)
Keith Packard's avatar
Keith Packard committed
962 963 964 965 966 967
	    ret = FcTrue;
    }
    return ret;
}


968 969 970 971 972 973 974
#define _FcDoubleFloor(d)	((int) (d))
#define _FcDoubleCeil(d)	((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
#define FcDoubleFloor(d)	((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
#define FcDoubleCeil(d)		((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
#define FcDoubleRound(d)	FcDoubleFloor ((d) + 0.5)
#define FcDoubleTrunc(d)	((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))

Keith Packard's avatar
Keith Packard committed
975
static FcValue
976
FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
Keith Packard's avatar
Keith Packard committed
977
{
978
    FcValue	v, vl, vr, vle, vre;
Keith Packard's avatar
Keith Packard committed
979
    FcMatrix	*m;
980
    FcChar8     *str;
981
    FcOp	op = FC_OP_GET_OP (e->op);
982
    FcValuePromotionBuffer buf1, buf2;
983

Behdad Esfahbod's avatar
Behdad Esfahbod committed
984
    switch ((int) op) {
Keith Packard's avatar
Keith Packard committed
985 986 987 988 989 990 991 992 993 994
    case FcOpInteger:
	v.type = FcTypeInteger;
	v.u.i = e->u.ival;
	break;
    case FcOpDouble:
	v.type = FcTypeDouble;
	v.u.d = e->u.dval;
	break;
    case FcOpString:
	v.type = FcTypeString;
995 996
	v.u.s = e->u.sval;
	v = FcValueSave (v);
Keith Packard's avatar
Keith Packard committed
997 998
	break;
    case FcOpMatrix:
999 1000 1001
	{
	  FcMatrix m;
	  FcValue xx, xy, yx, yy;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1002
	  v.type = FcTypeMatrix;
1003 1004 1005 1006
	  xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xx), v, NULL);
	  xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xy), v, NULL);
	  yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yx), v, NULL);
	  yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yy), v, NULL);
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
	  if (xx.type == FcTypeDouble && xy.type == FcTypeDouble &&
	      yx.type == FcTypeDouble && yy.type == FcTypeDouble)
	  {
	    m.xx = xx.u.d;
	    m.xy = xy.u.d;
	    m.yx = yx.u.d;
	    m.yy = yy.u.d;
	    v.u.m = &m;
	  }
	  else
	    v.type = FcTypeVoid;
	  v = FcValueSave (v);
	}
Keith Packard's avatar
Keith Packard committed
1020 1021 1022
	break;
    case FcOpCharSet:
	v.type = FcTypeCharSet;
1023
	v.u.c = e->u.cval;
Keith Packard's avatar
Keith Packard committed
1024 1025
	v = FcValueSave (v);
	break;
1026 1027 1028 1029 1030
    case FcOpLangSet:
	v.type = FcTypeLangSet;
	v.u.l = e->u.lval;
	v = FcValueSave (v);
	break;
1031 1032 1033 1034 1035
    case FcOpRange:
	v.type = FcTypeRange;
	v.u.r = e->u.rval;
	v = FcValueSave (v);
	break;
Keith Packard's avatar
Keith Packard committed
1036 1037 1038 1039 1040
    case FcOpBool:
	v.type = FcTypeBool;
	v.u.b = e->u.bval;
	break;
    case FcOpField:
1041
	if (kind == FcMatchFont && e->u.name.kind == FcMatchPattern)
1042 1043 1044 1045 1046 1047 1048 1049
	{
	    if (FcResultMatch != FcPatternObjectGet (p_pat, e->u.name.object, 0, &v))
		v.type = FcTypeVoid;
	}
	else if (kind == FcMatchPattern && e->u.name.kind == FcMatchFont)
	{
	    fprintf (stderr,
                    "Fontconfig warning: <name> tag has target=\"font\" in a <match target=\"pattern\">.\n");
Keith Packard's avatar
Keith Packard committed
1050
	    v.type = FcTypeVoid;
1051 1052 1053 1054 1055 1056
	}
	else
	{
	    if (FcResultMatch != FcPatternObjectGet (p, e->u.name.object, 0, &v))
		v.type = FcTypeVoid;
	}
1057
	v = FcValueSave (v);
Keith Packard's avatar
Keith Packard committed
1058 1059 1060 1061 1062 1063 1064 1065
	break;
    case FcOpConst:
	if (FcNameConstant (e->u.constant, &v.u.i))
	    v.type = FcTypeInteger;
	else
	    v.type = FcTypeVoid;
	break;
    case FcOpQuest:
1066
	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
Keith Packard's avatar
Keith Packard committed
1067 1068 1069
	if (vl.type == FcTypeBool)
	{
	    if (vl.u.b)
1070
		v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.left);
Keith Packard's avatar
Keith Packard committed
1071
	    else
1072
		v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.right);
Keith Packard's avatar
Keith Packard committed
1073 1074 1075 1076 1077
	}
	else
	    v.type = FcTypeVoid;
	FcValueDestroy (vl);
	break;
1078
    case FcOpEqual:
Keith Packard's avatar
Keith Packard committed
1079 1080 1081 1082 1083
    case FcOpNotEqual:
    case FcOpLess:
    case FcOpLessEqual:
    case FcOpMore:
    case FcOpMoreEqual:
1084 1085
    case FcOpContains:
    case FcOpNotContains:
1086
    case FcOpListing:
1087 1088
	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
	vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
1089
	v.type = FcTypeBool;
1090
	v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
1091 1092 1093 1094 1095
	FcValueDestroy (vl);
	FcValueDestroy (vr);
	break;	
    case FcOpOr:
    case FcOpAnd:
Keith Packard's avatar
Keith Packard committed
1096 1097 1098 1099
    case FcOpPlus:
    case FcOpMinus:
    case FcOpTimes:
    case FcOpDivide:
1100 1101
	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
	vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
1102 1103 1104
	vle = FcConfigPromote (vl, vr, &buf1);
	vre = FcConfigPromote (vr, vle, &buf2);
	if (vle.type == vre.type)
Keith Packard's avatar
Keith Packard committed
1105
	{
1106
	    switch ((int) vle.type) {
Keith Packard's avatar
Keith Packard committed
1107
	    case FcTypeDouble:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1108
		switch ((int) op) {
1109
		case FcOpPlus:	
Keith Packard's avatar
Keith Packard committed
1110
		    v.type = FcTypeDouble;
1111
		    v.u.d = vle.u.d + vre.u.d;
Keith Packard's avatar
Keith Packard committed
1112 1113 1114
		    break;
		case FcOpMinus:
		    v.type = FcTypeDouble;
1115
		    v.u.d = vle.u.d - vre.u.d;
Keith Packard's avatar
Keith Packard committed
1116 1117 1118
		    break;
		case FcOpTimes:
		    v.type = FcTypeDouble;
1119
		    v.u.d = vle.u.d * vre.u.d;
Keith Packard's avatar
Keith Packard committed
1120 1121 1122
		    break;
		case FcOpDivide:
		    v.type = FcTypeDouble;
1123
		    v.u.d = vle.u.d / vre.u.d;
Keith Packard's avatar
Keith Packard committed
1124 1125
		    break;
		default:
1126
		    v.type = FcTypeVoid;
Keith Packard's avatar
Keith Packard committed
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
		    break;
		}
		if (v.type == FcTypeDouble &&
		    v.u.d == (double) (int) v.u.d)
		{
		    v.type = FcTypeInteger;
		    v.u.i = (int) v.u.d;
		}
		break;
	    case FcTypeBool:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1137
		switch ((int) op) {
Keith Packard's avatar
Keith Packard committed
1138 1139
		case FcOpOr:
		    v.type = FcTypeBool;
1140
		    v.u.b = vle.u.b || vre.u.b;
Keith Packard's avatar
Keith Packard committed
1141 1142 1143
		    break;
		case FcOpAnd:
		    v.type = FcTypeBool;
1144
		    v.u.b = vle.u.b && vre.u.b;
Keith Packard's avatar
Keith Packard committed
1145 1146
		    break;
		default:
1147
		    v.type = FcTypeVoid;
Keith Packard's avatar
Keith Packard committed
1148 1149 1150 1151
		    break;
		}
		break;
	    case FcTypeString:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1152
		switch ((int) op) {
Keith Packard's avatar
Keith Packard committed
1153 1154
		case FcOpPlus:
		    v.type = FcTypeString;
1155
		    str = FcStrPlus (vle.u.s, vre.u.s);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1156
		    v.u.s = FcStrdup (str);
1157
		    FcStrFree (str);
1158
			
1159
		    if (!v.u.s)
Keith Packard's avatar
Keith Packard committed
1160 1161 1162 1163 1164 1165
			v.type = FcTypeVoid;
		    break;
		default:
		    v.type = FcTypeVoid;
		    break;
		}
1166
		break;
Keith Packard's avatar
Keith Packard committed
1167
	    case FcTypeMatrix:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1168
		switch ((int) op) {
Keith Packard's avatar
Keith Packard committed
1169 1170 1171 1172 1173
		case FcOpTimes:
		    v.type = FcTypeMatrix;
		    m = malloc (sizeof (FcMatrix));
		    if (m)
		    {
1174
			FcMatrixMultiply (m, vle.u.m, vre.u.m);
1175
			v.u.m = m;
Keith Packard's avatar
Keith Packard committed
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
		    }
		    else
		    {
			v.type = FcTypeVoid;
		    }
		    break;
		default:
		    v.type = FcTypeVoid;
		    break;
		}
		break;
1187
	    case FcTypeCharSet:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1188
		switch ((int) op) {
1189 1190
		case FcOpPlus:
		    v.type = FcTypeCharSet;
1191
		    v.u.c = FcCharSetUnion (vle.u.c, vre.u.c);
1192 1193 1194 1195 1196
		    if (!v.u.c)
			v.type = FcTypeVoid;
		    break;
		case FcOpMinus:
		    v.type = FcTypeCharSet;
1197
		    v.u.c = FcCharSetSubtract (vle.u.c, vre.u.c);
1198 1199 1200 1201 1202 1203 1204 1205
		    if (!v.u.c)
			v.type = FcTypeVoid;
		    break;
		default:
		    v.type = FcTypeVoid;
		    break;
		}
		break;
1206
	    case FcTypeLangSet:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1207
		switch ((int) op) {
1208 1209
		case FcOpPlus:
		    v.type = FcTypeLangSet;
1210
		    v.u.l = FcLangSetUnion (vle.u.l, vre.u.l);
1211 1212 1213 1214 1215
		    if (!v.u.l)
			v.type = FcTypeVoid;
		    break;
		case FcOpMinus:
		    v.type = FcTypeLangSet;
1216
		    v.u.l = FcLangSetSubtract (vle.u.l, vre.u.l);
1217 1218 1219 1220 1221 1222 1223 1224
		    if (!v.u.l)
			v.type = FcTypeVoid;
		    break;
		default:
		    v.type = FcTypeVoid;
		    break;
		}
		break;
Keith Packard's avatar
Keith Packard committed
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
	    default:
		v.type = FcTypeVoid;
		break;
	    }
	}
	else
	    v.type = FcTypeVoid;
	FcValueDestroy (vl);
	FcValueDestroy (vr);
	break;
    case FcOpNot:
1236
	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1237
	switch ((int) vl.type) {
Keith Packard's avatar
Keith Packard committed
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
	case FcTypeBool:
	    v.type = FcTypeBool;
	    v.u.b = !vl.u.b;
	    break;
	default:
	    v.type = FcTypeVoid;
	    break;
	}
	FcValueDestroy (vl);
	break;
1248
    case FcOpFloor:
1249
	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1250
	switch ((int) vl.type) {
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
	case FcTypeInteger:
	    v = vl;
	    break;
	case FcTypeDouble:
	    v.type = FcTypeInteger;
	    v.u.i = FcDoubleFloor (vl.u.d);
	    break;
	default:
	    v.type = FcTypeVoid;
	    break;
	}
	FcValueDestroy (vl);
	break;
    case FcOpCeil:
1265
	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1266
	switch ((int) vl.type) {
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
	case FcTypeInteger:
	    v = vl;
	    break;
	case FcTypeDouble:
	    v.type = FcTypeInteger;
	    v.u.i = FcDoubleCeil (vl.u.d);
	    break;
	default:
	    v.type = FcTypeVoid;
	    break;
	}
	FcValueDestroy (vl);
	break;
    case FcOpRound:
1281
	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1282
	switch ((int) vl.type) {
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
	case FcTypeInteger:
	    v = vl;
	    break;
	case FcTypeDouble:
	    v.type = FcTypeInteger;
	    v.u.i = FcDoubleRound (vl.u.d);
	    break;
	default:
	    v.type = FcTypeVoid;
	    break;
	}
	FcValueDestroy (vl);
	break;
    case FcOpTrunc:
1297
	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1298
	switch ((int) vl.type) {
1299 1300 1301 1302 1303 1304 1305 1306 1307 1308