Coverage Report

Created: 2026-01-25 07:18

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