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