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