Add padding to make valgrind and glibc not hate each other when calling
[platform/upstream/fontconfig.git] / src / fcname.c
1 /*
2  * $RCSId: xc/lib/fontconfig/src/fcname.c,v 1.15 2002/09/26 00:17:28 keithp Exp $
3  *
4  * Copyright © 2000 Keith Packard
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include "fcint.h"
30
31 /* Please do not revoke any of these bindings. */
32 static const FcObjectType _FcBaseObjectTypes[] = {
33     { "__DUMMY__",      FcTypeVoid, },
34     { FC_FAMILY,        FcTypeString, },
35     { FC_FAMILYLANG,    FcTypeString, },
36     { FC_STYLE,         FcTypeString, },
37     { FC_STYLELANG,     FcTypeString, },
38     { FC_FULLNAME,      FcTypeString, },
39     { FC_FULLNAMELANG,  FcTypeString, },
40     { FC_SLANT,         FcTypeInteger, },
41     { FC_WEIGHT,        FcTypeInteger, },
42     { FC_WIDTH,         FcTypeInteger, },
43     { FC_SIZE,          FcTypeDouble, },
44     { FC_ASPECT,        FcTypeDouble, },
45     { FC_PIXEL_SIZE,    FcTypeDouble, },
46     { FC_SPACING,       FcTypeInteger, },
47     { FC_FOUNDRY,       FcTypeString, },
48 /*    { FC_CORE,                FcTypeBool, }, */
49     { FC_ANTIALIAS,     FcTypeBool, },
50     { FC_HINT_STYLE,    FcTypeInteger, },
51     { FC_HINTING,       FcTypeBool, },
52     { FC_VERTICAL_LAYOUT,   FcTypeBool, },
53     { FC_AUTOHINT,      FcTypeBool, },
54     { FC_GLOBAL_ADVANCE,    FcTypeBool, },
55 /*    { FC_XLFD,                FcTypeString, }, */
56     { FC_FILE,          FcTypeString, },
57     { FC_INDEX,         FcTypeInteger, },
58     { FC_RASTERIZER,    FcTypeString, },
59     { FC_OUTLINE,       FcTypeBool, },
60     { FC_SCALABLE,      FcTypeBool, },
61     { FC_DPI,           FcTypeDouble },
62     { FC_RGBA,          FcTypeInteger, },
63     { FC_SCALE,         FcTypeDouble, },
64     { FC_MINSPACE,      FcTypeBool, },
65 /*    { FC_RENDER,      FcTypeBool, },*/
66     { FC_CHAR_WIDTH,    FcTypeInteger },
67     { FC_CHAR_HEIGHT,   FcTypeInteger },
68     { FC_MATRIX,        FcTypeMatrix },
69     { FC_CHARSET,       FcTypeCharSet },
70     { FC_LANG,          FcTypeLangSet },
71     { FC_FONTVERSION,   FcTypeInteger },
72     { FC_CAPABILITY,    FcTypeString },
73     { FC_FONTFORMAT,    FcTypeString },
74     { FC_EMBOLDEN,      FcTypeBool },
75 };
76
77 #define NUM_OBJECT_TYPES    (sizeof _FcBaseObjectTypes / sizeof _FcBaseObjectTypes[0])
78
79 static FcObjectType * _FcUserObjectNames = 0;
80 static int user_obj_alloc = 0;
81 static int next_basic_offset = NUM_OBJECT_TYPES;
82
83 typedef struct _FcObjectTypeList    FcObjectTypeList;
84
85 struct _FcObjectTypeList {
86     const FcObjectTypeList  *next;
87     const FcObjectType      *types;
88     int                     ntypes;
89     int                     basic_offset;
90 };
91
92 static const FcObjectTypeList _FcBaseObjectTypesList = {
93     0,
94     _FcBaseObjectTypes,
95     NUM_OBJECT_TYPES
96 };
97
98 static const FcObjectTypeList   *_FcObjectTypes = &_FcBaseObjectTypesList;
99
100 FcBool
101 FcNameRegisterObjectTypes (const FcObjectType *types, int ntypes)
102 {
103     FcObjectTypeList    *l;
104
105     l = (FcObjectTypeList *) malloc (sizeof (FcObjectTypeList));
106     if (!l)
107         return FcFalse;
108     FcMemAlloc (FC_MEM_OBJECTTYPE, sizeof (FcObjectTypeList));
109     l->types = types;
110     l->ntypes = ntypes;
111     l->next = _FcObjectTypes;
112     l->basic_offset = next_basic_offset;
113     next_basic_offset += ntypes;
114     _FcObjectTypes = l;
115     return FcTrue;
116 }
117
118 static FcBool
119 FcNameUnregisterObjectTypesFree (const FcObjectType *types, int ntypes, 
120                                  FcBool do_free)
121 {
122     const FcObjectTypeList      *l, **prev;
123
124     for (prev = &_FcObjectTypes; 
125          (l = *prev); 
126          prev = (const FcObjectTypeList **) &(l->next))
127     {
128         if (l->types == types && l->ntypes == ntypes)
129         {
130             *prev = l->next;
131             if (do_free) {
132                 FcMemFree (FC_MEM_OBJECTTYPE, sizeof (FcObjectTypeList));
133                 free ((void *) l);
134             }
135             return FcTrue;
136         }
137     }
138     return FcFalse;
139 }
140
141 FcBool
142 FcNameUnregisterObjectTypes (const FcObjectType *types, int ntypes)
143 {
144     return FcNameUnregisterObjectTypesFree (types, ntypes, FcTrue);
145 }
146
147 const FcObjectType *
148 FcNameGetObjectType (const char *object)
149 {
150     int                     i;
151     const FcObjectTypeList  *l;
152     const FcObjectType      *t;
153     
154     for (l = _FcObjectTypes; l; l = l->next)
155     {
156         for (i = 0; i < l->ntypes; i++)
157         {
158             t = &l->types[i];
159             if (!strcmp (object, t->object))
160                 return t;
161         }
162     }
163     return 0;
164 }
165
166 #define OBJECT_HASH_SIZE    31
167 struct objectBucket {
168     struct objectBucket *next;
169     FcChar32            hash;
170     int                 id;
171 };
172 static struct objectBucket *FcObjectBuckets[OBJECT_HASH_SIZE];
173
174 /* Design constraint: biggest_known_ntypes must never change
175  * after any call to FcNameRegisterObjectTypes. */
176 static const FcObjectType *biggest_known_types = _FcBaseObjectTypes; 
177 static FcBool allocated_biggest_known_types;
178 static int biggest_known_ntypes = NUM_OBJECT_TYPES;
179 static int biggest_known_count = 0;
180 static char * biggest_ptr;
181
182
183 static FcObjectPtr
184 FcObjectToPtrLookup (const char * object)
185 {
186     FcObjectPtr             i = 0, n;
187     const FcObjectTypeList  *l;
188     FcObjectType            *t = _FcUserObjectNames;
189
190     for (l = _FcObjectTypes; l; l = l->next)
191     {
192         for (i = 0; i < l->ntypes; i++)
193         {
194             t = (FcObjectType *)&l->types[i];
195             if (!strcmp (object, t->object))
196             {
197                 if (l == (FcObjectTypeList*)_FcUserObjectNames)
198                     return -i;
199                 else
200                     return l->basic_offset + i;
201             }
202         }
203     }
204
205     /* We didn't match.  Look for the correct FcObjectTypeList
206      * to replace it in-place. */
207     for (l = _FcObjectTypes; l; l = l->next)
208     {
209         if (l->types == _FcUserObjectNames)
210             break;
211     }
212
213     if (!_FcUserObjectNames || 
214         (l && l->types == _FcUserObjectNames && user_obj_alloc < l->ntypes))
215     {
216         int nt = user_obj_alloc + 4;
217         FcObjectType * t = realloc (_FcUserObjectNames, 
218                                     nt * sizeof (FcObjectType));
219         if (!t)
220             return 0;
221         _FcUserObjectNames = t;
222         user_obj_alloc = nt;
223     }
224
225     if (l && l->types == _FcUserObjectNames)
226     {
227         n = l->ntypes;
228         FcNameUnregisterObjectTypesFree (l->types, l->ntypes, FcFalse);
229     }
230     else
231         n = 0;
232
233     FcNameRegisterObjectTypes (_FcUserObjectNames, n+1);
234
235     for (l = _FcObjectTypes; l; l = l->next)
236     {
237         if (l->types == _FcUserObjectNames)
238         {
239             t = (FcObjectType *)l->types;
240             break;
241         }
242     }
243
244     if (!t)
245         return 0;
246
247     t[n].object = object;
248     t[n].type = FcTypeVoid;
249
250     return -n;
251 }
252
253 FcObjectPtr
254 FcObjectToPtr (const char * name)
255 {
256     FcChar32            hash = FcStringHash ((const FcChar8 *) name);
257     struct objectBucket **p;
258     struct objectBucket *b;
259     int                 size;
260
261     for (p = &FcObjectBuckets[hash % OBJECT_HASH_SIZE]; (b = *p); p = &(b->next)
262 )
263         if (b->hash == hash && !strcmp (name, (char *) (b + 1)))
264             return b->id;
265     size = sizeof (struct objectBucket) + strlen (name) + 1;
266     /* workaround glibc bug which reads strlen in groups of 4 */
267     b = malloc (size + sizeof (int));
268     FcMemAlloc (FC_MEM_STATICSTR, size + sizeof(int));
269     if (!b)
270         return 0;
271     b->next = 0;
272     b->hash = hash;
273     b->id = FcObjectToPtrLookup (name);
274     strcpy ((char *) (b + 1), name);
275     *p = b;
276     return b->id;
277 }
278
279 void
280 FcObjectStaticNameFini (void)
281 {
282     int i, size;
283     struct objectBucket *b, *next;
284     char *name;
285
286     for (i = 0; i < OBJECT_HASH_SIZE; i++)
287     {
288         for (b = FcObjectBuckets[i]; b; b = next)
289         {
290             next = b->next;
291             name = (char *) (b + 1);
292             size = sizeof (struct objectBucket) + strlen (name) + 1;
293             FcMemFree (FC_MEM_STATICSTR, size);
294             free (b);
295         }
296         FcObjectBuckets[i] = 0;
297     }
298 }
299
300 const char *
301 FcObjectPtrU (FcObjectPtr si)
302 {
303     const FcObjectTypeList  *l;
304     int i, j;
305
306     if (si > 0)
307     {
308         if (si < biggest_known_ntypes)
309             return biggest_known_types[si].object;
310
311         j = 0;
312         for (l = _FcObjectTypes; l; l = l->next)
313             for (i = 0; i < l->ntypes; i++, j++)
314                 if (j == si)
315                     return l->types[i].object;
316     }
317
318     return _FcUserObjectNames[-si].object;
319 }
320
321 int
322 FcObjectNeededBytes ()
323 {
324     int num = 0, i;
325     for (i = 0; i < biggest_known_ntypes; i++)
326     {
327         const char * t = biggest_known_types[i].object;
328         num = num + strlen(t) + 1;
329     }
330     biggest_known_count = num;
331     return num + sizeof(int);
332 }
333
334 void *
335 FcObjectDistributeBytes (FcCache * metadata, void * block_ptr)
336 {
337     *(int *)block_ptr = biggest_known_ntypes;
338     block_ptr = (int *) block_ptr + 1;
339     biggest_ptr = block_ptr;
340     block_ptr = (char *) block_ptr + biggest_known_count;
341     return block_ptr;
342 }
343
344 void
345 FcObjectSerialize ()
346 {
347     int i;
348     for (i = 0; i < biggest_known_ntypes; i++)
349     {
350         const char * t = biggest_known_types[i].object;
351         strcpy (biggest_ptr, t);
352         biggest_ptr = biggest_ptr + strlen(t) + 1;
353     }
354 }
355
356 void *
357 FcObjectUnserialize (FcCache metadata, void *block_ptr)
358 {
359     int new_biggest;
360     new_biggest = *(int *)block_ptr;
361     block_ptr = (int *) block_ptr + 1;
362     if (biggest_known_ntypes < new_biggest)
363     {
364         int i;
365         char * bp = (char *)block_ptr;
366         FcObjectType * bn;
367         FcObjectTypeList * bnl;
368
369         bn = malloc (sizeof (const FcObjectType) * (new_biggest + 1));
370         if (!bn)
371             return 0;
372
373         bnl = malloc (sizeof (FcObjectTypeList));
374         if (!bnl)
375         {
376             free (bn);
377             return 0;
378         }
379
380         for (i = 0; i < new_biggest; i++)
381         {
382             const FcObjectType * t = FcNameGetObjectType(bp);
383             if (t)
384                 bn[i].type = t->type;
385             else
386                 bn[i].type = FcTypeVoid;
387             bn[i].object = bp;
388             bp = bp + strlen(bp) + 1;
389         }
390
391         FcNameUnregisterObjectTypesFree (biggest_known_types, biggest_known_ntypes, FcFalse);
392         if (allocated_biggest_known_types)
393         {
394             free ((FcObjectTypeList *)biggest_known_types);
395         }
396         else
397             allocated_biggest_known_types = FcTrue;
398
399         FcNameRegisterObjectTypes (bn, new_biggest);
400         biggest_known_ntypes = new_biggest;
401         biggest_known_types = (const FcObjectType *)bn;
402     }
403     block_ptr = (char *) block_ptr + biggest_known_count;
404     return block_ptr;
405 }
406
407 int
408 FcObjectPtrCompare (const FcObjectPtr a, const FcObjectPtr b)
409 {
410     return a - b;
411 }
412
413 static const FcConstant _FcBaseConstants[] = {
414     { (FcChar8 *) "thin",           "weight",   FC_WEIGHT_THIN, },
415     { (FcChar8 *) "extralight",     "weight",   FC_WEIGHT_EXTRALIGHT, },
416     { (FcChar8 *) "ultralight",     "weight",   FC_WEIGHT_EXTRALIGHT, },
417     { (FcChar8 *) "light",          "weight",   FC_WEIGHT_LIGHT, },
418     { (FcChar8 *) "book",           "weight",   FC_WEIGHT_BOOK, },
419     { (FcChar8 *) "regular",        "weight",   FC_WEIGHT_REGULAR, },
420     { (FcChar8 *) "medium",         "weight",   FC_WEIGHT_MEDIUM, },
421     { (FcChar8 *) "demibold",       "weight",   FC_WEIGHT_DEMIBOLD, },
422     { (FcChar8 *) "semibold",       "weight",   FC_WEIGHT_DEMIBOLD, },
423     { (FcChar8 *) "bold",           "weight",   FC_WEIGHT_BOLD, },
424     { (FcChar8 *) "extrabold",      "weight",   FC_WEIGHT_EXTRABOLD, },
425     { (FcChar8 *) "ultrabold",      "weight",   FC_WEIGHT_EXTRABOLD, },
426     { (FcChar8 *) "black",          "weight",   FC_WEIGHT_BLACK, },
427
428     { (FcChar8 *) "roman",          "slant",    FC_SLANT_ROMAN, },
429     { (FcChar8 *) "italic",         "slant",    FC_SLANT_ITALIC, },
430     { (FcChar8 *) "oblique",        "slant",    FC_SLANT_OBLIQUE, },
431
432     { (FcChar8 *) "ultracondensed", "width",    FC_WIDTH_ULTRACONDENSED },
433     { (FcChar8 *) "extracondensed", "width",    FC_WIDTH_EXTRACONDENSED },
434     { (FcChar8 *) "condensed",      "width",    FC_WIDTH_CONDENSED },
435     { (FcChar8 *) "semicondensed", "width",     FC_WIDTH_SEMICONDENSED },
436     { (FcChar8 *) "normal",         "width",    FC_WIDTH_NORMAL },
437     { (FcChar8 *) "semiexpanded",   "width",    FC_WIDTH_SEMIEXPANDED },
438     { (FcChar8 *) "expanded",       "width",    FC_WIDTH_EXPANDED },
439     { (FcChar8 *) "extraexpanded",  "width",    FC_WIDTH_EXTRAEXPANDED },
440     { (FcChar8 *) "ultraexpanded",  "width",    FC_WIDTH_ULTRAEXPANDED },
441     
442     { (FcChar8 *) "proportional",   "spacing",  FC_PROPORTIONAL, },
443     { (FcChar8 *) "dual",           "spacing",  FC_DUAL, },
444     { (FcChar8 *) "mono",           "spacing",  FC_MONO, },
445     { (FcChar8 *) "charcell",       "spacing",  FC_CHARCELL, },
446
447     { (FcChar8 *) "unknown",        "rgba",         FC_RGBA_UNKNOWN },
448     { (FcChar8 *) "rgb",            "rgba",         FC_RGBA_RGB, },
449     { (FcChar8 *) "bgr",            "rgba",         FC_RGBA_BGR, },
450     { (FcChar8 *) "vrgb",           "rgba",         FC_RGBA_VRGB },
451     { (FcChar8 *) "vbgr",           "rgba",         FC_RGBA_VBGR },
452     { (FcChar8 *) "none",           "rgba",         FC_RGBA_NONE },
453
454     { (FcChar8 *) "hintnone",       "hintstyle",   FC_HINT_NONE },
455     { (FcChar8 *) "hintslight",     "hintstyle",   FC_HINT_SLIGHT },
456     { (FcChar8 *) "hintmedium",     "hintstyle",   FC_HINT_MEDIUM },
457     { (FcChar8 *) "hintfull",       "hintstyle",   FC_HINT_FULL },
458 };
459
460 #define NUM_FC_CONSTANTS   (sizeof _FcBaseConstants/sizeof _FcBaseConstants[0])
461
462 typedef struct _FcConstantList FcConstantList;
463
464 struct _FcConstantList {
465     const FcConstantList    *next;
466     const FcConstant        *consts;
467     int                     nconsts;
468 };
469
470 static const FcConstantList _FcBaseConstantList = {
471     0,
472     _FcBaseConstants,
473     NUM_FC_CONSTANTS
474 };
475
476 static const FcConstantList     *_FcConstants = &_FcBaseConstantList;
477
478 FcBool
479 FcNameRegisterConstants (const FcConstant *consts, int nconsts)
480 {
481     FcConstantList      *l;
482
483     l = (FcConstantList *) malloc (sizeof (FcConstantList));
484     if (!l)
485         return FcFalse;
486     FcMemAlloc (FC_MEM_CONSTANT, sizeof (FcConstantList));
487     l->consts = consts;
488     l->nconsts = nconsts;
489     l->next = _FcConstants;
490     _FcConstants = l;
491     return FcTrue;
492 }
493
494 FcBool
495 FcNameUnregisterConstants (const FcConstant *consts, int nconsts)
496 {
497     const FcConstantList        *l, **prev;
498
499     for (prev = &_FcConstants; 
500          (l = *prev); 
501          prev = (const FcConstantList **) &(l->next))
502     {
503         if (l->consts == consts && l->nconsts == nconsts)
504         {
505             *prev = l->next;
506             FcMemFree (FC_MEM_CONSTANT, sizeof (FcConstantList));
507             free ((void *) l);
508             return FcTrue;
509         }
510     }
511     return FcFalse;
512 }
513
514 const FcConstant *
515 FcNameGetConstant (FcChar8 *string)
516 {
517     const FcConstantList    *l;
518     int                     i;
519
520     for (l = _FcConstants; l; l = l->next)
521     {
522         for (i = 0; i < l->nconsts; i++)
523             if (!FcStrCmpIgnoreCase (string, l->consts[i].name))
524                 return &l->consts[i];
525     }
526     return 0;
527 }
528
529 FcBool
530 FcNameConstant (FcChar8 *string, int *result)
531 {
532     const FcConstant    *c;
533
534     if ((c = FcNameGetConstant(string)))
535     {
536         *result = c->value;
537         return FcTrue;
538     }
539     return FcFalse;
540 }
541
542 FcBool
543 FcNameBool (const FcChar8 *v, FcBool *result)
544 {
545     char    c0, c1;
546
547     c0 = *v;
548     c0 = FcToLower (c0);
549     if (c0 == 't' || c0 == 'y' || c0 == '1')
550     {
551         *result = FcTrue;
552         return FcTrue;
553     }
554     if (c0 == 'f' || c0 == 'n' || c0 == '0')
555     {
556         *result = FcFalse;
557         return FcTrue;
558     }
559     if (c0 == 'o')
560     {
561         c1 = v[1];
562         c1 = FcToLower (c1);
563         if (c1 == 'n')
564         {
565             *result = FcTrue;
566             return FcTrue;
567         }
568         if (c1 == 'f')
569         {
570             *result = FcFalse;
571             return FcTrue;
572         }
573     }
574     return FcFalse;
575 }
576
577 static FcValue
578 FcNameConvert (FcType type, FcChar8 *string, FcMatrix *m)
579 {
580     FcValue     v;
581
582     v.type = type;
583     switch (v.type) {
584     case FcTypeInteger:
585         if (!FcNameConstant (string, &v.u.i))
586             v.u.i = atoi ((char *) string);
587         break;
588     case FcTypeString:
589         v.u.s = FcStrStaticName(string);
590         break;
591     case FcTypeBool:
592         if (!FcNameBool (string, &v.u.b))
593             v.u.b = FcFalse;
594         break;
595     case FcTypeDouble:
596         v.u.d = strtod ((char *) string, 0);
597         break;
598     case FcTypeMatrix:
599         v.u.m = m;
600         sscanf ((char *) string, "%lg %lg %lg %lg", &m->xx, &m->xy, &m->yx, &m->yy);
601         break;
602     case FcTypeCharSet:
603         v.u.c = FcNameParseCharSet (string);
604         break;
605     case FcTypeLangSet:
606         v.u.l = FcNameParseLangSet (string);
607         break;
608     default:
609         break;
610     }
611     return v;
612 }
613
614 static const FcChar8 *
615 FcNameFindNext (const FcChar8 *cur, const char *delim, FcChar8 *save, FcChar8 *last)
616 {
617     FcChar8    c;
618     
619     while ((c = *cur))
620     {
621         if (c == '\\')
622         {
623             ++cur;
624             if (!(c = *cur))
625                 break;
626         }
627         else if (strchr (delim, c))
628             break;
629         ++cur;
630         *save++ = c;
631     }
632     *save = 0;
633     *last = *cur;
634     if (*cur)
635         cur++;
636     return cur;
637 }
638
639 FcPattern *
640 FcNameParse (const FcChar8 *name)
641 {
642     FcChar8             *save;
643     FcPattern           *pat;
644     double              d;
645     FcChar8             *e;
646     FcChar8             delim;
647     FcValue             v;
648     FcMatrix            m;
649     const FcObjectType  *t;
650     const FcConstant    *c;
651
652     /* freed below */
653     save = malloc (strlen ((char *) name) + 1);
654     if (!save)
655         goto bail0;
656     pat = FcPatternCreate ();
657     if (!pat)
658         goto bail1;
659
660     for (;;)
661     {
662         name = FcNameFindNext (name, "-,:", save, &delim);
663         if (save[0])
664         {
665             if (!FcPatternAddString (pat, FC_FAMILY, save))
666                 goto bail2;
667         }
668         if (delim != ',')
669             break;
670     }
671     if (delim == '-')
672     {
673         for (;;)
674         {
675             name = FcNameFindNext (name, "-,:", save, &delim);
676             d = strtod ((char *) save, (char **) &e);
677             if (e != save)
678             {
679                 if (!FcPatternAddDouble (pat, FC_SIZE, d))
680                     goto bail2;
681             }
682             if (delim != ',')
683                 break;
684         }
685     }
686     while (delim == ':')
687     {
688         name = FcNameFindNext (name, "=_:", save, &delim);
689         if (save[0])
690         {
691             if (delim == '=' || delim == '_')
692             {
693                 t = FcNameGetObjectType ((char *) save);
694                 for (;;)
695                 {
696                     name = FcNameFindNext (name, ":,", save, &delim);
697                     if (t)
698                     {
699                         v = FcNameConvert (t->type, save, &m);
700                         if (!FcPatternAdd (pat, t->object, v, FcTrue))
701                         {
702                             switch (v.type) {
703                             case FcTypeCharSet:
704                                 FcCharSetDestroy ((FcCharSet *) v.u.c);
705                                 break;
706                             case FcTypeLangSet:
707                                 FcLangSetDestroy ((FcLangSet *) v.u.l);
708                                 break;
709                             default:
710                                 break;
711                             }
712                             goto bail2;
713                         }
714                         switch (v.type) {
715                         case FcTypeCharSet:
716                             FcCharSetDestroy ((FcCharSet *) v.u.c);
717                             break;
718                         case FcTypeLangSet:
719                             FcLangSetDestroy ((FcLangSet *) v.u.l);
720                             break;
721                         default:
722                             break;
723                         }
724                     }
725                     if (delim != ',')
726                         break;
727                 }
728             }
729             else
730             {
731                 if ((c = FcNameGetConstant (save)))
732                 {
733                     if (!FcPatternAddInteger (pat, c->object, c->value))
734                         goto bail2;
735                 }
736             }
737         }
738     }
739
740     free (save);
741     return pat;
742
743 bail2:
744     FcPatternDestroy (pat);
745 bail1:
746     free (save);
747 bail0:
748     return 0;
749 }
750 static FcBool
751 FcNameUnparseString (FcStrBuf       *buf, 
752                      const FcChar8  *string,
753                      const FcChar8  *escape)
754 {
755     FcChar8 c;
756     while ((c = *string++))
757     {
758         if (escape && strchr ((char *) escape, (char) c))
759         {
760             if (!FcStrBufChar (buf, escape[0]))
761                 return FcFalse;
762         }
763         if (!FcStrBufChar (buf, c))
764             return FcFalse;
765     }
766     return FcTrue;
767 }
768
769 static FcBool
770 FcNameUnparseValue (FcStrBuf    *buf,
771                     int         bank,
772                     FcValue     *v0,
773                     FcChar8     *escape)
774 {
775     FcChar8     temp[1024];
776     FcValue v = FcValueCanonicalize(v0);
777     
778     switch (v.type) {
779     case FcTypeVoid:
780         return FcTrue;
781     case FcTypeInteger:
782         sprintf ((char *) temp, "%d", v.u.i);
783         return FcNameUnparseString (buf, temp, 0);
784     case FcTypeDouble:
785         sprintf ((char *) temp, "%g", v.u.d);
786         return FcNameUnparseString (buf, temp, 0);
787     case FcTypeString:
788         return FcNameUnparseString (buf, v.u.s, escape);
789     case FcTypeBool:
790         return FcNameUnparseString (buf, v.u.b ? (FcChar8 *) "True" : (FcChar8 *) "False", 0);
791     case FcTypeMatrix:
792         sprintf ((char *) temp, "%g %g %g %g", 
793                  v.u.m->xx, v.u.m->xy, v.u.m->yx, v.u.m->yy);
794         return FcNameUnparseString (buf, temp, 0);
795     case FcTypeCharSet:
796         return FcNameUnparseCharSet (buf, v.u.c);
797     case FcTypeLangSet:
798         return FcNameUnparseLangSet (buf, v.u.l);
799     case FcTypeFTFace:
800         return FcTrue;
801     }
802     return FcFalse;
803 }
804
805 static FcBool
806 FcNameUnparseValueList (FcStrBuf        *buf,
807                         FcValueListPtr  v,
808                         FcChar8         *escape)
809 {
810     while (FcValueListPtrU(v))
811     {
812         if (!FcNameUnparseValue (buf, v.bank, &FcValueListPtrU(v)->value, escape))
813             return FcFalse;
814         if (FcValueListPtrU(v = FcValueListPtrU(v)->next))
815             if (!FcNameUnparseString (buf, (FcChar8 *) ",", 0))
816                 return FcFalse;
817     }
818     return FcTrue;
819 }
820
821 #define FC_ESCAPE_FIXED    "\\-:,"
822 #define FC_ESCAPE_VARIABLE "\\=_:,"
823
824 FcChar8 *
825 FcNameUnparse (FcPattern *pat)
826 {
827     FcStrBuf                buf;
828     FcChar8                 buf_static[8192];
829     int                     i;
830     FcPatternElt            *e;
831     const FcObjectTypeList  *l;
832     const FcObjectType      *o;
833
834     FcStrBufInit (&buf, buf_static, sizeof (buf_static));
835     e = FcPatternFindElt (pat, FC_FAMILY);
836     if (e)
837     {
838         if (!FcNameUnparseValueList (&buf, e->values, (FcChar8 *) FC_ESCAPE_FIXED))
839             goto bail0;
840     }
841     e = FcPatternFindElt (pat, FC_SIZE);
842     if (e)
843     {
844         if (!FcNameUnparseString (&buf, (FcChar8 *) "-", 0))
845             goto bail0;
846         if (!FcNameUnparseValueList (&buf, e->values, (FcChar8 *) FC_ESCAPE_FIXED))
847             goto bail0;
848     }
849     for (l = _FcObjectTypes; l; l = l->next)
850     {
851         for (i = 0; i < l->ntypes; i++)
852         {
853             o = &l->types[i];
854             if (!strcmp (o->object, FC_FAMILY) || 
855                 !strcmp (o->object, FC_SIZE) ||
856                 !strcmp (o->object, FC_FILE))
857                 continue;
858             
859             e = FcPatternFindElt (pat, o->object);
860             if (e)
861             {
862                 if (!FcNameUnparseString (&buf, (FcChar8 *) ":", 0))
863                     goto bail0;
864                 if (!FcNameUnparseString (&buf, (FcChar8 *) o->object, (FcChar8 *) FC_ESCAPE_VARIABLE))
865                     goto bail0;
866                 if (!FcNameUnparseString (&buf, (FcChar8 *) "=", 0))
867                     goto bail0;
868                 if (!FcNameUnparseValueList (&buf, e->values, 
869                                              (FcChar8 *) FC_ESCAPE_VARIABLE))
870                     goto bail0;
871             }
872         }
873     }
874     return FcStrBufDone (&buf);
875 bail0:
876     FcStrBufDestroy (&buf);
877     return 0;
878 }