text: /*
   * fontconfig/src/fccfg.c
   *
   * Copyright © 2000 Keith Packard
   *
   * 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
   * documentation, and that the name of the author(s) not be used in
   * advertising or publicity pertaining to distribution of the software without
   * specific, written prior permission.  The authors make no
   * representations about the suitability of this software for any purpose.  It
   * is provided "as is" without express or implied warranty.
   *
   * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
   * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
   * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
   * 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.
   */
  
  /* Objects MT-safe for readonly access. */
  
  #include "fcint.h"
  #include <dirent.h>
  #include <sys/types.h>
  
  #if defined (_WIN32) && !defined (R_OK)
  #define R_OK 4
  #endif
  
  static FcConfig    *_fcConfig; /* MT-safe */
  
  static FcConfig *
  FcConfigEnsure (void)
  {
      FcConfig	*config;
  retry:
      config = fc_atomic_ptr_get (&_fcConfig);
      if (!config)
      {
  	config = FcInitLoadConfigAndFonts ();
  
  	if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
  	    FcConfigDestroy (config);
  	    goto retry;
  	}
      }
      return config;
  }
  
  FcBool
  FcConfigInit (void)
  {
    return FcConfigEnsure () ? FcTrue : FcFalse;
  }
  
  void
  FcConfigFini (void)
  {
      FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
      if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
  	FcConfigDestroy (cfg);
  }
  
  
  FcConfig *
  FcConfigCreate (void)
  {
      FcSetName	set;
      FcConfig	*config;
  
      config = malloc (sizeof (FcConfig));
      if (!config)
  	goto bail0;
  
      config->configDirs = FcStrSetCreate ();
      if (!config->configDirs)
  	goto bail1;
  
      config->configFiles = FcStrSetCreate ();
      if (!config->configFiles)
  	goto bail2;
  
      config->fontDirs = FcStrSetCreate ();
      if (!config->fontDirs)
  	goto bail3;
  
      config->acceptGlobs = FcStrSetCreate ();
      if (!config->acceptGlobs)
  	goto bail4;
  
      config->rejectGlobs = FcStrSetCreate ();
      if (!config->rejectGlobs)
  	goto bail5;
  
      config->acceptPatterns = FcFontSetCreate ();
      if (!config->acceptPatterns)
  	goto bail6;
  
      config->rejectPatterns = FcFontSetCreate ();
      if (!config->rejectPatterns)
  	goto bail7;
  
      config->cacheDirs = FcStrSetCreate ();
      if (!config->cacheDirs)
  	goto bail8;
  
      config->blanks = 0;
  
      config->substPattern = 0;
      config->substFont = 0;
      config->substScan = 0;
      config->maxObjects = 0;
      for (set = FcSetSystem; set <= FcSetApplication; set++)
  	config->fonts[set] = 0;
  
      config->rescanTime = time(0);
      config->rescanInterval = 30;
  
      config->expr_pool = NULL;
  
      config->sysRoot = NULL;
  
      FcRefInit (&config->ref, 1);
  
      return config;
  
  bail8:
      FcFontSetDestroy (config->rejectPatterns);
  bail7:
      FcFontSetDestroy (config->acceptPatterns);
  bail6:
      FcStrSetDestroy (config->rejectGlobs);
  bail5:
      FcStrSetDestroy (config->acceptGlobs);
  bail4:
      FcStrSetDestroy (config->fontDirs);
  bail3:
      FcStrSetDestroy (config->configFiles);
  bail2:
      FcStrSetDestroy (config->configDirs);
  bail1:
      free (config);
  bail0:
      return 0;
  }
  
  static FcFileTime
  FcConfigNewestFile (FcStrSet *files)
  {
      FcStrList	    *list = FcStrListCreate (files);
      FcFileTime	    newest = { 0, FcFalse };
      FcChar8	    *file;
      struct  stat    statb;
  
      if (list)
      {
  	while ((file = FcStrListNext (list)))
  	    if (FcStat (file, &statb) == 0)
  		if (!newest.set || statb.st_mtime - newest.time > 0)
  		{
  		    newest.set = FcTrue;
  		    newest.time = statb.st_mtime;
  		}
  	FcStrListDone (list);
      }
      return newest;
  }
  
  FcBool
  FcConfigUptoDate (FcConfig *config)
  {
      FcFileTime	config_time, config_dir_time, font_time;
      time_t	now = time(0);
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return FcFalse;
      }
      config_time = FcConfigNewestFile (config->configFiles);
      config_dir_time = FcConfigNewestFile (config->configDirs);
      font_time = FcConfigNewestFile (config->fontDirs);
      if ((config_time.set && config_time.time - config->rescanTime > 0) ||
  	(config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) ||
  	(font_time.set && (font_time.time - config->rescanTime) > 0))
      {
  	/* 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,
                      "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n");
  	    config->rescanTime = now;
  	    return FcTrue;
  	}
  	else
  	    return FcFalse;
      }
      config->rescanTime = now;
      return FcTrue;
  }
  
  static void
  FcSubstDestroy (FcSubst *s)
  {
      FcSubst *n;
  
      while (s)
      {
  	n = s->next;
  	if (s->rule)
  	    FcRuleDestroy (s->rule);
  	free (s);
  	s = n;
      }
  }
  
  FcExpr *
  FcConfigAllocExpr (FcConfig *config)
  {
      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;
      }
  
      return config->expr_pool->next++;
  }
  
  FcConfig *
  FcConfigReference (FcConfig *config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return 0;
      }
  
      FcRefInc (&config->ref);
  
      return config;
  }
  
  void
  FcConfigDestroy (FcConfig *config)
  {
      FcSetName	set;
      FcExprPage	*page;
  
      if (FcRefDec (&config->ref) != 1)
  	return;
  
      (void) fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
  
      FcStrSetDestroy (config->configDirs);
      FcStrSetDestroy (config->fontDirs);
      FcStrSetDestroy (config->cacheDirs);
      FcStrSetDestroy (config->configFiles);
      FcStrSetDestroy (config->acceptGlobs);
      FcStrSetDestroy (config->rejectGlobs);
      FcFontSetDestroy (config->acceptPatterns);
      FcFontSetDestroy (config->rejectPatterns);
  
      if (config->blanks)
  	FcBlanksDestroy (config->blanks);
  
      FcSubstDestroy (config->substPattern);
      FcSubstDestroy (config->substFont);
      FcSubstDestroy (config->substScan);
      for (set = FcSetSystem; set <= FcSetApplication; set++)
  	if (config->fonts[set])
  	    FcFontSetDestroy (config->fonts[set]);
  
      page = config->expr_pool;
      while (page)
      {
        FcExprPage *next = page->next_page;
        free (page);
        page = next;
      }
      if (config->sysRoot)
  	FcStrFree (config->sysRoot);
  
      free (config);
  }
  
  /*
   * Add cache to configuration, adding fonts and directories
   */
  
  FcBool
  FcConfigAddCache (FcConfig *config, FcCache *cache,
  		  FcSetName set, FcStrSet *dirSet)
  {
      FcFontSet	*fs;
      intptr_t	*dirs;
      int		i;
  
      /*
       * Add fonts
       */
      fs = FcCacheSet (cache);
      if (fs)
      {
  	int	nref = 0;
  	
  	for (i = 0; i < fs->nfont; i++)
  	{
  	    FcPattern	*font = FcFontSetFont (fs, i);
  	    FcChar8	*font_file;
  
  	    /*
  	     * Check to see if font is banned by filename
  	     */
  	    if (FcPatternObjectGetString (font, FC_FILE_OBJECT,
  					  0, &font_file) == FcResultMatch &&
  		!FcConfigAcceptFilename (config, font_file))
  	    {
  		continue;
  	    }
  		
  	    /*
  	     * Check to see if font is banned by pattern
  	     */
  	    if (!FcConfigAcceptFont (config, font))
  		continue;
  		
  	    if (FcFontSetAdd (config->fonts[set], font))
  		nref++;
  	}
  	FcDirCacheReference (cache, nref);
      }
  
      /*
       * Add directories
       */
      dirs = FcCacheDirs (cache);
      if (dirs)
      {
  	for (i = 0; i < cache->dirs_count; i++)
  	{
  	    FcChar8	*dir = FcOffsetToPtr (dirs, dirs[i], FcChar8);
  	    if (FcConfigAcceptFilename (config, dir))
  		FcStrSetAddFilename (dirSet, dir);
  	}
      }
      return FcTrue;
  }
  
  static FcBool
  FcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet)
  {
      FcStrList	    *dirlist;
      FcChar8	    *dir;
      FcCache	    *cache;
  
      dirlist = FcStrListCreate (dirSet);
      if (!dirlist)
          return FcFalse;
  	
      while ((dir = FcStrListNext (dirlist)))
      {
  	if (FcDebug () & FC_DBG_FONTSET)
  	    printf ("adding fonts from%s\n", dir);
  	cache = FcDirCacheRead (dir, FcFalse, config);
  	if (!cache)
  	    continue;
  	FcConfigAddCache (config, cache, set, dirSet);
  	FcDirCacheUnload (cache);
      }
      FcStrListDone (dirlist);
      return FcTrue;
  }
  
  /*
   * Scan the current list of directories in the configuration
   * and build the set of available fonts.
   */
  
  FcBool
  FcConfigBuildFonts (FcConfig *config)
  {
      FcFontSet	    *fonts;
  
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return FcFalse;
      }
  	
      fonts = FcFontSetCreate ();
      if (!fonts)
  	return FcFalse;
  
      FcConfigSetFonts (config, fonts, FcSetSystem);
  
      if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs))
  	return FcFalse;
      if (FcDebug () & FC_DBG_FONTSET)
  	FcFontSetPrint (fonts);
      return FcTrue;
  }
  
  FcBool
  FcConfigSetCurrent (FcConfig *config)
  {
      FcConfig *cfg;
  
  retry:
      cfg = fc_atomic_ptr_get (&_fcConfig);
  
      if (config == cfg)
  	return FcTrue;
  
      if (config && !config->fonts[FcSetSystem])
  	if (!FcConfigBuildFonts (config))
  	    return FcFalse;
  
      if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
  	goto retry;
  
      if (cfg)
  	FcConfigDestroy (cfg);
  
      return FcTrue;
  }
  
  FcConfig *
  FcConfigGetCurrent (void)
  {
      return FcConfigEnsure ();
  }
  
  FcBool
  FcConfigAddConfigDir (FcConfig	    *config,
  		      const FcChar8 *d)
  {
      return FcStrSetAddFilename (config->configDirs, d);
  }
  
  FcStrList *
  FcConfigGetConfigDirs (FcConfig   *config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return 0;
      }
      return FcStrListCreate (config->configDirs);
  }
  
  FcBool
  FcConfigAddFontDir (FcConfig	    *config,
  		    const FcChar8   *d)
  {
      return FcStrSetAddFilename (config->fontDirs, d);
  }
  
  FcBool
  FcConfigAddDir (FcConfig	    *config,
  		const FcChar8	    *d)
  {
      return (FcConfigAddConfigDir (config, d) &&
  	    FcConfigAddFontDir (config, d));
  }
  
  FcStrList *
  FcConfigGetFontDirs (FcConfig	*config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return 0;
      }
      return FcStrListCreate (config->fontDirs);
  }
  
  FcBool
  FcConfigAddCacheDir (FcConfig	    *config,
  		     const FcChar8  *d)
  {
      return FcStrSetAddFilename (config->cacheDirs, d);
  }
  
  FcStrList *
  FcConfigGetCacheDirs (const FcConfig *config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return 0;
      }
      return FcStrListCreate (config->cacheDirs);
  }
  
  FcBool
  FcConfigAddConfigFile (FcConfig	    *config,
  		       const FcChar8   *f)
  {
      FcBool	ret;
      FcChar8	*file = FcConfigFilename (f);
  
      if (!file)
  	return FcFalse;
  
      ret = FcStrSetAdd (config->configFiles, file);
      FcStrFree (file);
      return ret;
  }
  
  FcStrList *
  FcConfigGetConfigFiles (FcConfig    *config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return 0;
      }
      return FcStrListCreate (config->configFiles);
  }
  
  FcChar8 *
  FcConfigGetCache (FcConfig  *config FC_UNUSED)
  {
      return NULL;
  }
  
  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;
  }
  
  FcBlanks *
  FcConfigGetBlanks (FcConfig	*config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return 0;
      }
      return config->blanks;
  }
  
  FcBool
  FcConfigAddBlank (FcConfig	*config,
  		  FcChar32    	blank)
  {
      FcBlanks	*b, *freeme = 0;
  
      b = config->blanks;
      if (!b)
      {
  	freeme = b = FcBlanksCreate ();
  	if (!b)
  	    return FcFalse;
      }
      if (!FcBlanksAdd (b, blank))
      {
          if (freeme)
              FcBlanksDestroy (freeme);
  	return FcFalse;
      }
      config->blanks = b;
      return FcTrue;
  }
  
  int
  FcConfigGetRescanInterval (FcConfig *config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return 0;
      }
      return config->rescanInterval;
  }
  
  FcBool
  FcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return FcFalse;
      }
      config->rescanInterval = rescanInterval;
      return FcTrue;
  }
  
  /*
   * 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);
  }
  
  FcBool
  FcConfigAddRule (FcConfig	*config,
  		 FcRule		*rule,
  		 FcMatchKind	kind)
  {
      FcSubst	*subst, **prev;
      FcRule	*r;
      int		n = 0;
  
      if (!rule)
  	return FcFalse;
      switch (kind) {
      case FcMatchPattern:
  	prev = &config->substPattern;
  	break;
      case FcMatchFont:
  	prev = &config->substFont;
  	break;
      case FcMatchScan:
  	prev = &config->substScan;
  	break;
      default:
  	return FcFalse;
      }
      subst = (FcSubst *) malloc (sizeof (FcSubst));
      if (!subst)
  	return FcFalse;
      for (; *prev; prev = &(*prev)->next);
      *prev = subst;
      subst->next = NULL;
      subst->rule = rule;
      for (r = rule; r; r = r->next)
      {
  	switch (r->type)
  	{
  	case FcRuleTest:
  	    if (r->u.test &&
  		r->u.test->kind == FcMatchDefault)
  		r->u.test->kind = kind;
  
  	    if (n < r->u.test->object)
  		n = r->u.test->object;
  	    break;
  	case FcRuleEdit:
  	    if (n < r->u.edit->object)
  		n = r->u.edit->object;
  	    break;
  	default:
  	    break;
  	}
      }
      n = FC_OBJ_ID (n) - FC_MAX_BASE_OBJECT;
      if (config->maxObjects < n)
  	config->maxObjects = n;
      if (FcDebug () & FC_DBG_EDIT)
      {
  	printf ("Add Subst ");
  	FcSubstPrint (subst);
      }
      return FcTrue;
  }
  
  static FcValue
  FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
  {
      if (v.type == FcTypeInteger)
      {
  	v.type = FcTypeDouble;
  	v.u.d = (double) v.u.i;
      }
      else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
      {
  	v.u.m = &FcIdentityMatrix;
  	v.type = FcTypeMatrix;
      }
      else if (buf && v.type == FcTypeString && u.type == FcTypeLangSet)
      {
  	v.u.l = FcLangSetPromote (v.u.s, buf);
  	v.type = FcTypeLangSet;
      }
      if (buf && v.type == FcTypeDouble && u.type == FcTypeRange)
      {
  	v.u.r = FcRangePromote (v.u.d, buf);
  	v.type = FcTypeRange;
      }
      return v;
  }
  
  FcBool
  FcConfigCompareValue (const FcValue	*left_o,
  		      unsigned int      op_,
  		      const FcValue	*right_o)
  {
      FcValue	left = FcValueCanonicalize(left_o);
      FcValue	right = FcValueCanonicalize(right_o);
      FcBool	ret = FcFalse;
      FcOp	op = FC_OP_GET_OP (op_);
      int		flags = FC_OP_GET_FLAGS (op_);
      FcValuePromotionBuffer buf1, buf2;
  
      left = FcConfigPromote (left, right, &buf1);
      right = FcConfigPromote (right, left, &buf2);
      if (left.type == right.type)
      {
  	switch (left.type) {
  	case FcTypeUnknown:
  	    break;	/* No way to guess how to compare for this object */
  	case FcTypeInteger:
  	    break;	/* FcConfigPromote prevents this from happening */
  	case FcTypeDouble:
  	    switch ((int) op) {
  	    case FcOpEqual:
  	    case FcOpContains:
  	    case FcOpListing:
  		ret = left.u.d == right.u.d;
  		break;
  	    case FcOpNotEqual:
  	    case FcOpNotContains:
  		ret = left.u.d != right.u.d;
  		break;
  	    case FcOpLess:
  		ret = left.u.d < right.u.d;
  		break;
  	    case FcOpLessEqual:
  		ret = left.u.d <= right.u.d;
  		break;
  	    case FcOpMore:
  		ret = left.u.d > right.u.d;
  		break;
  	    case FcOpMoreEqual:
  		ret = left.u.d >= right.u.d;
  		break;
  	    default:
  		break;
  	    }
  	    break;
  	case FcTypeBool:
  	    switch ((int) op) {
  	    case FcOpEqual:
  	    case FcOpContains:
  	    case FcOpListing:
  		ret = left.u.b == right.u.b;
  		break;
  	    case FcOpNotEqual:
  	    case FcOpNotContains:
  		ret = left.u.b != right.u.b;
  		break;
  	    default:
  		break;
  	    }
  	    break;
  	case FcTypeString:
  	    switch ((int) op) {
  	    case FcOpEqual:
  	    case FcOpListing:
  		if (flags & FcOpFlagIgnoreBlanks)
  		    ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) == 0;
  		else
  		    ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
  		break;
  	    case FcOpContains:
  		ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
  		break;
  	    case FcOpNotEqual:
  		if (flags & FcOpFlagIgnoreBlanks)
  		    ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) != 0;
  		else
  		    ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
  		break;
  	    case FcOpNotContains:
  		ret = FcStrStrIgnoreCase (left.u.s, right.u.s) == 0;
  		break;
  	    default:
  		break;
  	    }
  	    break;
  	case FcTypeMatrix:
  	    switch ((int) op) {
  	    case FcOpEqual:
  	    case FcOpContains:
  	    case FcOpListing:
  		ret = FcMatrixEqual (left.u.m, right.u.m);
  		break;
  	    case FcOpNotEqual:
  	    case FcOpNotContains:
  		ret = !FcMatrixEqual (left.u.m, right.u.m);
  		break;
  	    default:
  		break;
  	    }
  	    break;
  	case FcTypeCharSet:
  	    switch ((int) op) {
  	    case FcOpContains:
  	    case FcOpListing:
  		/* left contains right if right is a subset of left */
  		ret = FcCharSetIsSubset (right.u.c, left.u.c);
  		break;
  	    case FcOpNotContains:
  		/* left contains right if right is a subset of left */
  		ret = !FcCharSetIsSubset (right.u.c, left.u.c);
  		break;
  	    case FcOpEqual:
  		ret = FcCharSetEqual (left.u.c, right.u.c);
  		break;
  	    case FcOpNotEqual:
  		ret = !FcCharSetEqual (left.u.c, right.u.c);
  		break;
  	    default:
  		break;
  	    }
  	    break;
  	case FcTypeLangSet:
  	    switch ((int) op) {
  	    case FcOpContains:
  	    case FcOpListing:
  		ret = FcLangSetContains (left.u.l, right.u.l);
  		break;
  	    case FcOpNotContains:
  		ret = !FcLangSetContains (left.u.l, right.u.l);
  		break;
  	    case FcOpEqual:
  		ret = FcLangSetEqual (left.u.l, right.u.l);
  		break;
  	    case FcOpNotEqual:
  		ret = !FcLangSetEqual (left.u.l, right.u.l);
  		break;
  	    default:
  		break;
  	    }
  	    break;
  	case FcTypeVoid:
  	    switch ((int) op) {
  	    case FcOpEqual:
  	    case FcOpContains:
  	    case FcOpListing:
  		ret = FcTrue;
  		break;
  	    default:
  		break;
  	    }
  	    break;
  	case FcTypeFTFace:
  	    switch ((int) op) {
  	    case FcOpEqual:
  	    case FcOpContains:
  	    case FcOpListing:
  		ret = left.u.f == right.u.f;
  		break;
  	    case FcOpNotEqual:
  	    case FcOpNotContains:
  		ret = left.u.f != right.u.f;
  		break;
  	    default:
  		break;
  	    }
  	    break;
  	case FcTypeRange:
  	    ret = FcRangeCompare (op, left.u.r, right.u.r);
  	    break;
  	}
      }
      else
      {
  	if (op == FcOpNotEqual || op == FcOpNotContains)
  	    ret = FcTrue;
      }
      return ret;
  }
  
  
  #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)))
  
  static FcValue
  FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
  {
      FcValue	v, vl, vr, vle, vre;
      FcMatrix	*m;
      FcChar8     *str;
      FcOp	op = FC_OP_GET_OP (e->op);
      FcValuePromotionBuffer buf1, buf2;
  
      switch ((int) op) {
      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;
  	v.u.s = e->u.sval;
  	v = FcValueSave (v);
  	break;
      case FcOpMatrix:
  	{
  	  FcMatrix m;
  	  FcValue xx, xy, yx, yy;
  	  v.type = FcTypeMatrix;
  	  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);
  	  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);
  	}
  	break;
      case FcOpCharSet:
  	v.type = FcTypeCharSet;
  	v.u.c = e->u.cval;
  	v = FcValueSave (v);
  	break;
      case FcOpLangSet:
  	v.type = FcTypeLangSet;
  	v.u.l = e->u.lval;
  	v = FcValueSave (v);
  	break;
      case FcOpRange:
  	v.type = FcTypeRange;
  	v.u.r = e->u.rval;
  	v = FcValueSave (v);
  	break;
      case FcOpBool:
  	v.type = FcTypeBool;
  	v.u.b = e->u.bval;
  	break;
      case FcOpField:
  	if (kind == FcMatchFont && e->u.name.kind == FcMatchPattern)
  	{
  	    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");
  	    v.type = FcTypeVoid;
  	}
  	else
  	{
  	    if (FcResultMatch != FcPatternObjectGet (p, e->u.name.object, 0, &v))
  		v.type = FcTypeVoid;
  	}
  	v = FcValueSave (v);
  	break;
      case FcOpConst:
  	if (FcNameConstant (e->u.constant, &v.u.i))
  	    v.type = FcTypeInteger;
  	else
  	    v.type = FcTypeVoid;
  	break;
      case FcOpQuest:
  	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	if (vl.type == FcTypeBool)
  	{
  	    if (vl.u.b)
  		v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.left);
  	    else
  		v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.right);
  	}
  	else
  	    v.type = FcTypeVoid;
  	FcValueDestroy (vl);
  	break;
      case FcOpEqual:
      case FcOpNotEqual:
      case FcOpLess:
      case FcOpLessEqual:
      case FcOpMore:
      case FcOpMoreEqual:
      case FcOpContains:
      case FcOpNotContains:
      case FcOpListing:
  	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
  	v.type = FcTypeBool;
  	v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
  	FcValueDestroy (vl);
  	FcValueDestroy (vr);
  	break;	
      case FcOpOr:
      case FcOpAnd:
      case FcOpPlus:
      case FcOpMinus:
      case FcOpTimes:
      case FcOpDivide:
  	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
  	vle = FcConfigPromote (vl, vr, &buf1);
  	vre = FcConfigPromote (vr, vle, &buf2);
  	if (vle.type == vre.type)
  	{
  	    switch ((int) vle.type) {
  	    case FcTypeDouble:
  		switch ((int) op) {
  		case FcOpPlus:	
  		    v.type = FcTypeDouble;
  		    v.u.d = vle.u.d + vre.u.d;
  		    break;
  		case FcOpMinus:
  		    v.type = FcTypeDouble;
  		    v.u.d = vle.u.d - vre.u.d;
  		    break;
  		case FcOpTimes:
  		    v.type = FcTypeDouble;
  		    v.u.d = vle.u.d * vre.u.d;
  		    break;
  		case FcOpDivide:
  		    v.type = FcTypeDouble;
  		    v.u.d = vle.u.d / vre.u.d;
  		    break;
  		default:
  		    v.type = FcTypeVoid;
  		    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:
  		switch ((int) op) {
  		case FcOpOr:
  		    v.type = FcTypeBool;
  		    v.u.b = vle.u.b || vre.u.b;
  		    break;
  		case FcOpAnd:
  		    v.type = FcTypeBool;
  		    v.u.b = vle.u.b && vre.u.b;
  		    break;
  		default:
  		    v.type = FcTypeVoid;
  		    break;
  		}
  		break;
  	    case FcTypeString:
  		switch ((int) op) {
  		case FcOpPlus:
  		    v.type = FcTypeString;
  		    str = FcStrPlus (vle.u.s, vre.u.s);
  		    v.u.s = FcStrdup (str);
  		    FcStrFree (str);
  			
  		    if (!v.u.s)
  			v.type = FcTypeVoid;
  		    break;
  		default:
  		    v.type = FcTypeVoid;
  		    break;
  		}
  		break;
  	    case FcTypeMatrix:
  		switch ((int) op) {
  		case FcOpTimes:
  		    v.type = FcTypeMatrix;
  		    m = malloc (sizeof (FcMatrix));
  		    if (m)
  		    {
  			FcMatrixMultiply (m, vle.u.m, vre.u.m);
  			v.u.m = m;
  		    }
  		    else
  		    {
  			v.type = FcTypeVoid;
  		    }
  		    break;
  		default:
  		    v.type = FcTypeVoid;
  		    break;
  		}
  		break;
  	    case FcTypeCharSet:
  		switch ((int) op) {
  		case FcOpPlus:
  		    v.type = FcTypeCharSet;
  		    v.u.c = FcCharSetUnion (vle.u.c, vre.u.c);
  		    if (!v.u.c)
  			v.type = FcTypeVoid;
  		    break;
  		case FcOpMinus:
  		    v.type = FcTypeCharSet;
  		    v.u.c = FcCharSetSubtract (vle.u.c, vre.u.c);
  		    if (!v.u.c)
  			v.type = FcTypeVoid;
  		    break;
  		default:
  		    v.type = FcTypeVoid;
  		    break;
  		}
  		break;
  	    case FcTypeLangSet:
  		switch ((int) op) {
  		case FcOpPlus:
  		    v.type = FcTypeLangSet;
  		    v.u.l = FcLangSetUnion (vle.u.l, vre.u.l);
  		    if (!v.u.l)
  			v.type = FcTypeVoid;
  		    break;
  		case FcOpMinus:
  		    v.type = FcTypeLangSet;
  		    v.u.l = FcLangSetSubtract (vle.u.l, vre.u.l);
  		    if (!v.u.l)
  			v.type = FcTypeVoid;
  		    break;
  		default:
  		    v.type = FcTypeVoid;
  		    break;
  		}
  		break;
  	    default:
  		v.type = FcTypeVoid;
  		break;
  	    }
  	}
  	else
  	    v.type = FcTypeVoid;
  	FcValueDestroy (vl);
  	FcValueDestroy (vr);
  	break;
      case FcOpNot:
  	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	switch ((int) vl.type) {
  	case FcTypeBool:
  	    v.type = FcTypeBool;
  	    v.u.b = !vl.u.b;
  	    break;
  	default:
  	    v.type = FcTypeVoid;
  	    break;
  	}
  	FcValueDestroy (vl);
  	break;
      case FcOpFloor:
  	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	switch ((int) vl.type) {
  	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:
  	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	switch ((int) vl.type) {
  	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:
  	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	switch ((int) vl.type) {
  	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:
  	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	switch ((int) vl.type) {
  	case FcTypeInteger:
  	    v = vl;
  	    break;
  	case FcTypeDouble:
  	    v.type = FcTypeInteger;
  	    v.u.i = FcDoubleTrunc (vl.u.d);
  	    break;
  	default:
  	    v.type = FcTypeVoid;
  	    break;
  	}
  	FcValueDestroy (vl);
  	break;
      default:
  	v.type = FcTypeVoid;
  	break;
      }
      return v;
  }
  
  static FcValueList *
  FcConfigMatchValueList (FcPattern	*p,
  			FcPattern	*p_pat,
  			FcMatchKind      kind,
  			FcTest		*t,
  			FcValueList	*values)
  {
      FcValueList	    *ret = 0;
      FcExpr	    *e = t->expr;
      FcValue	    value;
      FcValueList	    *v;
  
      while (e)
      {
  	/* Compute the value of the match expression */
  	if (FC_OP_GET_OP (e->op) == FcOpComma)
  	{
  	    value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	    e = e->u.tree.right;
  	}
  	else
  	{
  	    value = FcConfigEvaluate (p, p_pat, kind, e);
  	    e = 0;
  	}
  
  	for (v = values; v; v = FcValueListNext(v))
  	{
  	    /* Compare the pattern value to the match expression value */
  	    if (FcConfigCompareValue (&v->value, t->op, &value))
  	    {
  		if (!ret)
  		    ret = v;
  	    }
  	    else
  	    {
  		if (t->qual == FcQualAll)
  		{
  		    ret = 0;
  		    break;
  		}
  	    }
  	}
  	FcValueDestroy (value);
      }
      return ret;
  }
  
  static FcValueList *
  FcConfigValues (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e, FcValueBinding binding)
  {
      FcValueList	*l;
  
      if (!e)
  	return 0;
      l = (FcValueList *) malloc (sizeof (FcValueList));
      if (!l)
  	return 0;
      if (FC_OP_GET_OP (e->op) == FcOpComma)
      {
  	l->value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
  	l->next = FcConfigValues (p, p_pat, kind, e->u.tree.right, binding);
      }
      else
      {
  	l->value = FcConfigEvaluate (p, p_pat, kind, e);
  	l->next = NULL;
      }
      l->binding = binding;
      if (l->value.type == FcTypeVoid)
      {
  	FcValueList  *next = FcValueListNext(l);
  
  	free (l);
  	l = next;
      }
  
      return l;
  }
  
  static FcBool
  FcConfigAdd (FcValueListPtr *head,
  	     FcValueList    *position,
  	     FcBool	    append,
  	     FcValueList    *new,
  	     FcObject        object)
  {
      FcValueListPtr  *prev, l, last, v;
      FcValueBinding  sameBinding;
  
      /*
       * Make sure the stored type is valid for built-in objects
       */
      for (l = new; l != NULL; l = FcValueListNext (l))
      {
  	if (!FcObjectValidType (object, l->value.type))
  	{
  	    fprintf (stderr,
  		     "Fontconfig warning: FcPattern object %s does not accept value", FcObjectName (object));
  	    FcValuePrintFile (stderr, l->value);
  	    fprintf (stderr, "\n");
  
  	    if (FcDebug () & FC_DBG_EDIT)
  	    {
  		printf ("Not adding\n");
  	    }
  
  	    return FcFalse;
  	}
      }
  
      if (position)
  	sameBinding = position->binding;
      else
  	sameBinding = FcValueBindingWeak;
      for (v = new; v != NULL; v = FcValueListNext(v))
  	if (v->binding == FcValueBindingSame)
  	    v->binding = sameBinding;
      if (append)
      {
  	if (position)
  	    prev = &position->next;
  	else
  	    for (prev = head; *prev != NULL;
  		 prev = &(*prev)->next)
  		;
      }
      else
      {
  	if (position)
  	{
  	    for (prev = head; *prev != NULL;
  		 prev = &(*prev)->next)
  	    {
  		if (*prev == position)
  		    break;
  	    }
  	}
  	else
  	    prev = head;
  
  	if (FcDebug () & FC_DBG_EDIT)
  	{
  	    if (*prev == NULL)
  		printf ("position not on list\n");
  	}
      }
  
      if (FcDebug () & FC_DBG_EDIT)
      {
  	printf ("%s list before ", append ? "Append" : "Prepend");
  	FcValueListPrintWithPosition (*head, *prev);
  	printf ("\n");
      }
  
      if (new)
      {
  	last = new;
  	while (last->next != NULL)
  	    last = last->next;
  
  	last->next = *prev;
  	*prev = new;
      }
  
      if (FcDebug () & FC_DBG_EDIT)
      {
  	printf ("%s list after ", append ? "Append" : "Prepend");
  	FcValueListPrint (*head);
  	printf ("\n");
      }
  
      return FcTrue;
  }
  
  static void
  FcConfigDel (FcValueListPtr *head,
  	     FcValueList    *position)
  {
      FcValueListPtr *prev;
  
      for (prev = head; *prev != NULL; prev = &(*prev)->next)
      {
  	if (*prev == position)
  	{
  	    *prev = position->next;
  	    position->next = NULL;
  	    FcValueListDestroy (position);
  	    break;
  	}
      }
  }
  
  static void
  FcConfigPatternAdd (FcPattern	*p,
  		    FcObject	object,
  		    FcValueList	*list,
  		    FcBool	append)
  {
      if (list)
      {
  	FcPatternElt    *e = FcPatternObjectInsertElt (p, object);
  
  	if (!e)
  	    return;
  	FcConfigAdd (&e->values, 0, append, list, object);
      }
  }
  
  /*
   * Delete all values associated with a field
   */
  static void
  FcConfigPatternDel (FcPattern	*p,
  		    FcObject	object)
  {
      FcPatternElt    *e = FcPatternObjectFindElt (p, object);
      if (!e)
  	return;
      while (e->values != NULL)
  	FcConfigDel (&e->values, e->values);
  }
  
  static void
  FcConfigPatternCanon (FcPattern	    *p,
  		      FcObject	    object)
  {
      FcPatternElt    *e = FcPatternObjectFindElt (p, object);
      if (!e)
  	return;
      if (e->values == NULL)
  	FcPatternObjectDel (p, object);
  }
  
  FcBool
  FcConfigSubstituteWithPat (FcConfig    *config,
  			   FcPattern   *p,
  			   FcPattern   *p_pat,
  			   FcMatchKind kind)
  {
      FcValue v;
      FcSubst	    *s;
      FcRule          *r;
      FcValueList	    *l, **value = NULL, *vl;
      FcPattern	    *m;
      FcStrSet	    *strs;
      FcObject	    object = FC_INVALID_OBJECT;
      FcPatternElt    **elt = NULL, *e;
      int		    i, nobjs;
      FcBool	    retval = FcTrue;
      FcTest	    **tst = NULL;
  
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return FcFalse;
      }
  
      switch (kind) {
      case FcMatchPattern:
  	s = config->substPattern;
  	strs = FcGetDefaultLangs ();
  	if (strs)
  	{
  	    FcStrList *l = FcStrListCreate (strs);
  	    FcChar8 *lang;
  	    FcValue v;
  
  	    FcStrSetDestroy (strs);
  	    while (l && (lang = FcStrListNext (l)))
  	    {
  		v.type = FcTypeString;
  		v.u.s = lang;
  		FcPatternObjectAddWithBinding (p, FC_LANG_OBJECT, v, FcValueBindingWeak, FcTrue);
  	    }
  	    FcStrListDone (l);
  	}
  	if (FcPatternObjectGet (p, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch)
  	{
  	    FcChar8 *prgname = FcGetPrgname ();
  	    if (prgname)
  		FcPatternObjectAddString (p, FC_PRGNAME_OBJECT, prgname);
  	}
  	break;
      case FcMatchFont:
  	s = config->substFont;
  	break;
      case FcMatchScan:
  	s = config->substScan;
  	break;
      default:
  	return FcFalse;
      }
  
      nobjs = FC_MAX_BASE_OBJECT + config->maxObjects + 2;
      value = (FcValueList **) malloc (SIZEOF_VOID_P * nobjs);
      if (!value)
      {
  	retval = FcFalse;
  	goto bail1;
      }
      elt = (FcPatternElt **) malloc (SIZEOF_VOID_P * nobjs);
      if (!elt)
      {
  	retval = FcFalse;
  	goto bail1;
      }
      tst = (FcTest **) malloc (SIZEOF_VOID_P * nobjs);
      if (!tst)
      {
  	retval = FcFalse;
  	goto bail1;
      }
  
      if (FcDebug () & FC_DBG_EDIT)
      {
  	printf ("FcConfigSubstitute ");
  	FcPatternPrint (p);
      }
      for (; s; s = s->next)
      {
  	r = s->rule;
  	for (i = 0; i < nobjs; i++)
  	{
  	    elt[i] = NULL;
  	    value[i] = NULL;
  	    tst[i] = NULL;
  	}
  	for (; r; r = r->next)
  	{
  	    switch (r->type) {
  	    case FcRuleUnknown:
  		/* shouldn't be reached */
  		break;
  	    case FcRuleTest:
  		object = FC_OBJ_ID (r->u.test->object);
  		/*
  		 * Check the tests to see if
  		 * they all match the pattern
  		 */
  		if (FcDebug () & FC_DBG_EDIT)
  		{
  		    printf ("FcConfigSubstitute test ");
  		    FcTestPrint (r->u.test);
  		}
  		if (kind == FcMatchFont && r->u.test->kind == FcMatchPattern)
  		    m = p_pat;
  		else
  		    m = p;
  		if (m)
  		    e = FcPatternObjectFindElt (m, r->u.test->object);
  		else
  		    e = NULL;
  		/* different 'kind' won't be the target of edit */
  		if (!elt[object] && kind == r->u.test->kind)
  		{
  		    elt[object] = e;
  		    tst[object] = r->u.test;
  		}
  		/*
  		 * If there's no such field in the font,
  		 * then FcQualAll matches while FcQualAny does not
  		 */
  		if (!e)
  		{
  		    if (r->u.test->qual == FcQualAll)
  		    {
  			value[object] = NULL;
  			continue;
  		    }
  		    else
  		    {
  			if (FcDebug () & FC_DBG_EDIT)
  			    printf ("No match\n");
  			goto bail;
  		    }
  		}
  		/*
  		 * Check to see if there is a match, mark the location
  		 * to apply match-relative edits
  		 */
  		vl = FcConfigMatchValueList (m, p_pat, kind, r->u.test, e->values);
  		/* different 'kind' won't be the target of edit */
  		if (!value[object] && kind == r->u.test->kind)
  		    value[object] = vl;
  		if (!vl ||
  		    (r->u.test->qual == FcQualFirst && vl != e->values) ||
  		    (r->u.test->qual == FcQualNotFirst && vl == e->values))
  		{
  		    if (FcDebug () & FC_DBG_EDIT)
  			printf ("No match\n");
  		    goto bail;
  		}
  		break;
  	    case FcRuleEdit:
  		object = FC_OBJ_ID (r->u.edit->object);
  		if (FcDebug () & FC_DBG_EDIT)
  		{
  		    printf ("Substitute ");
  		    FcEditPrint (r->u.edit);
  		    printf ("\n\n");
  		}
  		/*
  		 * Evaluate the list of expressions
  		 */
  		l = FcConfigValues (p, p_pat,kind,  r->u.edit->expr, r->u.edit->binding);
  		if (tst[object] && (tst[object]->kind == FcMatchFont || kind == FcMatchPattern))
  		    elt[object] = FcPatternObjectFindElt (p, tst[object]->object);
  
  		switch (FC_OP_GET_OP (r->u.edit->op)) {
  		case FcOpAssign:
  		    /*
  		     * If there was a test, then replace the matched
  		     * value with the new list of values
  		     */
  		    if (value[object])
  		    {
  			FcValueList	*thisValue = value[object];
  			FcValueList	*nextValue = l;
  
  			/*
  			 * Append the new list of values after the current value
  			 */
  			FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object);
  			/*
  			 * Delete the marked value
  			 */
  			if (thisValue)
  			    FcConfigDel (&elt[object]->values, thisValue);
  			/*
  			 * Adjust a pointer into the value list to ensure
  			 * future edits occur at the same place
  			 */
  			value[object] = nextValue;
  			break;
  		    }
  		    /* fall through ... */
  		case FcOpAssignReplace:
  		    /*
  		     * Delete all of the values and insert
  		     * the new set
  		     */
  		    FcConfigPatternDel (p, r->u.edit->object);
  		    FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue);
  		    /*
  		     * Adjust a pointer into the value list as they no
  		     * longer point to anything valid
  		     */
  		    value[object] = NULL;
  		    break;
  		case FcOpPrepend:
  		    if (value[object])
  		    {
  			FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object);
  			break;
  		    }
  		    /* fall through ... */
  		case FcOpPrependFirst:
  		    FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse);
  		    break;
  		case FcOpAppend:
  		    if (value[object])
  		    {
  			FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object);
  			break;
  		    }
  		    /* fall through ... */
  		case FcOpAppendLast:
  		    FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue);
  		    break;
  		case FcOpDelete:
  		    if (value[object])
  		    {
  			FcConfigDel (&elt[object]->values, value[object]);
  			break;
  		    }
  		    /* fall through ... */
  		case FcOpDeleteAll:
  		    FcConfigPatternDel (p, r->u.edit->object);
  		    break;
  		default:
  		    FcValueListDestroy (l);
  		    break;
  		}
  		/*
  		 * Now go through the pattern and eliminate
  		 * any properties without data
  		 */
  		FcConfigPatternCanon (p, r->u.edit->object);
  
  		if (FcDebug () & FC_DBG_EDIT)
  		{
  		    printf ("FcConfigSubstitute edit");
  		    FcPatternPrint (p);
  		}
  		break;
  	    }
  	}
      bail:;
      }
      if (FcDebug () & FC_DBG_EDIT)
      {
  	printf ("FcConfigSubstitute done");
  	FcPatternPrint (p);
      }
  bail1:
      if (elt)
  	free (elt);
      if (value)
  	free (value);
      if (tst)
  	free (tst);
  
      return retval;
  }
  
  FcBool
  FcConfigSubstitute (FcConfig	*config,
  		    FcPattern	*p,
  		    FcMatchKind	kind)
  {
      return FcConfigSubstituteWithPat (config, p, 0, kind);
  }
  
  #if defined (_WIN32)
  
  static FcChar8 fontconfig_path[1000] = ""; /* MT-dontcare */
  
  #  if (defined (PIC) || defined (DLL_EXPORT))
  
  BOOL WINAPI
  DllMain (HINSTANCE hinstDLL,
  	 DWORD     fdwReason,
  	 LPVOID    lpvReserved);
  
  BOOL WINAPI
  DllMain (HINSTANCE hinstDLL,
  	 DWORD     fdwReason,
  	 LPVOID    lpvReserved)
  {
    FcChar8 *p;
  
    switch (fdwReason) {
    case DLL_PROCESS_ATTACH:
        if (!GetModuleFileName ((HMODULE) hinstDLL, (LPCH) fontconfig_path,
  			      sizeof (fontconfig_path)))
  	  break;
  
        /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
         * assume it's a Unix-style installation tree, and use
         * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
         * folder where the DLL is as FONTCONFIG_PATH.
         */
        p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
        if (p)
        {
  	  *p = '\0';
  	  p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
  	  if (p && (FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "bin") == 0 ||
  		    FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "lib") == 0))
  	      *p = '\0';
  	  strcat ((char *) fontconfig_path, "\\etc\\fonts");
        }
        else
            fontconfig_path[0] = '\0';
  
        break;
    }
  
    return TRUE;
  }
  
  #  endif /* !PIC */
  
  #undef FONTCONFIG_PATH
  #define FONTCONFIG_PATH fontconfig_path
  
  #endif /* !_WIN32 */
  
  #ifndef FONTCONFIG_FILE
  #define FONTCONFIG_FILE	"fonts.conf"
  #endif
  
  static FcChar8 *
  FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
  {
      FcChar8    *path;
      int         size, osize;
  
      if (!dir)
  	dir = (FcChar8 *) "";
  
      osize = strlen ((char *) dir) + 1 + strlen ((char *) file) + 1;
      /*
       * workaround valgrind warning because glibc takes advantage of how it knows memory is
       * allocated to implement strlen by reading in groups of 4
       */
      size = (osize + 3) & ~3;
  
      path = malloc (size);
      if (!path)
  	return 0;
  
      strcpy ((char *) path, (const char *) dir);
      /* make sure there's a single separator */
  #ifdef _WIN32
      if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
  		      path[strlen((char *) path)-1] != '\\')) &&
  	!(file[0] == '/' ||
  	  file[0] == '\\' ||
  	  (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
  	strcat ((char *) path, "\\");
  #else
      if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
  	strcat ((char *) path, "/");
      else
  	osize--;
  #endif
      strcat ((char *) path, (char *) file);
  
      if (access ((char *) path, R_OK) == 0)
  	return path;
  
      FcStrFree (path);
  
      return 0;
  }
  
  static FcChar8 **
  FcConfigGetPath (void)
  {
      FcChar8    **path;
      FcChar8    *env, *e, *colon;
      FcChar8    *dir;
      int	    npath;
      int	    i;
  
      npath = 2;	/* default dir + null */
      env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
      if (env)
      {
  	e = env;
  	npath++;
  	while (*e)
  	    if (*e++ == FC_SEARCH_PATH_SEPARATOR)
  		npath++;
      }
      path = calloc (npath, sizeof (FcChar8 *));
      if (!path)
  	goto bail0;
      i = 0;
  
      if (env)
      {
  	e = env;
  	while (*e)
  	{
  	    colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
  	    if (!colon)
  		colon = e + strlen ((char *) e);
  	    path[i] = malloc (colon - e + 1);
  	    if (!path[i])
  		goto bail1;
  	    strncpy ((char *) path[i], (const char *) e, colon - e);
  	    path[i][colon - e] = '\0';
  	    if (*colon)
  		e = colon + 1;
  	    else
  		e = colon;
  	    i++;
  	}
      }
  
  #ifdef _WIN32
  	if (fontconfig_path[0] == '\0')
  	{
  		char *p;
  		if(!GetModuleFileName(NULL, (LPCH) fontconfig_path, sizeof(fontconfig_path)))
  			goto bail1;
  		p = strrchr ((const char *) fontconfig_path, '\\');
  		if (p) *p = '\0';
  		strcat ((char *) fontconfig_path, "\\fonts");
  	}
  #endif
      dir = (FcChar8 *) FONTCONFIG_PATH;
      path[i] = malloc (strlen ((char *) dir) + 1);
      if (!path[i])
  	goto bail1;
      strcpy ((char *) path[i], (const char *) dir);
      return path;
  
  bail1:
      for (i = 0; path[i]; i++)
  	free (path[i]);
      free (path);
  bail0:
      return 0;
  }
  
  static void
  FcConfigFreePath (FcChar8 **path)
  {
      FcChar8    **p;
  
      for (p = path; *p; p++)
  	free (*p);
      free (path);
  }
  
  static FcBool	_FcConfigHomeEnabled = FcTrue; /* MT-goodenough */
  
  FcChar8 *
  FcConfigHome (void)
  {
      if (_FcConfigHomeEnabled)
      {
          char *home = getenv ("HOME");
  
  #ifdef _WIN32
  	if (home == NULL)
  	    home = getenv ("USERPROFILE");
  #endif
  
  	return (FcChar8 *) home;
      }
      return 0;
  }
  
  FcChar8 *
  FcConfigXdgCacheHome (void)
  {
      const char *env = getenv ("XDG_CACHE_HOME");
      FcChar8 *ret = NULL;
  
      if (env)
  	ret = FcStrCopy ((const FcChar8 *)env);
      else
      {
  	const FcChar8 *home = FcConfigHome ();
  	size_t len = home ? strlen ((const char *)home) : 0;
  
  	ret = malloc (len + 7 + 1);
  	if (ret)
  	{
  	    memcpy (ret, home, len);
  	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".cache", 7);
  	    ret[len + 7] = 0;
  	}
      }
  
      return ret;
  }
  
  FcChar8 *
  FcConfigXdgConfigHome (void)
  {
      const char *env = getenv ("XDG_CONFIG_HOME");
      FcChar8 *ret = NULL;
  
      if (env)
  	ret = FcStrCopy ((const FcChar8 *)env);
      else
      {
  	const FcChar8 *home = FcConfigHome ();
  	size_t len = home ? strlen ((const char *)home) : 0;
  
  	ret = malloc (len + 8 + 1);
  	if (ret)
  	{
  	    memcpy (ret, home, len);
  	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".config", 8);
  	    ret[len + 8] = 0;
  	}
      }
  
      return ret;
  }
  
  FcChar8 *
  FcConfigXdgDataHome (void)
  {
      const char *env = getenv ("XDG_DATA_HOME");
      FcChar8 *ret = NULL;
  
      if (env)
  	ret = FcStrCopy ((const FcChar8 *)env);
      else
      {
  	const FcChar8 *home = FcConfigHome ();
  	size_t len = home ? strlen ((const char *)home) : 0;
  
  	ret = malloc (len + 13 + 1);
  	if (ret)
  	{
  	    memcpy (ret, home, len);
  	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".local" FC_DIR_SEPARATOR_S "share", 13);
  	    ret[len + 13] = 0;
  	}
      }
  
      return ret;
  }
  
  FcBool
  FcConfigEnableHome (FcBool enable)
  {
      FcBool  prev = _FcConfigHomeEnabled;
      _FcConfigHomeEnabled = enable;
      return prev;
  }
  
  FcChar8 *
  FcConfigFilename (const FcChar8 *url)
  {
      FcChar8    *file, *dir, **path, **p;
  
      if (!url || !*url)
      {
  	url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
  	if (!url)
  	    url = (FcChar8 *) FONTCONFIG_FILE;
      }
      file = 0;
  
  #ifdef _WIN32
      if (isalpha (*url) &&
  	url[1] == ':' &&
  	(url[2] == '/' || url[2] == '\\'))
  	goto absolute_path;
  #endif
  
      switch (*url) {
      case '~':
  	dir = FcConfigHome ();
  	if (dir)
  	    file = FcConfigFileExists (dir, url + 1);
  	else
  	    file = 0;
  	break;
  #ifdef _WIN32
      case '\\':
      absolute_path:
  #endif
      case '/':
  	file = FcConfigFileExists (0, url);
  	break;
      default:
  	path = FcConfigGetPath ();
  	if (!path)
  	    return NULL;
  	for (p = path; *p; p++)
  	{
  	    file = FcConfigFileExists (*p, url);
  	    if (file)
  		break;
  	}
  	FcConfigFreePath (path);
  	break;
      }
  
      return file;
  }
  
  /*
   * Manage the application-specific fonts
   */
  
  FcBool
  FcConfigAppFontAddFile (FcConfig    *config,
  			const FcChar8  *file)
  {
      FcFontSet	*set;
      FcStrSet	*subdirs;
      FcStrList	*sublist;
      FcChar8	*subdir;
  
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return FcFalse;
      }
  
      subdirs = FcStrSetCreate ();
      if (!subdirs)
  	return FcFalse;
  
      set = FcConfigGetFonts (config, FcSetApplication);
      if (!set)
      {
  	set = FcFontSetCreate ();
  	if (!set)
  	{
  	    FcStrSetDestroy (subdirs);
  	    return FcFalse;
  	}
  	FcConfigSetFonts (config, set, FcSetApplication);
      }
  	
      if (!FcFileScanConfig (set, subdirs, config->blanks, file, config))
      {
  	FcStrSetDestroy (subdirs);
  	return FcFalse;
      }
      if ((sublist = FcStrListCreate (subdirs)))
      {
  	while ((subdir = FcStrListNext (sublist)))
  	{
  	    FcConfigAppFontAddDir (config, subdir);
  	}
  	FcStrListDone (sublist);
      }
      FcStrSetDestroy (subdirs);
      return FcTrue;
  }
  
  FcBool
  FcConfigAppFontAddDir (FcConfig	    *config,
  		       const FcChar8   *dir)
  {
      FcFontSet	*set;
      FcStrSet	*dirs;
  
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return FcFalse;
      }
  
      dirs = FcStrSetCreate ();
      if (!dirs)
  	return FcFalse;
  
      set = FcConfigGetFonts (config, FcSetApplication);
      if (!set)
      {
  	set = FcFontSetCreate ();
  	if (!set)
  	{
  	    FcStrSetDestroy (dirs);
  	    return FcFalse;
  	}
  	FcConfigSetFonts (config, set, FcSetApplication);
      }
  
      FcStrSetAddFilename (dirs, dir);
  
      if (!FcConfigAddDirList (config, FcSetApplication, dirs))
      {
  	FcStrSetDestroy (dirs);
  	return FcFalse;
      }
      FcStrSetDestroy (dirs);
      return FcTrue;
  }
  
  void
  FcConfigAppFontClear (FcConfig	    *config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return;
      }
  
      FcConfigSetFonts (config, 0, FcSetApplication);
  }
  
  /*
   * Manage filename-based font source selectors
   */
  
  FcBool
  FcConfigGlobAdd (FcConfig	*config,
  		 const FcChar8  *glob,
  		 FcBool		accept)
  {
      FcStrSet	*set = accept ? config->acceptGlobs : config->rejectGlobs;
  
      return FcStrSetAdd (set, glob);
  }
  
  static FcBool
  FcConfigGlobsMatch (const FcStrSet	*globs,
  		    const FcChar8	*string)
  {
      int	i;
  
      for (i = 0; i < globs->num; i++)
  	if (FcStrGlobMatch (globs->strs[i], string))
  	    return FcTrue;
      return FcFalse;
  }
  
  FcBool
  FcConfigAcceptFilename (FcConfig	*config,
  			const FcChar8	*filename)
  {
      if (FcConfigGlobsMatch (config->acceptGlobs, filename))
  	return FcTrue;
      if (FcConfigGlobsMatch (config->rejectGlobs, filename))
  	return FcFalse;
      return FcTrue;
  }
  
  /*
   * Manage font-pattern based font source selectors
   */
  
  FcBool
  FcConfigPatternsAdd (FcConfig	*config,
  		     FcPattern	*pattern,
  		     FcBool	accept)
  {
      FcFontSet	*set = accept ? config->acceptPatterns : config->rejectPatterns;
  
      return FcFontSetAdd (set, pattern);
  }
  
  static FcBool
  FcConfigPatternsMatch (const FcFontSet	*patterns,
  		       const FcPattern	*font)
  {
      int i;
  
      for (i = 0; i < patterns->nfont; i++)
  	if (FcListPatternMatchAny (patterns->fonts[i], font))
  	    return FcTrue;
      return FcFalse;
  }
  
  FcBool
  FcConfigAcceptFont (FcConfig	    *config,
  		    const FcPattern *font)
  {
      if (FcConfigPatternsMatch (config->acceptPatterns, font))
  	return FcTrue;
      if (FcConfigPatternsMatch (config->rejectPatterns, font))
  	return FcFalse;
      return FcTrue;
  }
  
  const FcChar8 *
  FcConfigGetSysRoot (const FcConfig *config)
  {
      if (!config)
      {
  	config = FcConfigGetCurrent ();
  	if (!config)
  	    return NULL;
      }
  
      return config->sysRoot;
  }
  
  void
  FcConfigSetSysRoot (FcConfig      *config,
  		    const FcChar8 *sysroot)
  {
      FcChar8 *s;
      FcBool init = FcFalse;
  
      if (!config)
      {
  	/* We can't use FcConfigGetCurrent() here to ensure
  	 * the sysroot is set prior to initialize FcConfig,
  	 * to avoid loading caches from non-sysroot dirs.
  	 * So postpone the initialization later.
  	 */
  	config = fc_atomic_ptr_get (&_fcConfig);
  	if (!config)
  	{
  	    config = FcConfigCreate ();
  	    if (!config)
  		return;
  	    init = FcTrue;
  	}
      }
  
      s = FcStrCopyFilename (sysroot);
      if (!s)
  	return;
  
      if (config->sysRoot)
  	FcStrFree (config->sysRoot);
  
      config->sysRoot = s;
      if (init)
      {
  	config = FcInitLoadOwnConfigAndFonts (config);
  	FcConfigSetCurrent (config);
      }
  }
  
  #define __fccfg__
  #include "fcaliastail.h"
  #undef __fccfg__
  