fclang.c revision c9710b42
1/*
2 * fontconfig/src/fclang.c
3 *
4 * Copyright © 2002 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 the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission.  The authors make 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 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) 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 "fcint.h"
26#include "fcftint.h"
27
28/* Objects MT-safe for readonly access. */
29
30typedef struct {
31    const FcChar8    	lang[8];
32    const FcCharSet	charset;
33} FcLangCharSet;
34
35typedef struct {
36    int begin;
37    int end;
38} FcLangCharSetRange;
39
40#include "../fc-lang/fclang.h"
41
42struct _FcLangSet {
43    FcStrSet	*extra;
44    FcChar32    map_size;
45    FcChar32	map[NUM_LANG_SET_MAP];
46};
47
48static int FcLangSetIndex (const FcChar8 *lang);
49
50
51static void
52FcLangSetBitSet (FcLangSet    *ls,
53		 unsigned int  id)
54{
55  unsigned int bucket;
56
57  id = fcLangCharSetIndices[id];
58  bucket = id >> 5;
59  if (bucket >= ls->map_size)
60    return; /* shouldn't happen really */
61
62  ls->map[bucket] |= ((FcChar32) 1 << (id & 0x1f));
63}
64
65static FcBool
66FcLangSetBitGet (const FcLangSet *ls,
67		 unsigned int     id)
68{
69  unsigned int bucket;
70
71  id = fcLangCharSetIndices[id];
72  bucket = id >> 5;
73  if (bucket >= ls->map_size)
74    return FcFalse;
75
76  return ((ls->map[bucket] >> (id & 0x1f)) & 1) ? FcTrue : FcFalse;
77}
78
79static void
80FcLangSetBitReset (FcLangSet    *ls,
81		   unsigned int  id)
82{
83  unsigned int bucket;
84
85  id = fcLangCharSetIndices[id];
86  bucket = id >> 5;
87  if (bucket >= ls->map_size)
88    return; /* shouldn't happen really */
89
90  ls->map[bucket] &= ~((FcChar32) 1 << (id & 0x1f));
91}
92
93FcLangSet *
94FcFreeTypeLangSet (const FcCharSet  *charset,
95		   const FcChar8    *exclusiveLang)
96{
97    int		    i, j;
98    FcChar32	    missing;
99    const FcCharSet *exclusiveCharset = 0;
100    FcLangSet	    *ls;
101
102    if (exclusiveLang)
103	exclusiveCharset = FcLangGetCharSet (exclusiveLang);
104    ls = FcLangSetCreate ();
105    if (!ls)
106	return 0;
107    if (FcDebug() & FC_DBG_LANGSET)
108    {
109	printf ("font charset");
110	FcCharSetPrint (charset);
111	printf ("\n");
112    }
113    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
114    {
115	if (FcDebug() & FC_DBG_LANGSET)
116	{
117	    printf ("%s charset", fcLangCharSets[i].lang);
118	    FcCharSetPrint (&fcLangCharSets[i].charset);
119	    printf ("\n");
120	}
121
122	/*
123	 * Check for Han charsets to make fonts
124	 * which advertise support for a single language
125	 * not support other Han languages
126	 */
127	if (exclusiveCharset &&
128	    FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang))
129	{
130	    if (fcLangCharSets[i].charset.num != exclusiveCharset->num)
131		continue;
132
133	    for (j = 0; j < fcLangCharSets[i].charset.num; j++)
134		if (FcCharSetLeaf(&fcLangCharSets[i].charset, j) !=
135		    FcCharSetLeaf(exclusiveCharset, j))
136		    continue;
137	}
138	missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
139        if (FcDebug() & FC_DBG_SCANV)
140	{
141	    if (missing && missing < 10)
142	    {
143		FcCharSet   *missed = FcCharSetSubtract (&fcLangCharSets[i].charset,
144							 charset);
145		FcChar32    ucs4;
146		FcChar32    map[FC_CHARSET_MAP_SIZE];
147		FcChar32    next;
148
149		printf ("\n%s(%u) ", fcLangCharSets[i].lang, missing);
150		printf ("{");
151		for (ucs4 = FcCharSetFirstPage (missed, map, &next);
152		     ucs4 != FC_CHARSET_DONE;
153		     ucs4 = FcCharSetNextPage (missed, map, &next))
154		{
155		    int	    i, j;
156		    for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
157			if (map[i])
158			{
159			    for (j = 0; j < 32; j++)
160				if (map[i] & (1 << j))
161				    printf (" %04x", ucs4 + i * 32 + j);
162			}
163		}
164		printf (" }\n\t");
165		FcCharSetDestroy (missed);
166	    }
167	    else
168		printf ("%s(%u) ", fcLangCharSets[i].lang, missing);
169	}
170	if (!missing)
171	    FcLangSetBitSet (ls, i);
172    }
173
174    if (FcDebug() & FC_DBG_SCANV)
175	printf ("\n");
176
177
178    return ls;
179}
180
181FcChar8 *
182FcLangNormalize (const FcChar8 *lang)
183{
184    FcChar8 *result = NULL, *s, *orig;
185    char *territory, *encoding, *modifier;
186    size_t llen, tlen = 0, mlen = 0;
187
188    if (!lang || !*lang)
189	return NULL;
190
191    if (FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C") == 0 ||
192	FcStrCmpIgnoreCase (lang, (const FcChar8 *)"POSIX") == 0)
193    {
194	result = FcStrCopy ((const FcChar8 *)"en");
195	goto bail;
196    }
197
198    s = FcStrCopy (lang);
199    if (!s)
200	goto bail;
201
202    /* from the comments in glibc:
203     *
204     * LOCALE can consist of up to four recognized parts for the XPG syntax:
205     *
206     *            language[_territory[.codeset]][@modifier]
207     *
208     * Beside the first all of them are allowed to be missing.  If the
209     * full specified locale is not found, the less specific one are
210     * looked for.  The various part will be stripped off according to
211     * the following order:
212     *            (1) codeset
213     *            (2) normalized codeset
214     *            (3) territory
215     *            (4) modifier
216     *
217     * So since we don't take care of the codeset part here, what patterns
218     * we need to deal with is:
219     *
220     *   1. language_territory@modifier
221     *   2. language@modifier
222     *   3. language
223     *
224     * then. and maybe no need to try language_territory here.
225     */
226    modifier = strchr ((const char *) s, '@');
227    if (modifier)
228    {
229	*modifier = 0;
230	modifier++;
231	mlen = strlen (modifier);
232    }
233    encoding = strchr ((const char *) s, '.');
234    if (encoding)
235    {
236	*encoding = 0;
237	encoding++;
238	if (modifier)
239	{
240	    memmove (encoding, modifier, mlen + 1);
241	    modifier = encoding;
242	}
243    }
244    territory = strchr ((const char *) s, '_');
245    if (!territory)
246	territory = strchr ((const char *) s, '-');
247    if (territory)
248    {
249	*territory = 0;
250	territory++;
251	tlen = strlen (territory);
252    }
253    llen = strlen ((const char *) s);
254    if (llen < 2 || llen > 3)
255    {
256	fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid language tag\n",
257		 lang);
258	goto bail0;
259    }
260    if (territory && (tlen < 2 || tlen > 3))
261    {
262	fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid region tag\n",
263		 lang);
264	goto bail0;
265    }
266    if (territory)
267	territory[-1] = '-';
268    if (modifier)
269	modifier[-1] = '@';
270    orig = FcStrDowncase (s);
271    if (!orig)
272	goto bail0;
273    if (territory)
274    {
275	if (FcDebug () & FC_DBG_LANGSET)
276	    printf("Checking the existence of %s.orth\n", s);
277	if (FcLangSetIndex (s) < 0)
278	{
279	    memmove (territory - 1, territory + tlen, (mlen > 0 ? mlen + 1 : 0) + 1);
280	    if (modifier)
281		modifier = territory;
282	}
283	else
284	{
285	    result = s;
286	    /* we'll miss the opportunity to reduce the correct size
287	     * of the allocated memory for the string after that.
288	     */
289	    s = NULL;
290	    goto bail1;
291	}
292    }
293    if (modifier)
294    {
295	if (FcDebug () & FC_DBG_LANGSET)
296	    printf("Checking the existence of %s.orth\n", s);
297	if (FcLangSetIndex (s) < 0)
298	    modifier[-1] = 0;
299	else
300	{
301	    result = s;
302	    /* we'll miss the opportunity to reduce the correct size
303	     * of the allocated memory for the string after that.
304	     */
305	    s = NULL;
306	    goto bail1;
307	}
308    }
309    if (FcDebug () & FC_DBG_LANGSET)
310	printf("Checking the existence of %s.orth\n", s);
311    if (FcLangSetIndex (s) < 0)
312    {
313	/* there seems no languages matched in orth.
314	 * add the language as is for fallback.
315	 */
316	result = orig;
317	orig = NULL;
318    }
319    else
320    {
321	result = s;
322	/* we'll miss the opportunity to reduce the correct size
323	 * of the allocated memory for the string after that.
324	 */
325	s = NULL;
326    }
327  bail1:
328    if (orig)
329	FcStrFree (orig);
330  bail0:
331    if (s)
332	free (s);
333  bail:
334    if (FcDebug () & FC_DBG_LANGSET)
335    {
336	if (result)
337	    printf ("normalized: %s -> %s\n", lang, result);
338	else
339	    printf ("Unable to normalize %s\n", lang);
340    }
341
342    return result;
343}
344
345#define FcLangEnd(c)	((c) == '-' || (c) == '\0')
346
347FcLangResult
348FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
349{
350    FcChar8	    c1, c2;
351    FcLangResult    result = FcLangDifferentLang;
352
353    for (;;)
354    {
355	c1 = *s1++;
356	c2 = *s2++;
357
358	c1 = FcToLower (c1);
359	c2 = FcToLower (c2);
360	if (c1 != c2)
361	{
362	    if (FcLangEnd (c1) && FcLangEnd (c2))
363		result = FcLangDifferentTerritory;
364	    return result;
365	}
366	else if (!c1)
367	    return FcLangEqual;
368	else if (c1 == '-')
369	    result = FcLangDifferentTerritory;
370    }
371}
372
373/*
374 * Return FcTrue when super contains sub.
375 *
376 * super contains sub if super and sub have the same
377 * language and either the same country or one
378 * is missing the country
379 */
380
381static FcBool
382FcLangContains (const FcChar8 *super, const FcChar8 *sub)
383{
384    FcChar8	    c1, c2;
385
386    for (;;)
387    {
388	c1 = *super++;
389	c2 = *sub++;
390
391	c1 = FcToLower (c1);
392	c2 = FcToLower (c2);
393	if (c1 != c2)
394	{
395	    /* see if super has a country while sub is mising one */
396	    if (c1 == '-' && c2 == '\0')
397		return FcTrue;
398	    /* see if sub has a country while super is mising one */
399	    if (c1 == '\0' && c2 == '-')
400		return FcTrue;
401	    return FcFalse;
402	}
403	else if (!c1)
404	    return FcTrue;
405    }
406}
407
408const FcCharSet *
409FcLangGetCharSet (const FcChar8 *lang)
410{
411    int		i;
412    int		country = -1;
413
414    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
415    {
416	switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
417	case FcLangEqual:
418	    return &fcLangCharSets[i].charset;
419	case FcLangDifferentTerritory:
420	    if (country == -1)
421		country = i;
422	case FcLangDifferentLang:
423	default:
424	    break;
425	}
426    }
427    if (country == -1)
428	return 0;
429    return &fcLangCharSets[country].charset;
430}
431
432FcStrSet *
433FcGetLangs (void)
434{
435    FcStrSet *langs;
436    int	i;
437
438    langs = FcStrSetCreate();
439    if (!langs)
440	return 0;
441
442    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
443	FcStrSetAdd (langs, fcLangCharSets[i].lang);
444
445    return langs;
446}
447
448FcLangSet *
449FcLangSetCreate (void)
450{
451    FcLangSet	*ls;
452
453    ls = malloc (sizeof (FcLangSet));
454    if (!ls)
455	return 0;
456    memset (ls->map, '\0', sizeof (ls->map));
457    ls->map_size = NUM_LANG_SET_MAP;
458    ls->extra = 0;
459    return ls;
460}
461
462void
463FcLangSetDestroy (FcLangSet *ls)
464{
465    if (ls->extra)
466	FcStrSetDestroy (ls->extra);
467    free (ls);
468}
469
470FcLangSet *
471FcLangSetCopy (const FcLangSet *ls)
472{
473    FcLangSet	*new;
474
475    new = FcLangSetCreate ();
476    if (!new)
477	goto bail0;
478    memset (new->map, '\0', sizeof (new->map));
479    memcpy (new->map, ls->map, FC_MIN (sizeof (new->map), ls->map_size * sizeof (ls->map[0])));
480    if (ls->extra)
481    {
482	FcStrList	*list;
483	FcChar8		*extra;
484
485	new->extra = FcStrSetCreate ();
486	if (!new->extra)
487	    goto bail1;
488
489	list = FcStrListCreate (ls->extra);
490	if (!list)
491	    goto bail1;
492
493	while ((extra = FcStrListNext (list)))
494	    if (!FcStrSetAdd (new->extra, extra))
495	    {
496		FcStrListDone (list);
497		goto bail1;
498	    }
499	FcStrListDone (list);
500    }
501    return new;
502bail1:
503    FcLangSetDestroy (new);
504bail0:
505    return 0;
506}
507
508static int
509FcLangSetIndex (const FcChar8 *lang)
510{
511    int	    low, high, mid = 0;
512    int	    cmp = 0;
513    FcChar8 firstChar = FcToLower(lang[0]);
514    FcChar8 secondChar = firstChar ? FcToLower(lang[1]) : '\0';
515
516    if (firstChar < 'a')
517    {
518	low = 0;
519	high = fcLangCharSetRanges[0].begin;
520    }
521    else if(firstChar > 'z')
522    {
523	low = fcLangCharSetRanges[25].begin;
524	high = NUM_LANG_CHAR_SET - 1;
525    }
526    else
527    {
528	low = fcLangCharSetRanges[firstChar - 'a'].begin;
529	high = fcLangCharSetRanges[firstChar - 'a'].end;
530	/* no matches */
531	if (low > high)
532	    return -low; /* next entry after where it would be */
533    }
534
535    while (low <= high)
536    {
537	mid = (high + low) >> 1;
538	if(fcLangCharSets[mid].lang[0] != firstChar)
539	    cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang);
540	else
541	{   /* fast path for resolving 2-letter languages (by far the most common) after
542	     * finding the first char (probably already true because of the hash table) */
543	    cmp = fcLangCharSets[mid].lang[1] - secondChar;
544	    if (cmp == 0 &&
545		(fcLangCharSets[mid].lang[2] != '\0' ||
546		 lang[2] != '\0'))
547	    {
548		cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2,
549					 lang+2);
550	    }
551	}
552	if (cmp == 0)
553	    return mid;
554	if (cmp < 0)
555	    low = mid + 1;
556	else
557	    high = mid - 1;
558    }
559    if (cmp < 0)
560	mid++;
561    return -(mid + 1);
562}
563
564FcBool
565FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
566{
567    int	    id;
568
569    id = FcLangSetIndex (lang);
570    if (id >= 0)
571    {
572	FcLangSetBitSet (ls, id);
573	return FcTrue;
574    }
575    if (!ls->extra)
576    {
577	ls->extra = FcStrSetCreate ();
578	if (!ls->extra)
579	    return FcFalse;
580    }
581    return FcStrSetAdd (ls->extra, lang);
582}
583
584FcBool
585FcLangSetDel (FcLangSet *ls, const FcChar8 *lang)
586{
587    int	id;
588
589    id = FcLangSetIndex (lang);
590    if (id >= 0)
591    {
592	FcLangSetBitReset (ls, id);
593    }
594    else if (ls->extra)
595    {
596	FcStrSetDel (ls->extra, lang);
597    }
598    return FcTrue;
599}
600
601FcLangResult
602FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
603{
604    int		    id;
605    FcLangResult    best, r;
606    int		    i;
607
608    id = FcLangSetIndex (lang);
609    if (id < 0)
610	id = -id - 1;
611    else if (FcLangSetBitGet (ls, id))
612	return FcLangEqual;
613    best = FcLangDifferentLang;
614    for (i = id - 1; i >= 0; i--)
615    {
616	r = FcLangCompare (lang, fcLangCharSets[i].lang);
617	if (r == FcLangDifferentLang)
618	    break;
619	if (FcLangSetBitGet (ls, i) && r < best)
620	    best = r;
621    }
622    for (i = id; i < NUM_LANG_CHAR_SET; i++)
623    {
624	r = FcLangCompare (lang, fcLangCharSets[i].lang);
625	if (r == FcLangDifferentLang)
626	    break;
627	if (FcLangSetBitGet (ls, i) && r < best)
628	    best = r;
629    }
630    if (ls->extra)
631    {
632	FcStrList	*list = FcStrListCreate (ls->extra);
633	FcChar8		*extra;
634
635	if (list)
636	{
637	    while (best > FcLangEqual && (extra = FcStrListNext (list)))
638	    {
639		r = FcLangCompare (lang, extra);
640		if (r < best)
641		    best = r;
642	    }
643	    FcStrListDone (list);
644	}
645    }
646    return best;
647}
648
649static FcLangResult
650FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
651{
652    FcStrList	    *list = FcStrListCreate (set);
653    FcLangResult    r, best = FcLangDifferentLang;
654    FcChar8	    *extra;
655
656    if (list)
657    {
658	while (best > FcLangEqual && (extra = FcStrListNext (list)))
659	{
660	    r = FcLangSetHasLang (ls, extra);
661	    if (r < best)
662		best = r;
663	}
664	FcStrListDone (list);
665    }
666    return best;
667}
668
669FcLangResult
670FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
671{
672    int		    i, j, count;
673    FcLangResult    best, r;
674
675    count = FC_MIN (lsa->map_size, lsb->map_size);
676    count = FC_MIN (NUM_LANG_SET_MAP, count);
677    for (i = 0; i < count; i++)
678	if (lsa->map[i] & lsb->map[i])
679	    return FcLangEqual;
680    best = FcLangDifferentLang;
681    for (j = 0; j < NUM_COUNTRY_SET; j++)
682	for (i = 0; i < count; i++)
683	    if ((lsa->map[i] & fcLangCountrySets[j][i]) &&
684		(lsb->map[i] & fcLangCountrySets[j][i]))
685	    {
686		best = FcLangDifferentTerritory;
687		break;
688	    }
689    if (lsa->extra)
690    {
691	r = FcLangSetCompareStrSet (lsb, lsa->extra);
692	if (r < best)
693	    best = r;
694    }
695    if (best > FcLangEqual && lsb->extra)
696    {
697	r = FcLangSetCompareStrSet (lsa, lsb->extra);
698	if (r < best)
699	    best = r;
700    }
701    return best;
702}
703
704/*
705 * Used in computing values -- mustn't allocate any storage
706 */
707FcLangSet *
708FcLangSetPromote (const FcChar8 *lang, FcValuePromotionBuffer *vbuf)
709{
710    int		id;
711    typedef struct {
712	FcLangSet  ls;
713	FcStrSet   strs;
714	FcChar8   *str;
715    } FcLangSetPromotionBuffer;
716    FcLangSetPromotionBuffer *buf = (FcLangSetPromotionBuffer *) vbuf;
717
718    FC_ASSERT_STATIC (sizeof (FcLangSetPromotionBuffer) <= sizeof (FcValuePromotionBuffer));
719
720    memset (buf->ls.map, '\0', sizeof (buf->ls.map));
721    buf->ls.map_size = NUM_LANG_SET_MAP;
722    buf->ls.extra = 0;
723    id = FcLangSetIndex (lang);
724    if (id > 0)
725    {
726	FcLangSetBitSet (&buf->ls, id);
727    }
728    else
729    {
730	buf->ls.extra = &buf->strs;
731	buf->strs.num = 1;
732	buf->strs.size = 1;
733	buf->strs.strs = &buf->str;
734	FcRefInit (&buf->strs.ref, 1);
735	buf->str = (FcChar8 *) lang;
736    }
737    return &buf->ls;
738}
739
740FcChar32
741FcLangSetHash (const FcLangSet *ls)
742{
743    FcChar32	h = 0;
744    int		i, count;
745
746    count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
747    for (i = 0; i < count; i++)
748	h ^= ls->map[i];
749    if (ls->extra)
750	h ^= ls->extra->num;
751    return h;
752}
753
754FcLangSet *
755FcNameParseLangSet (const FcChar8 *string)
756{
757    FcChar8	    lang[32], c = 0;
758    int i;
759    FcLangSet	    *ls;
760
761    ls = FcLangSetCreate ();
762    if (!ls)
763	goto bail0;
764
765    for(;;)
766    {
767	for(i = 0; i < 31;i++)
768	{
769	    c = *string++;
770	    if(c == '\0' || c == '|')
771		break; /* end of this code */
772	    lang[i] = c;
773	}
774	lang[i] = '\0';
775	if (!FcLangSetAdd (ls, lang))
776	    goto bail1;
777	if(c == '\0')
778	    break;
779    }
780    return ls;
781bail1:
782    FcLangSetDestroy (ls);
783bail0:
784    return 0;
785}
786
787FcBool
788FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
789{
790    int		i, bit, count;
791    FcChar32	bits;
792    FcBool	first = FcTrue;
793
794    count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
795    for (i = 0; i < count; i++)
796    {
797	if ((bits = ls->map[i]))
798	{
799	    for (bit = 0; bit <= 31; bit++)
800		if (bits & (1 << bit))
801		{
802		    int id = (i << 5) | bit;
803		    if (!first)
804			if (!FcStrBufChar (buf, '|'))
805			    return FcFalse;
806		    if (!FcStrBufString (buf, fcLangCharSets[fcLangCharSetIndicesInv[id]].lang))
807			return FcFalse;
808		    first = FcFalse;
809		}
810	}
811    }
812    if (ls->extra)
813    {
814	FcStrList   *list = FcStrListCreate (ls->extra);
815	FcChar8	    *extra;
816
817	if (!list)
818	    return FcFalse;
819	while ((extra = FcStrListNext (list)))
820	{
821	    if (!first)
822		if (!FcStrBufChar (buf, '|'))
823                {
824                    FcStrListDone (list);
825		    return FcFalse;
826                }
827	    if (!FcStrBufString (buf, extra))
828                {
829                    FcStrListDone (list);
830                    return FcFalse;
831                }
832	    first = FcFalse;
833	}
834        FcStrListDone (list);
835    }
836    return FcTrue;
837}
838
839FcBool
840FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
841{
842    int	    i, count;
843
844    count = FC_MIN (lsa->map_size, lsb->map_size);
845    count = FC_MIN (NUM_LANG_SET_MAP, count);
846    for (i = 0; i < count; i++)
847    {
848	if (lsa->map[i] != lsb->map[i])
849	    return FcFalse;
850    }
851    if (!lsa->extra && !lsb->extra)
852	return FcTrue;
853    if (lsa->extra && lsb->extra)
854	return FcStrSetEqual (lsa->extra, lsb->extra);
855    return FcFalse;
856}
857
858static FcBool
859FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang)
860{
861    int		    id;
862    int		    i;
863
864    id = FcLangSetIndex (lang);
865    if (id < 0)
866	id = -id - 1;
867    else if (FcLangSetBitGet (ls, id))
868	return FcTrue;
869    /*
870     * search up and down among equal languages for a match
871     */
872    for (i = id - 1; i >= 0; i--)
873    {
874	if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
875	    break;
876	if (FcLangSetBitGet (ls, i) &&
877	    FcLangContains (fcLangCharSets[i].lang, lang))
878	    return FcTrue;
879    }
880    for (i = id; i < NUM_LANG_CHAR_SET; i++)
881    {
882	if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
883	    break;
884	if (FcLangSetBitGet (ls, i) &&
885	    FcLangContains (fcLangCharSets[i].lang, lang))
886	    return FcTrue;
887    }
888    if (ls->extra)
889    {
890	FcStrList	*list = FcStrListCreate (ls->extra);
891	FcChar8		*extra;
892
893	if (list)
894	{
895	    while ((extra = FcStrListNext (list)))
896	    {
897		if (FcLangContains (extra, lang))
898		    break;
899	    }
900	    FcStrListDone (list);
901    	    if (extra)
902		return FcTrue;
903	}
904    }
905    return FcFalse;
906}
907
908/*
909 * return FcTrue if lsa contains every language in lsb
910 */
911FcBool
912FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb)
913{
914    int		    i, j, count;
915    FcChar32	    missing;
916
917    if (FcDebug() & FC_DBG_MATCHV)
918    {
919	printf ("FcLangSet "); FcLangSetPrint (lsa);
920	printf (" contains "); FcLangSetPrint (lsb);
921	printf ("\n");
922    }
923    /*
924     * check bitmaps for missing language support
925     */
926    count = FC_MIN (lsa->map_size, lsb->map_size);
927    count = FC_MIN (NUM_LANG_SET_MAP, count);
928    for (i = 0; i < count; i++)
929    {
930	missing = lsb->map[i] & ~lsa->map[i];
931	if (missing)
932	{
933	    for (j = 0; j < 32; j++)
934		if (missing & (1 << j))
935		{
936		    if (!FcLangSetContainsLang (lsa,
937						fcLangCharSets[fcLangCharSetIndicesInv[i*32 + j]].lang))
938		    {
939			if (FcDebug() & FC_DBG_MATCHV)
940			    printf ("\tMissing bitmap %s\n", fcLangCharSets[fcLangCharSetIndicesInv[i*32+j]].lang);
941			return FcFalse;
942		    }
943		}
944	}
945    }
946    if (lsb->extra)
947    {
948	FcStrList   *list = FcStrListCreate (lsb->extra);
949	FcChar8	    *extra;
950
951	if (list)
952	{
953	    while ((extra = FcStrListNext (list)))
954	    {
955		if (!FcLangSetContainsLang (lsa, extra))
956		{
957		    if (FcDebug() & FC_DBG_MATCHV)
958			printf ("\tMissing string %s\n", extra);
959		    break;
960		}
961	    }
962	    FcStrListDone (list);
963	    if (extra)
964		return FcFalse;
965	}
966    }
967    return FcTrue;
968}
969
970FcBool
971FcLangSetSerializeAlloc (FcSerialize *serialize, const FcLangSet *l)
972{
973    if (!FcSerializeAlloc (serialize, l, sizeof (FcLangSet)))
974	return FcFalse;
975    return FcTrue;
976}
977
978FcLangSet *
979FcLangSetSerialize(FcSerialize *serialize, const FcLangSet *l)
980{
981    FcLangSet	*l_serialize = FcSerializePtr (serialize, l);
982
983    if (!l_serialize)
984	return NULL;
985    memset (l_serialize->map, '\0', sizeof (l_serialize->map));
986    memcpy (l_serialize->map, l->map, FC_MIN (sizeof (l_serialize->map), l->map_size * sizeof (l->map[0])));
987    l_serialize->map_size = NUM_LANG_SET_MAP;
988    l_serialize->extra = NULL; /* We don't serialize ls->extra */
989    return l_serialize;
990}
991
992FcStrSet *
993FcLangSetGetLangs (const FcLangSet *ls)
994{
995    FcStrSet *langs;
996    int	      i;
997
998    langs = FcStrSetCreate();
999    if (!langs)
1000	return 0;
1001
1002    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
1003	if (FcLangSetBitGet (ls, i))
1004	    FcStrSetAdd (langs, fcLangCharSets[i].lang);
1005
1006    if (ls->extra)
1007    {
1008	FcStrList	*list = FcStrListCreate (ls->extra);
1009	FcChar8		*extra;
1010
1011	if (list)
1012	{
1013	    while ((extra = FcStrListNext (list)))
1014		FcStrSetAdd (langs, extra);
1015
1016	    FcStrListDone (list);
1017	}
1018    }
1019
1020    return langs;
1021}
1022
1023static FcLangSet *
1024FcLangSetOperate(const FcLangSet	*a,
1025		 const FcLangSet	*b,
1026		 FcBool			(*func) (FcLangSet 	*ls,
1027						 const FcChar8	*s))
1028{
1029    FcLangSet	*langset = FcLangSetCopy (a);
1030    FcStrSet	*set = FcLangSetGetLangs (b);
1031    FcStrList	*sl = FcStrListCreate (set);
1032    FcChar8	*str;
1033
1034    FcStrSetDestroy (set);
1035    while ((str = FcStrListNext (sl)))
1036    {
1037	func (langset, str);
1038    }
1039    FcStrListDone (sl);
1040
1041    return langset;
1042}
1043
1044FcLangSet *
1045FcLangSetUnion (const FcLangSet *a, const FcLangSet *b)
1046{
1047    return FcLangSetOperate(a, b, FcLangSetAdd);
1048}
1049
1050FcLangSet *
1051FcLangSetSubtract (const FcLangSet *a, const FcLangSet *b)
1052{
1053    return FcLangSetOperate(a, b, FcLangSetDel);
1054}
1055
1056#define __fclang__
1057#include "fcaliastail.h"
1058#include "fcftaliastail.h"
1059#undef __fclang__
1060