Portability fixes for HP-UX (reported by Christoph Bauer). Replace
[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 /* The __DUMMY__ object enables callers to distinguish the error return
33  * of FcObjectToPtrLookup from FC_FAMILY's FcObjectPtr, which would
34  * otherwise be 0. */
35 static const FcObjectType _FcBaseObjectTypes[] = {
36     { "__DUMMY__",      FcTypeVoid, }, 
37     { FC_FAMILY,        FcTypeString, },
38     { FC_FAMILYLANG,    FcTypeString, },
39     { FC_STYLE,         FcTypeString, },
40     { FC_STYLELANG,     FcTypeString, },
41     { FC_FULLNAME,      FcTypeString, },
42     { FC_FULLNAMELANG,  FcTypeString, },
43     { FC_SLANT,         FcTypeInteger, },
44     { FC_WEIGHT,        FcTypeInteger, },
45     { FC_WIDTH,         FcTypeInteger, },
46     { FC_SIZE,          FcTypeDouble, },
47     { FC_ASPECT,        FcTypeDouble, },
48     { FC_PIXEL_SIZE,    FcTypeDouble, },
49     { FC_SPACING,       FcTypeInteger, },
50     { FC_FOUNDRY,       FcTypeString, },
51 /*    { FC_CORE,                FcTypeBool, }, */
52     { FC_ANTIALIAS,     FcTypeBool, },
53     { FC_HINT_STYLE,    FcTypeInteger, },
54     { FC_HINTING,       FcTypeBool, },
55     { FC_VERTICAL_LAYOUT,   FcTypeBool, },
56     { FC_AUTOHINT,      FcTypeBool, },
57     { FC_GLOBAL_ADVANCE,    FcTypeBool, },
58 /*    { FC_XLFD,                FcTypeString, }, */
59     { FC_FILE,          FcTypeString, },
60     { FC_INDEX,         FcTypeInteger, },
61     { FC_RASTERIZER,    FcTypeString, },
62     { FC_OUTLINE,       FcTypeBool, },
63     { FC_SCALABLE,      FcTypeBool, },
64     { FC_DPI,           FcTypeDouble },
65     { FC_RGBA,          FcTypeInteger, },
66     { FC_SCALE,         FcTypeDouble, },
67     { FC_MINSPACE,      FcTypeBool, },
68 /*    { FC_RENDER,      FcTypeBool, },*/
69     { FC_CHAR_WIDTH,    FcTypeInteger },
70     { FC_CHAR_HEIGHT,   FcTypeInteger },
71     { FC_MATRIX,        FcTypeMatrix },
72     { FC_CHARSET,       FcTypeCharSet },
73     { FC_LANG,          FcTypeLangSet },
74     { FC_FONTVERSION,   FcTypeInteger },
75     { FC_CAPABILITY,    FcTypeString },
76     { FC_FONTFORMAT,    FcTypeString },
77     { FC_EMBOLDEN,      FcTypeBool },
78     { FC_EMBEDDED_BITMAP,   FcTypeBool },
79 };
80
81 #define NUM_OBJECT_TYPES    (sizeof _FcBaseObjectTypes / sizeof _FcBaseObjectTypes[0])
82
83 static FcObjectType * _FcUserObjectNames = 0;
84 static int user_obj_alloc = 0;
85 static int next_basic_offset = NUM_OBJECT_TYPES;
86
87 typedef struct _FcObjectTypeList    FcObjectTypeList;
88
89 struct _FcObjectTypeList {
90     const FcObjectTypeList  *next;
91     const FcObjectType      *types;
92     int                     ntypes;
93     int                     basic_offset;
94 };
95
96 static const FcObjectTypeList _FcBaseObjectTypesList = {
97     0,
98     _FcBaseObjectTypes,
99     NUM_OBJECT_TYPES,
100     0
101 };
102
103 static const FcObjectTypeList   *_FcObjectTypes = &_FcBaseObjectTypesList;
104
105 FcBool
106 FcNameRegisterObjectTypes (const FcObjectType *types, int ntypes)
107 {
108     FcObjectTypeList    *l;
109
110     l = (FcObjectTypeList *) malloc (sizeof (FcObjectTypeList));
111     if (!l)
112         return FcFalse;
113     FcMemAlloc (FC_MEM_OBJECTTYPE, sizeof (FcObjectTypeList));
114     l->types = types;
115     l->ntypes = ntypes;
116     l->next = _FcObjectTypes;
117     l->basic_offset = next_basic_offset;
118     next_basic_offset += ntypes;
119     _FcObjectTypes = l;
120     return FcTrue;
121 }
122
123 static FcBool
124 FcNameUnregisterObjectTypesFree (const FcObjectType *types, int ntypes, 
125                                  FcBool do_free)
126 {
127     const FcObjectTypeList      *l, **prev;
128
129     for (prev = &_FcObjectTypes; 
130          (l = *prev); 
131          prev = (const FcObjectTypeList **) &(l->next))
132     {
133         if (l->types == types && l->ntypes == ntypes)
134         {
135             *prev = l->next;
136             if (do_free) {
137                 FcMemFree (FC_MEM_OBJECTTYPE, sizeof (FcObjectTypeList));
138                 free ((void *) l);
139             }
140             return FcTrue;
141         }
142     }
143     return FcFalse;
144 }
145
146 FcBool
147 FcNameUnregisterObjectTypes (const FcObjectType *types, int ntypes)
148 {
149     return FcNameUnregisterObjectTypesFree (types, ntypes, FcTrue);
150 }
151
152 const FcObjectType *
153 FcNameGetObjectType (const char *object)
154 {
155     int                     i;
156     const FcObjectTypeList  *l;
157     const FcObjectType      *t;
158     
159     for (l = _FcObjectTypes; l; l = l->next)
160     {
161         if (l == (FcObjectTypeList*)_FcUserObjectNames)
162             continue;
163
164         for (i = 0; i < l->ntypes; i++)
165         {
166             t = &l->types[i];
167             if (!strcmp (object, t->object))
168                 return t;
169         }
170     }
171     return 0;
172 }
173
174 #define OBJECT_HASH_SIZE    31
175 struct objectBucket {
176     struct objectBucket *next;
177     FcChar32            hash;
178     int                 id;
179 };
180 static struct objectBucket *FcObjectBuckets[OBJECT_HASH_SIZE];
181
182 /* Design constraint: biggest_known_ntypes must never change
183  * after any call to FcNameRegisterObjectTypes. */
184 static const FcObjectType *biggest_known_types = _FcBaseObjectTypes; 
185 static FcBool allocated_biggest_known_types;
186 static int biggest_known_ntypes = NUM_OBJECT_TYPES;
187 static int biggest_known_count = 0;
188 static char * biggest_ptr;
189
190
191 static FcObjectPtr
192 FcObjectToPtrLookup (const char * object)
193 {
194     FcObjectPtr             i = 0, n;
195     const FcObjectTypeList  *l;
196     FcObjectType            *t = _FcUserObjectNames;
197
198     for (l = _FcObjectTypes; l; l = l->next)
199     {
200         for (i = 0; i < l->ntypes; i++)
201         {
202             t = (FcObjectType *)&l->types[i];
203             if (!strcmp (object, t->object))
204             {
205                 if (l == (FcObjectTypeList*)_FcUserObjectNames)
206                     return -i;
207                 else
208                     return l->basic_offset + i;
209             }
210         }
211     }
212
213     /* We didn't match.  Look for the application's FcObjectTypeList
214      * and replace it in-place. */
215     for (l = _FcObjectTypes; l; l = l->next)
216     {
217         if (l->types == _FcUserObjectNames)
218             break;
219     }
220
221     if (!_FcUserObjectNames || 
222         (l && l->types == _FcUserObjectNames && user_obj_alloc < l->ntypes))
223     {
224         int nt = user_obj_alloc + 4;
225         FcObjectType * t = realloc (_FcUserObjectNames, 
226                                     nt * sizeof (FcObjectType));
227         if (!t)
228             return 0;
229         _FcUserObjectNames = t;
230         user_obj_alloc = nt;
231     }
232
233     if (l && l->types == _FcUserObjectNames)
234     {
235         n = l->ntypes;
236         FcNameUnregisterObjectTypesFree (l->types, l->ntypes, FcFalse);
237     }
238     else
239         n = 0;
240
241     FcNameRegisterObjectTypes (_FcUserObjectNames, n+1);
242
243     for (l = _FcObjectTypes; l; l = l->next)
244     {
245         if (l->types == _FcUserObjectNames)
246         {
247             t = (FcObjectType *)l->types;
248             break;
249         }
250     }
251
252     if (!t)
253         return 0;
254
255     t[n].object = object;
256     t[n].type = FcTypeVoid;
257
258     return -n;
259 }
260
261 FcObjectPtr
262 FcObjectToPtr (const char * name)
263 {
264     FcChar32            hash = FcStringHash ((const FcChar8 *) name);
265     struct objectBucket **p;
266     struct objectBucket *b;
267     int                 size;
268
269     for (p = &FcObjectBuckets[hash % OBJECT_HASH_SIZE]; (b = *p); p = &(b->next)
270 )
271         if (b->hash == hash && !strcmp (name, (char *) (b + 1)))
272             return b->id;
273     size = sizeof (struct objectBucket) + strlen (name) + 1;
274     /* workaround glibc bug which reads strlen in groups of 4 */
275     b = malloc (size + sizeof (int));
276     FcMemAlloc (FC_MEM_STATICSTR, size + sizeof(int));
277     if (!b)
278         return 0;
279     b->next = 0;
280     b->hash = hash;
281     b->id = FcObjectToPtrLookup (name);
282     strcpy ((char *) (b + 1), name);
283     *p = b;
284     return b->id;
285 }
286
287 void
288 FcObjectStaticNameFini (void)
289 {
290     int i, size;
291     struct objectBucket *b, *next;
292     char *name;
293
294     for (i = 0; i < OBJECT_HASH_SIZE; i++)
295     {
296         for (b = FcObjectBuckets[i]; b; b = next)
297         {
298             next = b->next;
299             name = (char *) (b + 1);
300             size = sizeof (struct objectBucket) + strlen (name) + 1;
301             FcMemFree (FC_MEM_STATICSTR, size);
302             free (b);
303         }
304         FcObjectBuckets[i] = 0;
305     }
306 }
307
308 const char *
309 FcObjectPtrU (FcObjectPtr si)
310 {
311     const FcObjectTypeList  *l;
312     int i, j;
313
314     if (si > 0)
315     {
316         if (si < biggest_known_ntypes)
317             return biggest_known_types[si].object;
318
319         j = 0;
320         for (l = _FcObjectTypes; l; l = l->next)
321             for (i = 0; i < l->ntypes; i++, j++)
322                 if (j == si)
323                     return l->types[i].object;
324     }
325
326     return _FcUserObjectNames[-si].object;
327 }
328
329 int
330 FcObjectNeededBytes ()
331 {
332     int num = 0, i;
333     for (i = 0; i < biggest_known_ntypes; i++)
334     {
335         const char * t = biggest_known_types[i].object;
336         num = num + strlen(t) + 1;
337     }
338     biggest_known_count = num;
339     return num + sizeof(int);
340 }
341
342 int
343 FcObjectNeededBytesAlign (void)
344 {
345     return fc_alignof (int) + fc_alignof (char);
346 }
347
348 void *
349 FcObjectDistributeBytes (FcCache * metadata, void * block_ptr)
350 {
351     block_ptr = ALIGN (block_ptr, int);
352     *(int *)block_ptr = biggest_known_ntypes;
353     block_ptr = (int *) block_ptr + 1;
354     block_ptr = ALIGN (block_ptr, char);
355     biggest_ptr = block_ptr;
356     block_ptr = (char *) block_ptr + biggest_known_count;
357     return block_ptr;
358 }
359
360 void
361 FcObjectSerialize (void)
362 {
363     int i;
364     for (i = 0; i < biggest_known_ntypes; i++)
365     {
366         const char * t = biggest_known_types[i].object;
367         strcpy (biggest_ptr, t);
368         biggest_ptr = biggest_ptr + strlen(t) + 1;
369     }
370 }
371
372 void *
373 FcObjectUnserialize (FcCache * metadata, void *block_ptr)
374 {
375     int new_biggest;
376     new_biggest = *(int *)block_ptr;
377     block_ptr = ALIGN (block_ptr, int);
378     block_ptr = (int *) block_ptr + 1;
379     if (biggest_known_ntypes < new_biggest)
380     {
381         int i;
382         char * bp = (char *)block_ptr;
383         FcObjectType * bn;
384         FcObjectTypeList * bnl;
385
386         bn = malloc (sizeof (const FcObjectType) * (new_biggest + 1));
387         if (!bn)
388             return 0;
389
390         bnl = malloc (sizeof (FcObjectTypeList));
391         if (!bnl)
392         {
393             free (bn);
394             return 0;
395         }
396
397         for (i = 0; i < new_biggest; i++)
398         {
399             const FcObjectType * t = FcNameGetObjectType(bp);
400             if (t)
401                 bn[i].type = t->type;
402             else
403                 bn[i].type = FcTypeVoid;
404             bn[i].object = bp;
405             bp = bp + strlen(bp) + 1;
406         }
407
408         FcNameUnregisterObjectTypesFree (biggest_known_types, biggest_known_ntypes, FcFalse);
409         if (allocated_biggest_known_types)
410         {
411             free ((FcObjectTypeList *)biggest_known_types);
412         }
413         else
414             allocated_biggest_known_types = FcTrue;
415
416         FcNameRegisterObjectTypes (bn, new_biggest);
417         biggest_known_ntypes = new_biggest;
418         biggest_known_types = (const FcObjectType *)bn;
419     }
420     block_ptr = ALIGN (block_ptr, char);
421     block_ptr = (char *) block_ptr + biggest_known_count;
422     return block_ptr;
423 }
424
425 static const FcConstant _FcBaseConstants[] = {
426     { (FcChar8 *) "thin",           "weight",   FC_WEIGHT_THIN, },
427     { (FcChar8 *) "extralight",     "weight",   FC_WEIGHT_EXTRALIGHT, },
428     { (FcChar8 *) "ultralight",     "weight",   FC_WEIGHT_EXTRALIGHT, },
429     { (FcChar8 *) "light",          "weight",   FC_WEIGHT_LIGHT, },
430     { (FcChar8 *) "book",           "weight",   FC_WEIGHT_BOOK, },
431     { (FcChar8 *) "regular",        "weight",   FC_WEIGHT_REGULAR, },
432     { (FcChar8 *) "medium",         "weight",   FC_WEIGHT_MEDIUM, },
433     { (FcChar8 *) "demibold",       "weight",   FC_WEIGHT_DEMIBOLD, },
434     { (FcChar8 *) "semibold",       "weight",   FC_WEIGHT_DEMIBOLD, },
435     { (FcChar8 *) "bold",           "weight",   FC_WEIGHT_BOLD, },
436     { (FcChar8 *) "extrabold",      "weight",   FC_WEIGHT_EXTRABOLD, },
437     { (FcChar8 *) "ultrabold",      "weight",   FC_WEIGHT_EXTRABOLD, },
438     { (FcChar8 *) "black",          "weight",   FC_WEIGHT_BLACK, },
439
440     { (FcChar8 *) "roman",          "slant",    FC_SLANT_ROMAN, },
441     { (FcChar8 *) "italic",         "slant",    FC_SLANT_ITALIC, },
442     { (FcChar8 *) "oblique",        "slant",    FC_SLANT_OBLIQUE, },
443
444     { (FcChar8 *) "ultracondensed", "width",    FC_WIDTH_ULTRACONDENSED },
445     { (FcChar8 *) "extracondensed", "width",    FC_WIDTH_EXTRACONDENSED },
446     { (FcChar8 *) "condensed",      "width",    FC_WIDTH_CONDENSED },
447     { (FcChar8 *) "semicondensed", "width",     FC_WIDTH_SEMICONDENSED },
448     { (FcChar8 *) "normal",         "width",    FC_WIDTH_NORMAL },
449     { (FcChar8 *) "semiexpanded",   "width",    FC_WIDTH_SEMIEXPANDED },
450     { (FcChar8 *) "expanded",       "width",    FC_WIDTH_EXPANDED },
451     { (FcChar8 *) "extraexpanded",  "width",    FC_WIDTH_EXTRAEXPANDED },
452     { (FcChar8 *) "ultraexpanded",  "width",    FC_WIDTH_ULTRAEXPANDED },
453     
454     { (FcChar8 *) "proportional",   "spacing",  FC_PROPORTIONAL, },
455     { (FcChar8 *) "dual",           "spacing",  FC_DUAL, },
456     { (FcChar8 *) "mono",           "spacing",  FC_MONO, },
457     { (FcChar8 *) "charcell",       "spacing",  FC_CHARCELL, },
458
459     { (FcChar8 *) "unknown",        "rgba",         FC_RGBA_UNKNOWN },
460     { (FcChar8 *) "rgb",            "rgba",         FC_RGBA_RGB, },
461     { (FcChar8 *) "bgr",            "rgba",         FC_RGBA_BGR, },
462     { (FcChar8 *) "vrgb",           "rgba",         FC_RGBA_VRGB },
463     { (FcChar8 *) "vbgr",           "rgba",         FC_RGBA_VBGR },
464     { (FcChar8 *) "none",           "rgba",         FC_RGBA_NONE },
465
466     { (FcChar8 *) "hintnone",       "hintstyle",   FC_HINT_NONE },
467     { (FcChar8 *) "hintslight",     "hintstyle",   FC_HINT_SLIGHT },
468     { (FcChar8 *) "hintmedium",     "hintstyle",   FC_HINT_MEDIUM },
469     { (FcChar8 *) "hintfull",       "hintstyle",   FC_HINT_FULL },
470 };
471
472 #define NUM_FC_CONSTANTS   (sizeof _FcBaseConstants/sizeof _FcBaseConstants[0])
473
474 typedef struct _FcConstantList FcConstantList;
475
476 struct _FcConstantList {
477     const FcConstantList    *next;
478     const FcConstant        *consts;
479     int                     nconsts;
480 };
481
482 static const FcConstantList _FcBaseConstantList = {
483     0,
484     _FcBaseConstants,
485     NUM_FC_CONSTANTS
486 };
487
488 static const FcConstantList     *_FcConstants = &_FcBaseConstantList;
489
490 FcBool
491 FcNameRegisterConstants (const FcConstant *consts, int nconsts)
492 {
493     FcConstantList      *l;
494
495     l = (FcConstantList *) malloc (sizeof (FcConstantList));
496     if (!l)
497         return FcFalse;
498     FcMemAlloc (FC_MEM_CONSTANT, sizeof (FcConstantList));
499     l->consts = consts;
500     l->nconsts = nconsts;
501     l->next = _FcConstants;
502     _FcConstants = l;
503     return FcTrue;
504 }
505
506 FcBool
507 FcNameUnregisterConstants (const FcConstant *consts, int nconsts)
508 {
509     const FcConstantList        *l, **prev;
510
511     for (prev = &_FcConstants; 
512          (l = *prev); 
513          prev = (const FcConstantList **) &(l->next))
514     {
515         if (l->consts == consts && l->nconsts == nconsts)
516         {
517             *prev = l->next;
518             FcMemFree (FC_MEM_CONSTANT, sizeof (FcConstantList));
519             free ((void *) l);
520             return FcTrue;
521         }
522     }
523     return FcFalse;
524 }
525
526 const FcConstant *
527 FcNameGetConstant (FcChar8 *string)
528 {
529     const FcConstantList    *l;
530     int                     i;
531
532     for (l = _FcConstants; l; l = l->next)
533     {
534         for (i = 0; i < l->nconsts; i++)
535             if (!FcStrCmpIgnoreCase (string, l->consts[i].name))
536                 return &l->consts[i];
537     }
538     return 0;
539 }
540
541 FcBool
542 FcNameConstant (FcChar8 *string, int *result)
543 {
544     const FcConstant    *c;
545
546     if ((c = FcNameGetConstant(string)))
547     {
548         *result = c->value;
549         return FcTrue;
550     }
551     return FcFalse;
552 }
553
554 FcBool
555 FcNameBool (const FcChar8 *v, FcBool *result)
556 {
557     char    c0, c1;
558
559     c0 = *v;
560     c0 = FcToLower (c0);
561     if (c0 == 't' || c0 == 'y' || c0 == '1')
562     {
563         *result = FcTrue;
564         return FcTrue;
565     }
566     if (c0 == 'f' || c0 == 'n' || c0 == '0')
567     {
568         *result = FcFalse;
569         return FcTrue;
570     }
571     if (c0 == 'o')
572     {
573         c1 = v[1];
574         c1 = FcToLower (c1);
575         if (c1 == 'n')
576         {
577             *result = FcTrue;
578             return FcTrue;
579         }
580         if (c1 == 'f')
581         {
582             *result = FcFalse;
583             return FcTrue;
584         }
585     }
586     return FcFalse;
587 }
588
589 static FcValue
590 FcNameConvert (FcType type, FcChar8 *string, FcMatrix *m)
591 {
592     FcValue     v;
593
594     v.type = type;
595     switch (v.type) {
596     case FcTypeInteger:
597         if (!FcNameConstant (string, &v.u.i))
598             v.u.i = atoi ((char *) string);
599         break;
600     case FcTypeString:
601         v.u.s = FcStrStaticName(string);
602         break;
603     case FcTypeBool:
604         if (!FcNameBool (string, &v.u.b))
605             v.u.b = FcFalse;
606         break;
607     case FcTypeDouble:
608         v.u.d = strtod ((char *) string, 0);
609         break;
610     case FcTypeMatrix:
611         v.u.m = m;
612         sscanf ((char *) string, "%lg %lg %lg %lg", &m->xx, &m->xy, &m->yx, &m->yy);
613         break;
614     case FcTypeCharSet:
615         v.u.c = FcNameParseCharSet (string);
616         break;
617     case FcTypeLangSet:
618         v.u.l = FcNameParseLangSet (string);
619         break;
620     default:
621         break;
622     }
623     return v;
624 }
625
626 static const FcChar8 *
627 FcNameFindNext (const FcChar8 *cur, const char *delim, FcChar8 *save, FcChar8 *last)
628 {
629     FcChar8    c;
630     
631     while ((c = *cur))
632     {
633         if (c == '\\')
634         {
635             ++cur;
636             if (!(c = *cur))
637                 break;
638         }
639         else if (strchr (delim, c))
640             break;
641         ++cur;
642         *save++ = c;
643     }
644     *save = 0;
645     *last = *cur;
646     if (*cur)
647         cur++;
648     return cur;
649 }
650
651 FcPattern *
652 FcNameParse (const FcChar8 *name)
653 {
654     FcChar8             *save;
655     FcPattern           *pat;
656     double              d;
657     FcChar8             *e;
658     FcChar8             delim;
659     FcValue             v;
660     FcMatrix            m;
661     const FcObjectType  *t;
662     const FcConstant    *c;
663
664     /* freed below */
665     save = malloc (strlen ((char *) name) + 1);
666     if (!save)
667         goto bail0;
668     pat = FcPatternCreate ();
669     if (!pat)
670         goto bail1;
671
672     for (;;)
673     {
674         name = FcNameFindNext (name, "-,:", save, &delim);
675         if (save[0])
676         {
677             if (!FcPatternAddString (pat, FC_FAMILY, save))
678                 goto bail2;
679         }
680         if (delim != ',')
681             break;
682     }
683     if (delim == '-')
684     {
685         for (;;)
686         {
687             name = FcNameFindNext (name, "-,:", save, &delim);
688             d = strtod ((char *) save, (char **) &e);
689             if (e != save)
690             {
691                 if (!FcPatternAddDouble (pat, FC_SIZE, d))
692                     goto bail2;
693             }
694             if (delim != ',')
695                 break;
696         }
697     }
698     while (delim == ':')
699     {
700         name = FcNameFindNext (name, "=_:", save, &delim);
701         if (save[0])
702         {
703             if (delim == '=' || delim == '_')
704             {
705                 t = FcNameGetObjectType ((char *) save);
706                 for (;;)
707                 {
708                     name = FcNameFindNext (name, ":,", save, &delim);
709                     if (t && strcmp (t->object, _FcBaseObjectTypes[0].object))
710                     {
711                         v = FcNameConvert (t->type, save, &m);
712                         if (!FcPatternAdd (pat, t->object, v, FcTrue))
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                             goto bail2;
725                         }
726                         switch (v.type) {
727                         case FcTypeCharSet:
728                             FcCharSetDestroy ((FcCharSet *) v.u.c);
729                             break;
730                         case FcTypeLangSet:
731                             FcLangSetDestroy ((FcLangSet *) v.u.l);
732                             break;
733                         default:
734                             break;
735                         }
736                     }
737                     if (delim != ',')
738                         break;
739                 }
740             }
741             else
742             {
743                 if ((c = FcNameGetConstant (save)))
744                 {
745                     if (!FcPatternAddInteger (pat, c->object, c->value))
746                         goto bail2;
747                 }
748             }
749         }
750     }
751
752     free (save);
753     return pat;
754
755 bail2:
756     FcPatternDestroy (pat);
757 bail1:
758     free (save);
759 bail0:
760     return 0;
761 }
762 static FcBool
763 FcNameUnparseString (FcStrBuf       *buf, 
764                      const FcChar8  *string,
765                      const FcChar8  *escape)
766 {
767     FcChar8 c;
768     while ((c = *string++))
769     {
770         if (escape && strchr ((char *) escape, (char) c))
771         {
772             if (!FcStrBufChar (buf, escape[0]))
773                 return FcFalse;
774         }
775         if (!FcStrBufChar (buf, c))
776             return FcFalse;
777     }
778     return FcTrue;
779 }
780
781 static FcBool
782 FcNameUnparseValue (FcStrBuf    *buf,
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, &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 }