text: /*
   * 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.
   */
  
  #include "fcint.h"
  #include "fcftint.h"
  
  /* Objects MT-safe for readonly access. */
  
  FcPattern *
  FcPatternCreate (void)
  {
      FcPattern	*p;
  
      p = (FcPattern *) malloc (sizeof (FcPattern));
      if (!p)
  	return 0;
      p->num = 0;
      p->size = 0;
      p->elts_offset = FcPtrToOffset (p, NULL);
      FcRefInit (&p->ref, 1);
      return p;
  }
  
  void
  FcValueDestroy (FcValue v)
  {
      switch ((int) v.type) {
      case FcTypeString:
  	FcFree (v.u.s);
  	break;
      case FcTypeMatrix:
  	FcMatrixFree ((FcMatrix *) v.u.m);
  	break;
      case FcTypeCharSet:
  	FcCharSetDestroy ((FcCharSet *) v.u.c);
  	break;
      case FcTypeLangSet:
  	FcLangSetDestroy ((FcLangSet *) v.u.l);
  	break;
      default:
  	break;
      }
  }
  
  FcValue
  FcValueCanonicalize (const FcValue *v)
  {
      FcValue new;
  
      switch ((int) v->type)
      {
      case FcTypeString:
  	new.u.s = FcValueString(v);
  	new.type = FcTypeString;
  	break;
      case FcTypeCharSet:
  	new.u.c = FcValueCharSet(v);
  	new.type = FcTypeCharSet;
  	break;
      case FcTypeLangSet:
  	new.u.l = FcValueLangSet(v);
  	new.type = FcTypeLangSet;
  	break;
      default:
  	new = *v;
  	break;
      }
      return new;
  }
  
  FcValue
  FcValueSave (FcValue v)
  {
      switch ((int) v.type) {
      case FcTypeString:
  	v.u.s = FcStrdup (v.u.s);
  	if (!v.u.s)
  	    v.type = FcTypeVoid;
  	break;
      case FcTypeMatrix:
  	v.u.m = FcMatrixCopy (v.u.m);
  	if (!v.u.m)
  	    v.type = FcTypeVoid;
  	break;
      case FcTypeCharSet:
  	v.u.c = FcCharSetCopy ((FcCharSet *) v.u.c);
  	if (!v.u.c)
  	    v.type = FcTypeVoid;
  	break;
      case FcTypeLangSet:
  	v.u.l = FcLangSetCopy (v.u.l);
  	if (!v.u.l)
  	    v.type = FcTypeVoid;
  	break;
      default:
  	break;
      }
      return v;
  }
  
  FcValueListPtr
  FcValueListCreate (void)
  {
      return calloc (1, sizeof (FcValueList));
  }
  
  void
  FcValueListDestroy (FcValueListPtr l)
  {
      FcValueListPtr next;
      for (; l; l = next)
      {
  	switch ((int) l->value.type) {
  	case FcTypeString:
  	    FcFree (l->value.u.s);
  	    break;
  	case FcTypeMatrix:
  	    FcMatrixFree ((FcMatrix *)l->value.u.m);
  	    break;
  	case FcTypeCharSet:
  	    FcCharSetDestroy
  		((FcCharSet *) (l->value.u.c));
  	    break;
  	case FcTypeLangSet:
  	    FcLangSetDestroy
  		((FcLangSet *) (l->value.u.l));
  	    break;
  	default:
  	    break;
  	}
  	next = FcValueListNext(l);
  	free(l);
      }
  }
  
  FcValueListPtr
  FcValueListPrepend (FcValueListPtr vallist,
  		    FcValue        value,
  		    FcValueBinding binding)
  {
      FcValueListPtr new;
  
      if (value.type == FcTypeVoid)
  	return vallist;
      new = FcValueListCreate ();
      if (!new)
  	return vallist;
  
      new->value = FcValueSave (value);
      new->binding = binding;
      new->next = vallist;
  
      return new;
  }
  
  FcValueListPtr
  FcValueListAppend (FcValueListPtr vallist,
  		   FcValue        value,
  		   FcValueBinding binding)
  {
      FcValueListPtr new, last;
  
      if (value.type == FcTypeVoid)
  	return vallist;
      new = FcValueListCreate ();
      if (!new)
  	return vallist;
  
      new->value = FcValueSave (value);
      new->binding = binding;
      new->next = NULL;
  
      if (vallist)
      {
  	for (last = vallist; FcValueListNext (last); last = FcValueListNext (last));
  
  	last->next = new;
      }
      else
  	vallist = new;
  
      return vallist;
  }
  
  FcValueListPtr
  FcValueListDuplicate(FcValueListPtr orig)
  {
      FcValueListPtr new = NULL, l, t = NULL;
      FcValue v;
  
      for (l = orig; l != NULL; l = FcValueListNext (l))
      {
  	if (!new)
  	{
  	    t = new = FcValueListCreate();
  	}
  	else
  	{
  	    t->next = FcValueListCreate();
  	    t = FcValueListNext (t);
  	}
  	v = FcValueCanonicalize (&l->value);
  	t->value = FcValueSave (v);
  	t->binding = l->binding;
  	t->next = NULL;
      }
  
      return new;
  }
  
  FcBool
  FcValueEqual (FcValue va, FcValue vb)
  {
      if (va.type != vb.type)
      {
  	if (va.type == FcTypeInteger)
  	{
  	    va.type = FcTypeDouble;
  	    va.u.d = va.u.i;
  	}
  	if (vb.type == FcTypeInteger)
  	{
  	    vb.type = FcTypeDouble;
  	    vb.u.d = vb.u.i;
  	}
  	if (va.type != vb.type)
  	    return FcFalse;
      }
      switch (va.type) {
      case FcTypeUnknown:
  	return FcFalse;	/* don't know how to compare this object */
      case FcTypeVoid:
  	return FcTrue;
      case FcTypeInteger:
  	return va.u.i == vb.u.i;
      case FcTypeDouble:
  	return va.u.d == vb.u.d;
      case FcTypeString:
  	return FcStrCmpIgnoreCase (va.u.s, vb.u.s) == 0;
      case FcTypeBool:
  	return va.u.b == vb.u.b;
      case FcTypeMatrix:
  	return FcMatrixEqual (va.u.m, vb.u.m);
      case FcTypeCharSet:
  	return FcCharSetEqual (va.u.c, vb.u.c);
      case FcTypeFTFace:
  	return va.u.f == vb.u.f;
      case FcTypeLangSet:
  	return FcLangSetEqual (va.u.l, vb.u.l);
      }
      return FcFalse;
  }
  
  static FcChar32
  FcDoubleHash (double d)
  {
      if (d < 0)
  	d = -d;
      if (d > 0xffffffff)
  	d = 0xffffffff;
      return (FcChar32) d;
  }
  
  FcChar32
  FcStringHash (const FcChar8 *s)
  {
      FcChar8	c;
      FcChar32	h = 0;
  
      if (s)
  	while ((c = *s++))
  	    h = ((h << 1) | (h >> 31)) ^ c;
      return h;
  }
  
  static FcChar32
  FcValueHash (const FcValue *v)
  {
      switch (v->type) {
      case FcTypeUnknown:
      case FcTypeVoid:
  	return 0;
      case FcTypeInteger:
  	return (FcChar32) v->u.i;
      case FcTypeDouble:
  	return FcDoubleHash (v->u.d);
      case FcTypeString:
  	return FcStringHash (FcValueString(v));
      case FcTypeBool:
  	return (FcChar32) v->u.b;
      case FcTypeMatrix:
  	return (FcDoubleHash (v->u.m->xx) ^
  		FcDoubleHash (v->u.m->xy) ^
  		FcDoubleHash (v->u.m->yx) ^
  		FcDoubleHash (v->u.m->yy));
      case FcTypeCharSet:
  	return (FcChar32) FcValueCharSet(v)->num;
      case FcTypeFTFace:
  	return FcStringHash ((const FcChar8 *) ((FT_Face) v->u.f)->family_name) ^
  	       FcStringHash ((const FcChar8 *) ((FT_Face) v->u.f)->style_name);
      case FcTypeLangSet:
  	return FcLangSetHash (FcValueLangSet(v));
      }
      return 0;
  }
  
  static FcBool
  FcValueListEqual (FcValueListPtr la, FcValueListPtr lb)
  {
      if (la == lb)
  	return FcTrue;
  
      while (la && lb)
      {
  	if (!FcValueEqual (la->value, lb->value))
  	    return FcFalse;
  	la = FcValueListNext(la);
  	lb = FcValueListNext(lb);
      }
      if (la || lb)
  	return FcFalse;
      return FcTrue;
  }
  
  static FcChar32
  FcValueListHash (FcValueListPtr l)
  {
      FcChar32	hash = 0;
  
      for (; l; l = FcValueListNext(l))
      {
  	hash = ((hash << 1) | (hash >> 31)) ^ FcValueHash (&l->value);
      }
      return hash;
  }
  
  void
  FcPatternDestroy (FcPattern *p)
  {
      int		    i;
      FcPatternElt    *elts;
  
      if (!p)
  	return;
  
      if (FcRefIsConst (&p->ref))
      {
  	FcCacheObjectDereference (p);
  	return;
      }
  	
      if (FcRefDec (&p->ref) != 1)
  	return;
  
      elts = FcPatternElts (p);
      for (i = 0; i < p->num; i++)
  	FcValueListDestroy (FcPatternEltValues(&elts[i]));
  
      free (elts);
      free (p);
  }
  
  static int
  FcPatternObjectPosition (const FcPattern *p, FcObject object)
  {
      int	    low, high, mid, c;
      FcPatternElt    *elts = FcPatternElts(p);
  
      low = 0;
      high = p->num - 1;
      c = 1;
      mid = 0;
      while (low <= high)
      {
  	mid = (low + high) >> 1;
  	c = elts[mid].object - object;
  	if (c == 0)
  	    return mid;
  	if (c < 0)
  	    low = mid + 1;
  	else
  	    high = mid - 1;
      }
      if (c < 0)
  	mid++;
      return -(mid + 1);
  }
  
  FcPatternElt *
  FcPatternObjectFindElt (const FcPattern *p, FcObject object)
  {
      int	    i = FcPatternObjectPosition (p, object);
      if (i < 0)
  	return 0;
      return &FcPatternElts(p)[i];
  }
  
  FcPatternElt *
  FcPatternObjectInsertElt (FcPattern *p, FcObject object)
  {
      int		    i;
      FcPatternElt   *e;
  
      i = FcPatternObjectPosition (p, object);
      if (i < 0)
      {
  	i = -i - 1;
  
  	/* reallocate array */
  	if (p->num + 1 >= p->size)
  	{
  	    int s = p->size + 16;
  	    if (p->size)
  	    {
  		FcPatternElt *e0 = FcPatternElts(p);
  		e = (FcPatternElt *) realloc (e0, s * sizeof (FcPatternElt));
  		if (!e) /* maybe it was mmapped */
  		{
  		    e = malloc(s * sizeof (FcPatternElt));
  		    if (e)
  			memcpy(e, e0, p->num * sizeof (FcPatternElt));
  		}
  	    }
  	    else
  		e = (FcPatternElt *) malloc (s * sizeof (FcPatternElt));
  	    if (!e)
  		return FcFalse;
  	    p->elts_offset = FcPtrToOffset (p, e);
  	    while (p->size < s)
  	    {
  		e[p->size].object = 0;
  		e[p->size].values = NULL;
  		p->size++;
  	    }
  	}
  	
  	e = FcPatternElts(p);
  	/* move elts up */
  	memmove (e + i + 1,
  		 e + i,
  		 sizeof (FcPatternElt) *
  		 (p->num - i));
  		
  	/* bump count */
  	p->num++;
  	
  	e[i].object = object;
  	e[i].values = NULL;
      }
  
      return FcPatternElts(p) + i;
  }
  
  FcBool
  FcPatternEqual (const FcPattern *pa, const FcPattern *pb)
  {
      int	i;
      FcPatternElt   *pae, *pbe;
  
      if (pa == pb)
  	return FcTrue;
  
      if (pa->num != pb->num)
  	return FcFalse;
      pae = FcPatternElts(pa);
      pbe = FcPatternElts(pb);
      for (i = 0; i < pa->num; i++)
      {
  	if (pae[i].object != pbe[i].object)
  	    return FcFalse;
  	if (!FcValueListEqual (FcPatternEltValues(&pae[i]),
  			       FcPatternEltValues(&pbe[i])))
  	    return FcFalse;
      }
      return FcTrue;
  }
  
  FcChar32
  FcPatternHash (const FcPattern *p)
  {
      int		i;
      FcChar32	h = 0;
      FcPatternElt    *pe = FcPatternElts(p);
  
      for (i = 0; i < p->num; i++)
      {
  	h = (((h << 1) | (h >> 31)) ^
  	     pe[i].object ^
  	     FcValueListHash (FcPatternEltValues(&pe[i])));
      }
      return h;
  }
  
  FcBool
  FcPatternEqualSubset (const FcPattern *pai, const FcPattern *pbi, const FcObjectSet *os)
  {
      FcPatternElt    *ea, *eb;
      int		    i;
  
      for (i = 0; i < os->nobject; i++)
      {
  	FcObject    object = FcObjectFromName (os->objects[i]);
  	ea = FcPatternObjectFindElt (pai, object);
  	eb = FcPatternObjectFindElt (pbi, object);
  	if (ea)
  	{
  	    if (!eb)
  		return FcFalse;
  	    if (!FcValueListEqual (FcPatternEltValues(ea), FcPatternEltValues(eb)))
  		return FcFalse;
  	}
  	else
  	{
  	    if (eb)
  		return FcFalse;
  	}
      }
      return FcTrue;
  }
  
  FcBool
  FcPatternObjectListAdd (FcPattern	*p,
  			FcObject	object,
  			FcValueListPtr	list,
  			FcBool		append)
  {
      FcPatternElt   *e;
      FcValueListPtr l, *prev;
  
      if (FcRefIsConst (&p->ref))
  	goto bail0;
  
      /*
       * Make sure the stored type is valid for built-in objects
       */
      for (l = list; 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");
  	    goto bail0;
  	}
      }
  
      e = FcPatternObjectInsertElt (p, object);
      if (!e)
  	goto bail0;
  
      if (append)
      {
  	for (prev = &e->values; *prev; prev = &(*prev)->next)
  	    ;
  	*prev = list;
      }
      else
      {
  	for (prev = &list; *prev; prev = &(*prev)->next)
  	    ;
  	*prev = e->values;
  	e->values = list;
      }
  
      return FcTrue;
  
  bail0:
      return FcFalse;
  }
  
  FcBool
  FcPatternObjectAddWithBinding  (FcPattern	*p,
  				FcObject	object,
  				FcValue		value,
  				FcValueBinding  binding,
  				FcBool		append)
  {
      FcPatternElt   *e;
      FcValueListPtr new, *prev;
  
      if (FcRefIsConst (&p->ref))
  	goto bail0;
  
      new = FcValueListCreate ();
      if (!new)
  	goto bail0;
  
      value = FcValueSave (value);
      if (value.type == FcTypeVoid)
  	goto bail1;
  
      /*
       * Make sure the stored type is valid for built-in objects
       */
      if (!FcObjectValidType (object, value.type))
      {
  	fprintf (stderr,
  		 "Fontconfig warning: FcPattern object %s does not accept value",
  		 FcObjectName (object));
  	FcValuePrintFile (stderr, value);
  	fprintf (stderr, "\n");
  	goto bail1;
      }
  
      new->value = value;
      new->binding = binding;
      new->next = NULL;
  
      e = FcPatternObjectInsertElt (p, object);
      if (!e)
  	goto bail2;
  
      if (append)
      {
  	for (prev = &e->values; *prev; prev = &(*prev)->next)
  	    ;
  	*prev = new;
      }
      else
      {
  	new->next = e->values;
  	e->values = new;
      }
  
      return FcTrue;
  
  bail2:
      FcValueDestroy (value);
  bail1:
      free (new);
  bail0:
      return FcFalse;
  }
  
  FcBool
  FcPatternObjectAdd (FcPattern *p, FcObject object, FcValue value, FcBool append)
  {
      return FcPatternObjectAddWithBinding (p, object,
  					  value, FcValueBindingStrong, append);
  }
  
  FcBool
  FcPatternAdd (FcPattern *p, const char *object, FcValue value, FcBool append)
  {
      return FcPatternObjectAddWithBinding (p, FcObjectFromName (object),
  					  value, FcValueBindingStrong, append);
  }
  
  FcBool
  FcPatternAddWeak  (FcPattern *p, const char *object, FcValue value, FcBool append)
  {
      return FcPatternObjectAddWithBinding (p, FcObjectFromName (object),
  					  value, FcValueBindingWeak, append);
  }
  
  FcBool
  FcPatternObjectDel (FcPattern *p, FcObject object)
  {
      FcPatternElt   *e;
  
      e = FcPatternObjectFindElt (p, object);
      if (!e)
  	return FcFalse;
  
      /* destroy value */
      FcValueListDestroy (e->values);
  
      /* shuffle existing ones down */
      memmove (e, e+1,
  	     (FcPatternElts(p) + p->num - (e + 1)) *
  	     sizeof (FcPatternElt));
      p->num--;
      e = FcPatternElts(p) + p->num;
      e->object = 0;
      e->values = NULL;
      return FcTrue;
  }
  
  FcBool
  FcPatternDel (FcPattern *p, const char *object)
  {
      return FcPatternObjectDel (p, FcObjectFromName (object));
  }
  
  FcBool
  FcPatternRemove (FcPattern *p, const char *object, int id)
  {
      FcPatternElt    *e;
      FcValueListPtr  *prev, l;
  
      e = FcPatternObjectFindElt (p, FcObjectFromName (object));
      if (!e)
  	return FcFalse;
      for (prev = &e->values; (l = *prev); prev = &l->next)
      {
  	if (!id)
  	{
  	    *prev = l->next;
  	    l->next = NULL;
  	    FcValueListDestroy (l);
  	    if (!e->values)
  		FcPatternDel (p, object);
  	    return FcTrue;
  	}
  	id--;
      }
      return FcFalse;
  }
  
  FcBool
  FcPatternObjectAddInteger (FcPattern *p, FcObject object, int i)
  {
      FcValue	v;
  
      v.type = FcTypeInteger;
      v.u.i = i;
      return FcPatternObjectAdd (p, object, v, FcTrue);
  }
  
  FcBool
  FcPatternAddInteger (FcPattern *p, const char *object, int i)
  {
      return FcPatternObjectAddInteger (p, FcObjectFromName (object), i);
  }
  
  FcBool
  FcPatternObjectAddDouble (FcPattern *p, FcObject object, double d)
  {
      FcValue	v;
  
      v.type = FcTypeDouble;
      v.u.d = d;
      return FcPatternObjectAdd (p, object, v, FcTrue);
  }
  
  
  FcBool
  FcPatternAddDouble (FcPattern *p, const char *object, double d)
  {
      return FcPatternObjectAddDouble (p, FcObjectFromName (object), d);
  }
  
  FcBool
  FcPatternObjectAddString (FcPattern *p, FcObject object, const FcChar8 *s)
  {
      FcValue	v;
  
      if (!s)
      {
  	v.type = FcTypeVoid;
  	v.u.s = 0;
  	return FcPatternObjectAdd (p, object, v, FcTrue);
      }
  
      v.type = FcTypeString;
      v.u.s = s;
      return FcPatternObjectAdd (p, object, v, FcTrue);
  }
  
  FcBool
  FcPatternAddString (FcPattern *p, const char *object, const FcChar8 *s)
  {
      return FcPatternObjectAddString (p, FcObjectFromName (object), s);
  }
  
  FcBool
  FcPatternAddMatrix (FcPattern *p, const char *object, const FcMatrix *s)
  {
      FcValue	v;
  
      v.type = FcTypeMatrix;
      v.u.m = s;
      return FcPatternAdd (p, object, v, FcTrue);
  }
  
  
  FcBool
  FcPatternObjectAddBool (FcPattern *p, FcObject object, FcBool b)
  {
      FcValue	v;
  
      v.type = FcTypeBool;
      v.u.b = b;
      return FcPatternObjectAdd (p, object, v, FcTrue);
  }
  
  FcBool
  FcPatternAddBool (FcPattern *p, const char *object, FcBool b)
  {
      return FcPatternObjectAddBool (p, FcObjectFromName (object), b);
  }
  
  FcBool
  FcPatternAddCharSet (FcPattern *p, const char *object, const FcCharSet *c)
  {
      FcValue	v;
  
      v.type = FcTypeCharSet;
      v.u.c = (FcCharSet *)c;
      return FcPatternAdd (p, object, v, FcTrue);
  }
  
  FcBool
  FcPatternAddFTFace (FcPattern *p, const char *object, const FT_Face f)
  {
      FcValue	v;
  
      v.type = FcTypeFTFace;
      v.u.f = (void *) f;
      return FcPatternAdd (p, object, v, FcTrue);
  }
  
  FcBool
  FcPatternAddLangSet (FcPattern *p, const char *object, const FcLangSet *ls)
  {
      FcValue	v;
  
      v.type = FcTypeLangSet;
      v.u.l = (FcLangSet *)ls;
      return FcPatternAdd (p, object, v, FcTrue);
  }
  
  FcResult
  FcPatternObjectGet (const FcPattern *p, FcObject object, int id, FcValue *v)
  {
      FcPatternElt   *e;
      FcValueListPtr l;
  
      if (!p)
  	return FcResultNoMatch;
      e = FcPatternObjectFindElt (p, object);
      if (!e)
  	return FcResultNoMatch;
      for (l = FcPatternEltValues(e); l; l = FcValueListNext(l))
      {
  	if (!id)
  	{
  	    *v = FcValueCanonicalize(&l->value);
  	    return FcResultMatch;
  	}
  	id--;
      }
      return FcResultNoId;
  }
  
  FcResult
  FcPatternGet (const FcPattern *p, const char *object, int id, FcValue *v)
  {
      return FcPatternObjectGet (p, FcObjectFromName (object), id, v);
  }
  
  FcResult
  FcPatternObjectGetInteger (const FcPattern *p, FcObject object, int id, int *i)
  {
      FcValue	v;
      FcResult	r;
  
      r = FcPatternObjectGet (p, object, id, &v);
      if (r != FcResultMatch)
  	return r;
      switch ((int) v.type) {
      case FcTypeDouble:
  	*i = (int) v.u.d;
  	break;
      case FcTypeInteger:
  	*i = v.u.i;
  	break;
      default:
          return FcResultTypeMismatch;
      }
      return FcResultMatch;
  }
  
  FcResult
  FcPatternGetInteger (const FcPattern *p, const char *object, int id, int *i)
  {
      return FcPatternObjectGetInteger (p, FcObjectFromName (object), id, i);
  }
  
  
  FcResult
  FcPatternObjectGetDouble (const FcPattern *p, FcObject object, int id, double *d)
  {
      FcValue	v;
      FcResult	r;
  
      r = FcPatternObjectGet (p, object, id, &v);
      if (r != FcResultMatch)
  	return r;
      switch ((int) v.type) {
      case FcTypeDouble:
  	*d = v.u.d;
  	break;
      case FcTypeInteger:
  	*d = (double) v.u.i;
  	break;
      default:
          return FcResultTypeMismatch;
      }
      return FcResultMatch;
  }
  
  FcResult
  FcPatternGetDouble (const FcPattern *p, const char *object, int id, double *d)
  {
      return FcPatternObjectGetDouble (p, FcObjectFromName (object), id, d);
  }
  
  FcResult
  FcPatternObjectGetString (const FcPattern *p, FcObject object, int id, FcChar8 ** s)
  {
      FcValue	v;
      FcResult	r;
  
      r = FcPatternObjectGet (p, object, id, &v);
      if (r != FcResultMatch)
  	return r;
      if (v.type != FcTypeString)
          return FcResultTypeMismatch;
  
      *s = (FcChar8 *) v.u.s;
      return FcResultMatch;
  }
  
  FcResult
  FcPatternGetString (const FcPattern *p, const char *object, int id, FcChar8 ** s)
  {
      return FcPatternObjectGetString (p, FcObjectFromName (object), id, s);
  }
  
  FcResult
  FcPatternGetMatrix(const FcPattern *p, const char *object, int id, FcMatrix **m)
  {
      FcValue	v;
      FcResult	r;
  
      r = FcPatternGet (p, object, id, &v);
      if (r != FcResultMatch)
  	return r;
      if (v.type != FcTypeMatrix)
          return FcResultTypeMismatch;
      *m = (FcMatrix *)v.u.m;
      return FcResultMatch;
  }
  
  
  FcResult
  FcPatternGetBool(const FcPattern *p, const char *object, int id, FcBool *b)
  {
      FcValue	v;
      FcResult	r;
  
      r = FcPatternGet (p, object, id, &v);
      if (r != FcResultMatch)
  	return r;
      if (v.type != FcTypeBool)
          return FcResultTypeMismatch;
      *b = v.u.b;
      return FcResultMatch;
  }
  
  FcResult
  FcPatternGetCharSet(const FcPattern *p, const char *object, int id, FcCharSet **c)
  {
      FcValue	v;
      FcResult	r;
  
      r = FcPatternGet (p, object, id, &v);
      if (r != FcResultMatch)
  	return r;
      if (v.type != FcTypeCharSet)
          return FcResultTypeMismatch;
      *c = (FcCharSet *)v.u.c;
      return FcResultMatch;
  }
  
  FcResult
  FcPatternGetFTFace(const FcPattern *p, const char *object, int id, FT_Face *f)
  {
      FcValue	v;
      FcResult	r;
  
      r = FcPatternGet (p, object, id, &v);
      if (r != FcResultMatch)
  	return r;
      if (v.type != FcTypeFTFace)
  	return FcResultTypeMismatch;
      *f = (FT_Face) v.u.f;
      return FcResultMatch;
  }
  
  FcResult
  FcPatternGetLangSet(const FcPattern *p, const char *object, int id, FcLangSet **ls)
  {
      FcValue	v;
      FcResult	r;
  
      r = FcPatternGet (p, object, id, &v);
      if (r != FcResultMatch)
  	return r;
      if (v.type != FcTypeLangSet)
          return FcResultTypeMismatch;
      *ls = (FcLangSet *)v.u.l;
      return FcResultMatch;
  }
  
  FcPattern *
  FcPatternDuplicate (const FcPattern *orig)
  {
      FcPattern	    *new;
      FcPatternElt    *e;
      int		    i;
      FcValueListPtr  l;
  
      new = FcPatternCreate ();
      if (!new)
  	goto bail0;
  
      e = FcPatternElts(orig);
  
      for (i = 0; i < orig->num; i++)
      {
  	for (l = FcPatternEltValues(e + i); l; l = FcValueListNext(l))
  	{
  	    if (!FcPatternObjectAddWithBinding (new, e[i].object,
  						FcValueCanonicalize(&l->value),
  						l->binding,
  						FcTrue))
  		goto bail1;
  	
  	}
      }
  
      return new;
  
  bail1:
      FcPatternDestroy (new);
  bail0:
      return 0;
  }
  
  void
  FcPatternReference (FcPattern *p)
  {
      if (!FcRefIsConst (&p->ref))
  	FcRefInc (&p->ref);
      else
  	FcCacheObjectReference (p);
  }
  
  FcPattern *
  FcPatternVaBuild (FcPattern *p, va_list va)
  {
      FcPattern	*ret;
  
      FcPatternVapBuild (ret, p, va);
      return ret;
  }
  
  FcPattern *
  FcPatternBuild (FcPattern *p, ...)
  {
      va_list	va;
  
      va_start (va, p);
      FcPatternVapBuild (p, p, va);
      va_end (va);
      return p;
  }
  
  /*
   * Add all of the elements in 's' to 'p'
   */
  FcBool
  FcPatternAppend (FcPattern *p, FcPattern *s)
  {
      int		    i;
      FcPatternElt    *e;
      FcValueListPtr  v;
  
      for (i = 0; i < s->num; i++)
      {
  	e = FcPatternElts(s)+i;
  	for (v = FcPatternEltValues(e); v; v = FcValueListNext(v))
  	{
  	    if (!FcPatternObjectAddWithBinding (p, e->object,
  						FcValueCanonicalize(&v->value),
  						v->binding, FcTrue))
  		return FcFalse;
  	}
      }
      return FcTrue;
  }
  
  FcPattern *
  FcPatternFilter (FcPattern *p, const FcObjectSet *os)
  {
      int		    i;
      FcPattern	    *ret;
      FcPatternElt    *e;
      FcValueListPtr  v;
  
      if (!os)
  	return FcPatternDuplicate (p);
  
      ret = FcPatternCreate ();
      if (!ret)
  	return NULL;
  
      for (i = 0; i < os->nobject; i++)
      {
  	FcObject object = FcObjectFromName (os->objects[i]);
  	e = FcPatternObjectFindElt (p, object);
  	if (e)
  	{
  	    for (v = FcPatternEltValues(e); v; v = FcValueListNext(v))
  	    {
  		if (!FcPatternObjectAddWithBinding (ret, e->object,
  						    FcValueCanonicalize(&v->value),
  						    v->binding, FcTrue))
  		    goto bail0;
  	    }
  	}
      }
      return ret;
  
  bail0:
      FcPatternDestroy (ret);
      return NULL;
  }
  
  
  FcBool
  FcPatternSerializeAlloc (FcSerialize *serialize, const FcPattern *pat)
  {
      int	i;
      FcPatternElt    *elts = FcPatternElts(pat);
  
      if (!FcSerializeAlloc (serialize, pat, sizeof (FcPattern)))
  	return FcFalse;
      if (!FcSerializeAlloc (serialize, elts, pat->num * sizeof (FcPatternElt)))
  	return FcFalse;
      for (i = 0; i < pat->num; i++)
  	if (!FcValueListSerializeAlloc (serialize, FcPatternEltValues(elts+i)))
  	    return FcFalse;
      return FcTrue;
  }
  
  FcPattern *
  FcPatternSerialize (FcSerialize *serialize, const FcPattern *pat)
  {
      FcPattern	    *pat_serialized;
      FcPatternElt    *elts = FcPatternElts (pat);
      FcPatternElt    *elts_serialized;
      FcValueList	    *values_serialized;
      int		    i;
  
      pat_serialized = FcSerializePtr (serialize, pat);
      if (!pat_serialized)
  	return NULL;
      *pat_serialized = *pat;
      pat_serialized->size = pat->num;
      FcRefSetConst (&pat_serialized->ref);
  
      elts_serialized = FcSerializePtr (serialize, elts);
      if (!elts_serialized)
  	return NULL;
  
      pat_serialized->elts_offset = FcPtrToOffset (pat_serialized,
  						 elts_serialized);
  
      for (i = 0; i < pat->num; i++)
      {
  	values_serialized = FcValueListSerialize (serialize, FcPatternEltValues (elts+i));
  	if (!values_serialized)
  	    return NULL;
  	elts_serialized[i].object = elts[i].object;
  	elts_serialized[i].values = FcPtrToEncodedOffset (&elts_serialized[i],
  							  values_serialized,
  							  FcValueList);
      }
      if (FcDebug() & FC_DBG_CACHEV) {
  	printf ("Raw pattern:\n");
  	FcPatternPrint (pat);
  	printf ("Serialized pattern:\n");
  	FcPatternPrint (pat_serialized);
  	printf ("\n");
      }
      return pat_serialized;
  }
  
  FcBool
  FcValueListSerializeAlloc (FcSerialize *serialize, const FcValueList *vl)
  {
      while (vl)
      {
  	if (!FcSerializeAlloc (serialize, vl, sizeof (FcValueList)))
  	    return FcFalse;
  	switch ((int) vl->value.type) {
  	case FcTypeString:
  	    if (!FcStrSerializeAlloc (serialize, vl->value.u.s))
  		return FcFalse;
  	    break;
  	case FcTypeCharSet:
  	    if (!FcCharSetSerializeAlloc (serialize, vl->value.u.c))
  		return FcFalse;
  	    break;
  	case FcTypeLangSet:
  	    if (!FcLangSetSerializeAlloc (serialize, vl->value.u.l))
  		return FcFalse;
  	    break;
  	default:
  	    break;
  	}
  	vl = vl->next;
      }
      return FcTrue;
  }
  
  FcValueList *
  FcValueListSerialize (FcSerialize *serialize, const FcValueList *vl)
  {
      FcValueList	*vl_serialized;
      FcChar8	*s_serialized;
      FcCharSet	*c_serialized;
      FcLangSet	*l_serialized;
      FcValueList	*head_serialized = NULL;
      FcValueList	*prev_serialized = NULL;
  
      while (vl)
      {
  	vl_serialized = FcSerializePtr (serialize, vl);
  	if (!vl_serialized)
  	    return NULL;
  
  	if (prev_serialized)
  	    prev_serialized->next = FcPtrToEncodedOffset (prev_serialized,
  							  vl_serialized,
  							  FcValueList);
  	else
  	    head_serialized = vl_serialized;
  	
  	vl_serialized->next = NULL;
  	vl_serialized->value.type = vl->value.type;
  	switch ((int) vl->value.type) {
  	case FcTypeInteger:
  	    vl_serialized->value.u.i = vl->value.u.i;
  	    break;
  	case FcTypeDouble:
  	    vl_serialized->value.u.d = vl->value.u.d;
  	    break;
  	case FcTypeString:
  	    s_serialized = FcStrSerialize (serialize, vl->value.u.s);
  	    if (!s_serialized)
  		return NULL;
  	    vl_serialized->value.u.s = FcPtrToEncodedOffset (&vl_serialized->value,
  							     s_serialized,
  							     FcChar8);
  	    break;
  	case FcTypeBool:
  	    vl_serialized->value.u.b = vl->value.u.b;
  	    break;
  	case FcTypeMatrix:
  	    /* can't happen */
  	    break;
  	case FcTypeCharSet:
  	    c_serialized = FcCharSetSerialize (serialize, vl->value.u.c);
  	    if (!c_serialized)
  		return NULL;
  	    vl_serialized->value.u.c = FcPtrToEncodedOffset (&vl_serialized->value,
  							     c_serialized,
  							     FcCharSet);
  	    break;
  	case FcTypeFTFace:
  	    /* can't happen */
  	    break;
  	case FcTypeLangSet:
  	    l_serialized = FcLangSetSerialize (serialize, vl->value.u.l);
  	    if (!l_serialized)
  		return NULL;
  	    vl_serialized->value.u.l = FcPtrToEncodedOffset (&vl_serialized->value,
  							     l_serialized,
  							     FcLangSet);
  	    break;
  	default:
  	    break;
  	}
  	prev_serialized = vl_serialized;
  	vl = vl->next;
      }
      return head_serialized;
  }
  #define __fcpat__
  #include "fcaliastail.h"
  #include "fcftaliastail.h"
  #undef __fcpat__
  