]> git.wh0rd.org - fontconfig.git/blob - src/fclang.c
Remove some unused variables, and initialize some other ones so gcc doesn't
[fontconfig.git] / src / fclang.c
1 /*
2 * $RCSId: xc/lib/fontconfig/src/fclang.c,v 1.7 2002/08/26 23:34:31 keithp Exp $
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 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 "fcint.h"
26
27 typedef struct {
28 FcChar8 *lang;
29 FcCharSet charset;
30 } FcLangCharSet;
31
32 typedef struct {
33 int begin;
34 int end;
35 } FcLangCharSetRange;
36
37 #include "../fc-lang/fclang.h"
38
39 struct _FcLangSet {
40 FcChar32 map[NUM_LANG_SET_MAP];
41 FcStrSet *extra;
42 };
43
44 #define FcLangSetBitSet(ls, id) ((ls)->map[(id)>>5] |= ((FcChar32) 1 << ((id) & 0x1f)))
45 #define FcLangSetBitGet(ls, id) (((ls)->map[(id)>>5] >> ((id) & 0x1f)) & 1)
46
47 FcLangSet *
48 FcFreeTypeLangSet (const FcCharSet *charset,
49 const FcChar8 *exclusiveLang)
50 {
51 int i;
52 FcChar32 missing;
53 const FcCharSet *exclusiveCharset = 0;
54 FcLangSet *ls;
55
56
57 if (exclusiveLang)
58 exclusiveCharset = FcCharSetForLang (exclusiveLang);
59 ls = FcLangSetCreate ();
60 if (!ls)
61 return 0;
62 for (i = 0; i < NUM_LANG_CHAR_SET; i++)
63 {
64 /*
65 * Check for Han charsets to make fonts
66 * which advertise support for a single language
67 * not support other Han languages
68 */
69 if (exclusiveCharset &&
70 FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang) &&
71 fcLangCharSets[i].charset.leaves != exclusiveCharset->leaves)
72 {
73 continue;
74 }
75 missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
76 if (FcDebug() & FC_DBG_SCANV)
77 {
78 if (missing && missing < 10)
79 {
80 FcCharSet *missed = FcCharSetSubtract (&fcLangCharSets[i].charset,
81 charset);
82 FcChar32 ucs4;
83 FcChar32 map[FC_CHARSET_MAP_SIZE];
84 FcChar32 next;
85
86 printf ("\n%s(%d) ", fcLangCharSets[i].lang, missing);
87 printf ("{");
88 for (ucs4 = FcCharSetFirstPage (missed, map, &next);
89 ucs4 != FC_CHARSET_DONE;
90 ucs4 = FcCharSetNextPage (missed, map, &next))
91 {
92 int i, j;
93 for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
94 if (map[i])
95 {
96 for (j = 0; j < 32; j++)
97 if (map[i] & (1 << j))
98 printf (" %04x", ucs4 + i * 32 + j);
99 }
100 }
101 printf (" }\n\t");
102 FcCharSetDestroy (missed);
103 }
104 else
105 printf ("%s(%d) ", fcLangCharSets[i].lang, missing);
106 }
107 if (!missing)
108 FcLangSetBitSet (ls, i);
109 }
110
111 if (FcDebug() & FC_DBG_SCANV)
112 printf ("\n");
113
114
115 return ls;
116 }
117
118 #define FcLangEnd(c) ((c) == '-' || (c) == '\0')
119
120 FcLangResult
121 FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
122 {
123 FcChar8 c1, c2;
124 FcLangResult result = FcLangDifferentLang;
125
126 for (;;)
127 {
128 c1 = *s1++;
129 c2 = *s2++;
130
131 c1 = FcToLower (c1);
132 c2 = FcToLower (c2);
133 if (c1 != c2)
134 {
135 if (FcLangEnd (c1) && FcLangEnd (c2))
136 result = FcLangDifferentCountry;
137 return result;
138 }
139 else if (!c1)
140 return FcLangEqual;
141 else if (c1 == '-')
142 result = FcLangDifferentCountry;
143 }
144 }
145
146 /*
147 * Return FcTrue when s1 contains s2.
148 *
149 * s1 contains s2 if s1 equals s2 or if s1 is a
150 * language with a country and s2 is just a language
151 */
152
153 static FcBool
154 FcLangContains (const FcChar8 *s1, const FcChar8 *s2)
155 {
156 FcChar8 c1, c2;
157
158 for (;;)
159 {
160 c1 = *s1++;
161 c2 = *s2++;
162
163 c1 = FcToLower (c1);
164 c2 = FcToLower (c2);
165 if (c1 != c2)
166 {
167 /* see if s1 has a country while s2 is mising one */
168 if (c1 == '-' && c2 == '\0')
169 return FcTrue;
170 return FcFalse;
171 }
172 else if (!c1)
173 return FcTrue;
174 }
175 }
176
177 const FcCharSet *
178 FcCharSetForLang (const FcChar8 *lang)
179 {
180 int i;
181 int country = -1;
182 for (i = 0; i < NUM_LANG_CHAR_SET; i++)
183 {
184 switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
185 case FcLangEqual:
186 return &fcLangCharSets[i].charset;
187 case FcLangDifferentCountry:
188 if (country == -1)
189 country = i;
190 default:
191 break;
192 }
193 }
194 if (country == -1)
195 return 0;
196 return &fcLangCharSets[i].charset;
197 }
198
199 FcLangSet *
200 FcLangSetCreate (void)
201 {
202 FcLangSet *ls;
203
204 ls = malloc (sizeof (FcLangSet));
205 if (!ls)
206 return 0;
207 FcMemAlloc (FC_MEM_LANGSET, sizeof (FcLangSet));
208 memset (ls->map, '\0', sizeof (ls->map));
209 ls->extra = 0;
210 return ls;
211 }
212
213 void
214 FcLangSetDestroy (FcLangSet *ls)
215 {
216 if (ls->extra)
217 FcStrSetDestroy (ls->extra);
218 FcMemFree (FC_MEM_LANGSET, sizeof (FcLangSet));
219 free (ls);
220 }
221
222 FcLangSet *
223 FcLangSetCopy (const FcLangSet *ls)
224 {
225 FcLangSet *new;
226
227 new = FcLangSetCreate ();
228 if (!new)
229 goto bail0;
230 memcpy (new->map, ls->map, sizeof (new->map));
231 if (ls->extra)
232 {
233 FcStrList *list;
234 FcChar8 *extra;
235
236 new->extra = FcStrSetCreate ();
237 if (!new->extra)
238 goto bail1;
239
240 list = FcStrListCreate (ls->extra);
241 if (!list)
242 goto bail1;
243
244 while ((extra = FcStrListNext (list)))
245 if (!FcStrSetAdd (new->extra, extra))
246 {
247 FcStrListDone (list);
248 goto bail1;
249 }
250 FcStrListDone (list);
251 }
252 return new;
253 bail1:
254 FcLangSetDestroy (new);
255 bail0:
256 return 0;
257 }
258
259 static int
260 FcLangSetIndex (const FcChar8 *lang)
261 {
262 int low, high, mid = 0;
263 int cmp = 0;
264 FcChar8 firstChar = FcToLower(lang[0]);
265
266 if (firstChar < 'a')
267 {
268 low = 0;
269 high = fcLangCharSetRanges[0].begin;
270 }
271 else if(firstChar > 'z')
272 {
273 low = fcLangCharSetRanges[25].begin;
274 high = NUM_LANG_CHAR_SET - 1;
275 }
276 else
277 {
278 low = fcLangCharSetRanges[firstChar - 'a'].begin;
279 high = fcLangCharSetRanges[firstChar - 'a'].end;
280 /* no matches */
281 if (low > high)
282 return -low; /* next entry after where it would be */
283 }
284
285 while (low <= high)
286 {
287 mid = (high + low) >> 1;
288 if(fcLangCharSets[mid].lang[0] != firstChar)
289 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang);
290 else
291 { /* fast path for resolving 2-letter languages (by far the most common) after
292 * finding the first char (probably already true because of the hash table) */
293 FcChar8 secondChar = FcToLower(lang[1]);
294 if (fcLangCharSets[mid].lang[1] > secondChar) /* check second chars */
295 {
296 high = mid - 1;
297 continue;
298 }
299 else if (fcLangCharSets[mid].lang[1] < secondChar)
300 {
301 low = mid + 1;
302 continue;
303 }
304 else if (fcLangCharSets[mid].lang[2] == '\0' && lang[2] == '\0')
305 return mid;
306
307 else /* identical through the first two charcters, but at least one string didn't end there */
308 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2, lang+2);
309 }
310 if (cmp == 0)
311 return mid;
312 if (cmp < 0)
313 low = mid + 1;
314 else
315 high = mid - 1;
316 }
317 if (cmp < 0)
318 mid++;
319 return -(mid + 1);
320 }
321
322 FcBool
323 FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
324 {
325 int id;
326
327 id = FcLangSetIndex (lang);
328 if (id >= 0)
329 {
330 FcLangSetBitSet (ls, id);
331 return FcTrue;
332 }
333 if (!ls->extra)
334 {
335 ls->extra = FcStrSetCreate ();
336 if (!ls->extra)
337 return FcFalse;
338 }
339 return FcStrSetAdd (ls->extra, lang);
340 }
341
342 FcLangResult
343 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
344 {
345 int id;
346 FcLangResult best, r;
347 int i;
348
349 id = FcLangSetIndex (lang);
350 if (id < 0)
351 id = -id - 1;
352 else if (FcLangSetBitGet (ls, id))
353 return FcLangEqual;
354 best = FcLangDifferentLang;
355 for (i = id - 1; i >= 0; i--)
356 {
357 r = FcLangCompare (lang, fcLangCharSets[i].lang);
358 if (r == FcLangDifferentLang)
359 break;
360 if (FcLangSetBitGet (ls, i) && r < best)
361 best = r;
362 }
363 for (i = id; i < NUM_LANG_CHAR_SET; i++)
364 {
365 r = FcLangCompare (lang, fcLangCharSets[i].lang);
366 if (r == FcLangDifferentLang)
367 break;
368 if (FcLangSetBitGet (ls, i) && r < best)
369 best = r;
370 }
371 if (ls->extra)
372 {
373 FcStrList *list = FcStrListCreate (ls->extra);
374 FcChar8 *extra;
375 FcLangResult r;
376
377 if (list)
378 {
379 while (best > FcLangEqual && (extra = FcStrListNext (list)))
380 {
381 r = FcLangCompare (lang, extra);
382 if (r < best)
383 best = r;
384 }
385 FcStrListDone (list);
386 }
387 }
388 return best;
389 }
390
391 static FcLangResult
392 FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
393 {
394 FcStrList *list = FcStrListCreate (set);
395 FcLangResult r, best = FcLangDifferentLang;
396 FcChar8 *extra;
397
398 if (list)
399 {
400 while (best > FcLangEqual && (extra = FcStrListNext (list)))
401 {
402 r = FcLangSetHasLang (ls, extra);
403 if (r < best)
404 best = r;
405 }
406 FcStrListDone (list);
407 }
408 return best;
409 }
410
411 FcLangResult
412 FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
413 {
414 int i, j;
415 FcLangResult best, r;
416
417 for (i = 0; i < NUM_LANG_SET_MAP; i++)
418 if (lsa->map[i] & lsb->map[i])
419 return FcLangEqual;
420 best = FcLangDifferentLang;
421 for (j = 0; j < NUM_COUNTRY_SET; j++)
422 for (i = 0; i < NUM_LANG_SET_MAP; i++)
423 if ((lsa->map[i] & fcLangCountrySets[j][i]) &&
424 (lsb->map[i] & fcLangCountrySets[j][i]))
425 {
426 best = FcLangDifferentCountry;
427 break;
428 }
429 if (lsa->extra)
430 {
431 r = FcLangSetCompareStrSet (lsb, lsa->extra);
432 if (r < best)
433 best = r;
434 }
435 if (best > FcLangEqual && lsb->extra)
436 {
437 r = FcLangSetCompareStrSet (lsa, lsb->extra);
438 if (r < best)
439 best = r;
440 }
441 return best;
442 }
443
444 /*
445 * Used in computing values -- mustn't allocate any storage
446 */
447 FcLangSet *
448 FcLangSetPromote (const FcChar8 *lang)
449 {
450 static FcLangSet ls;
451 static FcStrSet strs;
452 static FcChar8 *str;
453 int id;
454
455 memset (ls.map, '\0', sizeof (ls.map));
456 ls.extra = 0;
457 id = FcLangSetIndex (lang);
458 if (id > 0)
459 {
460 FcLangSetBitSet (&ls, id);
461 }
462 else
463 {
464 ls.extra = &strs;
465 strs.num = 1;
466 strs.size = 1;
467 strs.strs = &str;
468 strs.ref = 1;
469 str = (FcChar8 *) lang;
470 }
471 return &ls;
472 }
473
474 FcChar32
475 FcLangSetHash (const FcLangSet *ls)
476 {
477 FcChar32 h = 0;
478 int i;
479
480 for (i = 0; i < NUM_LANG_SET_MAP; i++)
481 h ^= ls->map[i];
482 if (ls->extra)
483 h ^= ls->extra->num;
484 return h;
485 }
486
487 FcLangSet *
488 FcNameParseLangSet (const FcChar8 *string)
489 {
490 FcChar8 lang[32],c;
491 int i;
492 FcLangSet *ls;
493
494 ls = FcLangSetCreate ();
495 if (!ls)
496 goto bail0;
497
498 for(;;)
499 {
500 for(i = 0; i < 31;i++)
501 {
502 c = *string++;
503 if(c == '\0' || c == '|')
504 break; /* end of this code */
505 lang[i] = c;
506 }
507 lang[i] = '\0';
508 if (!FcLangSetAdd (ls, lang))
509 goto bail1;
510 if(c == '\0')
511 break;
512 }
513 return ls;
514 bail1:
515 FcLangSetDestroy (ls);
516 bail0:
517 return 0;
518 }
519
520 FcBool
521 FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
522 {
523 int i, bit;
524 FcChar32 bits;
525 FcBool first = FcTrue;
526
527 for (i = 0; i < NUM_LANG_SET_MAP; i++)
528 {
529 if ((bits = ls->map[i]))
530 {
531 for (bit = 0; bit <= 31; bit++)
532 if (bits & (1 << bit))
533 {
534 int id = (i << 5) | bit;
535 if (!first)
536 if (!FcStrBufChar (buf, '|'))
537 return FcFalse;
538 if (!FcStrBufString (buf, fcLangCharSets[id].lang))
539 return FcFalse;
540 first = FcFalse;
541 }
542 }
543 }
544 if (ls->extra)
545 {
546 FcStrList *list = FcStrListCreate (ls->extra);
547 FcChar8 *extra;
548
549 if (!list)
550 return FcFalse;
551 while ((extra = FcStrListNext (list)))
552 {
553 if (!first)
554 if (!FcStrBufChar (buf, '|'))
555 return FcFalse;
556 if (!FcStrBufString (buf, extra))
557 return FcFalse;
558 first = FcFalse;
559 }
560 }
561 return FcTrue;
562 }
563
564 FcBool
565 FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
566 {
567 int i;
568
569 for (i = 0; i < NUM_LANG_SET_MAP; i++)
570 {
571 if (lsa->map[i] != lsb->map[i])
572 return FcFalse;
573 }
574 if (!lsa->extra && !lsb->extra)
575 return FcTrue;
576 if (lsa->extra && lsb->extra)
577 return FcStrSetEqual (lsa->extra, lsb->extra);
578 return FcFalse;
579 }
580
581 static FcBool
582 FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang)
583 {
584 int id;
585 int i;
586
587 id = FcLangSetIndex (lang);
588 if (id < 0)
589 id = -id - 1;
590 else if (FcLangSetBitGet (ls, id))
591 return FcTrue;
592 /*
593 * search up and down among equal languages for a match
594 */
595 for (i = id - 1; i >= 0; i--)
596 {
597 if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
598 break;
599 if (FcLangSetBitGet (ls, i) &&
600 FcLangContains (fcLangCharSets[i].lang, lang))
601 return FcTrue;
602 }
603 for (i = id; i < NUM_LANG_CHAR_SET; i++)
604 {
605 if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
606 break;
607 if (FcLangSetBitGet (ls, i) &&
608 FcLangContains (fcLangCharSets[i].lang, lang))
609 return FcTrue;
610 }
611 if (ls->extra)
612 {
613 FcStrList *list = FcStrListCreate (ls->extra);
614 FcChar8 *extra;
615
616 if (list)
617 {
618 while ((extra = FcStrListNext (list)))
619 {
620 if (FcLangContains (extra, lang))
621 break;
622 }
623 FcStrListDone (list);
624 if (extra)
625 return FcTrue;
626 }
627 }
628 return FcFalse;
629 }
630
631 /*
632 * return FcTrue if lsa contains every language in lsb
633 */
634 FcBool
635 FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb)
636 {
637 int i, j;
638 FcChar32 missing;
639
640 if (FcDebug() & FC_DBG_MATCHV)
641 {
642 printf ("FcLangSet "); FcLangSetPrint (lsa);
643 printf (" contains "); FcLangSetPrint (lsb);
644 printf ("\n");
645 }
646 /*
647 * check bitmaps for missing language support
648 */
649 for (i = 0; i < NUM_LANG_SET_MAP; i++)
650 {
651 missing = lsb->map[i] & ~lsa->map[i];
652 if (missing)
653 {
654 for (j = 0; j < 32; j++)
655 if (missing & (1 << j))
656 {
657 if (!FcLangSetContainsLang (lsa,
658 fcLangCharSets[i*32 + j].lang))
659 {
660 if (FcDebug() & FC_DBG_MATCHV)
661 printf ("\tMissing bitmap %s\n", fcLangCharSets[i*32+j].lang);
662 return FcFalse;
663 }
664 }
665 }
666 }
667 if (lsb->extra)
668 {
669 FcStrList *list = FcStrListCreate (lsb->extra);
670 FcChar8 *extra;
671
672 if (list)
673 {
674 while ((extra = FcStrListNext (list)))
675 {
676 if (!FcLangSetContainsLang (lsa, extra))
677 {
678 if (FcDebug() & FC_DBG_MATCHV)
679 printf ("\tMissing string %s\n", extra);
680 break;
681 }
682 }
683 FcStrListDone (list);
684 if (extra)
685 return FcFalse;
686 }
687 }
688 return FcTrue;
689 }