Coverage Report

Created: 2026-08-17 07:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gettext/gettext-tools/src/po-charset.c
Line
Count
Source
1
/* Charset handling while reading PO files.
2
   Copyright (C) 2001-2026 Free Software Foundation, Inc.
3
4
   This program is free software: you can redistribute it and/or modify
5
   it under the terms of the GNU General Public License as published by
6
   the Free Software Foundation; either version 3 of the License, or
7
   (at your option) any later version.
8
9
   This program is distributed in the hope that it will be useful,
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
   GNU General Public License for more details.
13
14
   You should have received a copy of the GNU General Public License
15
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16
17
/* Written by Bruno Haible.  */
18
19
20
#include <config.h>
21
#include <alloca.h>
22
23
/* Specification.  */
24
#include "po-charset.h"
25
26
#include <stdcountof.h>
27
#include <string.h>
28
29
#include "c-strcase.h"
30
#include "gettext.h"
31
32
#define _(str) gettext (str)
33
34
static const char ascii[] = "ASCII";
35
36
/* The canonicalized encoding name for ASCII.  */
37
const char *po_charset_ascii = ascii;
38
39
static const char utf8[] = "UTF-8";
40
41
/* The canonicalized encoding name for UTF-8.  */
42
const char *po_charset_utf8 = utf8;
43
44
/* Canonicalize an encoding name.  */
45
const char *
46
po_charset_canonicalize (const char *charset)
47
15.8k
{
48
  /* The list of charsets supported by glibc's iconv() and by the portable
49
     iconv() across platforms.  Taken from intl/localcharset.h.  */
50
15.8k
  static const char *standard_charsets[] =
51
15.8k
  {
52
15.8k
    ascii, "ANSI_X3.4-1968", "US-ASCII",        /* i = 0..2 */
53
15.8k
    "ISO-8859-1", "ISO_8859-1",                 /* i = 3, 4 */
54
15.8k
    "ISO-8859-2", "ISO_8859-2",
55
15.8k
    "ISO-8859-3", "ISO_8859-3",
56
15.8k
    "ISO-8859-4", "ISO_8859-4",
57
15.8k
    "ISO-8859-5", "ISO_8859-5",
58
15.8k
    "ISO-8859-6", "ISO_8859-6",
59
15.8k
    "ISO-8859-7", "ISO_8859-7",
60
15.8k
    "ISO-8859-8", "ISO_8859-8",
61
15.8k
    "ISO-8859-9", "ISO_8859-9",
62
15.8k
    "ISO-8859-13", "ISO_8859-13",
63
15.8k
    "ISO-8859-14", "ISO_8859-14",
64
15.8k
    "ISO-8859-15", "ISO_8859-15",               /* i = 25, 26 */
65
15.8k
    "KOI8-R",
66
15.8k
    "KOI8-U",
67
15.8k
    "KOI8-T",
68
15.8k
    "CP850",
69
15.8k
    "CP866",
70
15.8k
    "CP874",
71
15.8k
    "CP932",
72
15.8k
    "CP949",
73
15.8k
    "CP950",
74
15.8k
    "CP1250",
75
15.8k
    "CP1251",
76
15.8k
    "CP1252",
77
15.8k
    "CP1253",
78
15.8k
    "CP1254",
79
15.8k
    "CP1255",
80
15.8k
    "CP1256",
81
15.8k
    "CP1257",
82
15.8k
    "GB2312",
83
15.8k
    "EUC-JP",
84
15.8k
    "EUC-KR",
85
15.8k
    "EUC-TW",
86
15.8k
    "BIG5",
87
15.8k
    "BIG5-HKSCS",
88
15.8k
    "GBK",
89
15.8k
    "GB18030",
90
15.8k
    "SHIFT_JIS",
91
15.8k
    "JOHAB",
92
15.8k
    "TIS-620",
93
15.8k
    "VISCII",
94
15.8k
    "GEORGIAN-PS",
95
15.8k
    utf8
96
15.8k
  };
97
98
894k
  for (size_t i = 0; i < countof (standard_charsets); i++)
99
884k
    if (c_strcasecmp (charset, standard_charsets[i]) == 0)
100
5.72k
      return standard_charsets[i < 3 ? 0 : i < 27 ? ((i - 3) & ~1) + 3 : i];
101
10.1k
  return NULL;
102
15.8k
}
103
104
/* Test for ASCII compatibility.  */
105
bool
106
po_charset_ascii_compatible (const char *canon_charset)
107
0
{
108
  /* There are only a few exceptions to ASCII compatibility.  */
109
0
  if (streq (canon_charset, "SHIFT_JIS")
110
0
      || streq (canon_charset, "JOHAB")
111
0
      || streq (canon_charset, "VISCII"))
112
0
    return false;
113
0
  else
114
0
    return true;
115
0
}
116
117
/* Test for a weird encoding, i.e. an encoding which has double-byte
118
   characters ending in 0x5C.  */
119
bool po_is_charset_weird (const char *canon_charset)
120
0
{
121
0
  static const char *weird_charsets[] =
122
0
  {
123
0
    "BIG5",
124
0
    "BIG5-HKSCS",
125
0
    "GBK",
126
0
    "GB18030",
127
0
    "SHIFT_JIS",
128
0
    "JOHAB"
129
0
  };
130
131
0
  for (size_t i = 0; i < countof (weird_charsets); i++)
132
0
    if (streq (canon_charset, weird_charsets[i]))
133
0
      return true;
134
0
  return false;
135
0
}
136
137
/* Test for a weird CJK encoding, i.e. a weird encoding with CJK structure.
138
   An encoding has CJK structure if every valid character stream is composed
139
   of single bytes in the range 0x{00..7F} and of byte pairs in the range
140
   0x{80..FF}{30..FF}.  */
141
bool po_is_charset_weird_cjk (const char *canon_charset)
142
0
{
143
0
  static const char *weird_cjk_charsets[] =
144
0
  {                     /* single bytes   double bytes       */
145
0
    "BIG5",             /* 0x{00..7F},    0x{A1..F9}{40..FE} */
146
0
    "BIG5-HKSCS",       /* 0x{00..7F},    0x{88..FE}{40..FE} */
147
0
    "GBK",              /* 0x{00..7F},    0x{81..FE}{40..FE} */
148
0
    "GB18030",          /* 0x{00..7F},    0x{81..FE}{30..FE} */
149
0
    "SHIFT_JIS",        /* 0x{00..7F},    0x{81..F9}{40..FC} */
150
0
    "JOHAB"             /* 0x{00..7F},    0x{84..F9}{31..FE} */
151
0
  };
152
153
0
  for (size_t i = 0; i < countof (weird_cjk_charsets); i++)
154
0
    if (streq (canon_charset, weird_cjk_charsets[i]))
155
0
      return true;
156
0
  return false;
157
0
}
158
159
/* Hardcoded iterator functions for all kinds of encodings.
160
   We could also implement a general iterator function with iconv(),
161
   but we need a fast one.  */
162
163
/* Character iterator for 8-bit encodings.  */
164
static size_t
165
char_iterator (const char *s)
166
0
{
167
0
  return 1;
168
0
}
169
170
/* Character iterator for GB2312.  See libiconv/lib/euc_cn.h.  */
171
/* Character iterator for EUC-KR.  See libiconv/lib/euc_kr.h.  */
172
static size_t
173
euc_character_iterator (const char *s)
174
0
{
175
0
  unsigned char c = *s;
176
0
  if (c >= 0xa1 && c < 0xff)
177
0
    {
178
0
      unsigned char c2 = s[1];
179
0
      if (c2 >= 0xa1 && c2 < 0xff)
180
0
        return 2;
181
0
    }
182
0
  return 1;
183
0
}
184
185
/* Character iterator for EUC-JP.  See libiconv/lib/euc_jp.h.  */
186
static size_t
187
euc_jp_character_iterator (const char *s)
188
0
{
189
0
  unsigned char c = *s;
190
0
  if (c >= 0xa1 && c < 0xff)
191
0
    {
192
0
      unsigned char c2 = s[1];
193
0
      if (c2 >= 0xa1 && c2 < 0xff)
194
0
        return 2;
195
0
    }
196
0
  else if (c == 0x8e)
197
0
    {
198
0
      unsigned char c2 = s[1];
199
0
      if (c2 >= 0xa1 && c2 < 0xe0)
200
0
        return 2;
201
0
    }
202
0
  else if (c == 0x8f)
203
0
    {
204
0
      unsigned char c2 = s[1];
205
0
      if (c2 >= 0xa1 && c2 < 0xff)
206
0
        {
207
0
          unsigned char c3 = s[2];
208
0
          if (c3 >= 0xa1 && c3 < 0xff)
209
0
            return 3;
210
0
        }
211
0
    }
212
0
  return 1;
213
0
}
214
215
/* Character iterator for EUC-TW.  See libiconv/lib/euc_tw.h.  */
216
static size_t
217
euc_tw_character_iterator (const char *s)
218
0
{
219
0
  unsigned char c = *s;
220
0
  if (c >= 0xa1 && c < 0xff)
221
0
    {
222
0
      unsigned char c2 = s[1];
223
0
      if (c2 >= 0xa1 && c2 < 0xff)
224
0
        return 2;
225
0
    }
226
0
  else if (c == 0x8e)
227
0
    {
228
0
      unsigned char c2 = s[1];
229
0
      if (c2 >= 0xa1 && c2 <= 0xb0)
230
0
        {
231
0
          unsigned char c3 = s[2];
232
0
          if (c3 >= 0xa1 && c3 < 0xff)
233
0
            {
234
0
              unsigned char c4 = s[3];
235
0
              if (c4 >= 0xa1 && c4 < 0xff)
236
0
                return 4;
237
0
            }
238
0
        }
239
0
    }
240
0
  return 1;
241
0
}
242
243
/* Character iterator for BIG5.  See libiconv/lib/ces_big5.h.  */
244
static size_t
245
big5_character_iterator (const char *s)
246
0
{
247
0
  unsigned char c = *s;
248
0
  if (c >= 0xa1 && c < 0xff)
249
0
    {
250
0
      unsigned char c2 = s[1];
251
0
      if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0xa1 && c2 < 0xff))
252
0
        return 2;
253
0
    }
254
0
  return 1;
255
0
}
256
257
/* Character iterator for BIG5-HKSCS.  See libiconv/lib/big5hkscs.h.  */
258
static size_t
259
big5hkscs_character_iterator (const char *s)
260
0
{
261
0
  unsigned char c = *s;
262
0
  if (c >= 0x88 && c < 0xff)
263
0
    {
264
0
      unsigned char c2 = s[1];
265
0
      if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0xa1 && c2 < 0xff))
266
0
        return 2;
267
0
    }
268
0
  return 1;
269
0
}
270
271
/* Character iterator for GBK.  See libiconv/lib/ces_gbk.h and
272
   libiconv/lib/gbk.h.  */
273
static size_t
274
gbk_character_iterator (const char *s)
275
0
{
276
0
  unsigned char c = *s;
277
0
  if (c >= 0x81 && c < 0xff)
278
0
    {
279
0
      unsigned char c2 = s[1];
280
0
      if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0x80 && c2 < 0xff))
281
0
        return 2;
282
0
    }
283
0
  return 1;
284
0
}
285
286
/* Character iterator for GB18030.  See libiconv/lib/gb18030.h.  */
287
static size_t
288
gb18030_character_iterator (const char *s)
289
0
{
290
0
  unsigned char c = *s;
291
0
  if (c >= 0x81 && c < 0xff)
292
0
    {
293
0
      unsigned char c2 = s[1];
294
0
      if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0x80 && c2 < 0xff))
295
0
        return 2;
296
0
    }
297
0
  if (c >= 0x81 && c <= 0x84)
298
0
    {
299
0
      unsigned char c2 = s[1];
300
0
      if (c2 >= 0x30 && c2 <= 0x39)
301
0
        {
302
0
          unsigned char c3 = s[2];
303
0
          if (c3 >= 0x81 && c3 < 0xff)
304
0
            {
305
0
              unsigned char c4 = s[3];
306
0
              if (c4 >= 0x30 && c4 <= 0x39)
307
0
                return 4;
308
0
            }
309
0
        }
310
0
    }
311
0
  return 1;
312
0
}
313
314
/* Character iterator for SHIFT_JIS.  See libiconv/lib/sjis.h.  */
315
static size_t
316
shift_jis_character_iterator (const char *s)
317
0
{
318
0
  unsigned char c = *s;
319
0
  if ((c >= 0x81 && c <= 0x9f) || (c >= 0xe0 && c <= 0xf9))
320
0
    {
321
0
      unsigned char c2 = s[1];
322
0
      if ((c2 >= 0x40 && c2 <= 0x7e) || (c2 >= 0x80 && c2 <= 0xfc))
323
0
        return 2;
324
0
    }
325
0
  return 1;
326
0
}
327
328
/* Character iterator for JOHAB.  See libiconv/lib/johab.h and
329
   libiconv/lib/johab_hangul.h.  */
330
static size_t
331
johab_character_iterator (const char *s)
332
0
{
333
0
  unsigned char c = *s;
334
0
  if (c >= 0x84 && c <= 0xd3)
335
0
    {
336
0
      unsigned char c2 = s[1];
337
0
      if ((c2 >= 0x41 && c2 < 0x7f) || (c2 >= 0x81 && c2 < 0xff))
338
0
        return 2;
339
0
    }
340
0
  else if (c >= 0xd9 && c <= 0xf9)
341
0
    {
342
0
      unsigned char c2 = s[1];
343
0
      if ((c2 >= 0x31 && c2 <= 0x7e) || (c2 >= 0x91 && c2 <= 0xfe))
344
0
        return 2;
345
0
    }
346
0
  return 1;
347
0
}
348
349
/* Character iterator for UTF-8.  See libiconv/lib/utf8.h.  */
350
static size_t
351
utf8_character_iterator (const char *s)
352
0
{
353
0
  unsigned char c = *s;
354
0
  if (c >= 0xc2)
355
0
    {
356
0
      if (c < 0xe0)
357
0
        {
358
0
          unsigned char c2 = s[1];
359
0
          if (c2 >= 0x80 && c2 < 0xc0)
360
0
            return 2;
361
0
        }
362
0
      else if (c < 0xf0)
363
0
        {
364
0
          unsigned char c2 = s[1];
365
0
          if (c2 >= 0x80 && c2 < 0xc0)
366
0
            {
367
0
              unsigned char c3 = s[2];
368
0
              if (c3 >= 0x80 && c3 < 0xc0)
369
0
                return 3;
370
0
            }
371
0
        }
372
0
      else if (c < 0xf8)
373
0
        {
374
0
          unsigned char c2 = s[1];
375
0
          if (c2 >= 0x80 && c2 < 0xc0)
376
0
            {
377
0
              unsigned char c3 = s[2];
378
0
              if (c3 >= 0x80 && c3 < 0xc0)
379
0
                {
380
0
                  unsigned char c4 = s[3];
381
0
                  if (c4 >= 0x80 && c4 < 0xc0)
382
0
                    return 4;
383
0
                }
384
0
            }
385
0
        }
386
0
    }
387
0
  return 1;
388
0
}
389
390
/* Returns a character iterator for a given encoding.
391
   Given a pointer into a string, it returns the number occupied by the next
392
   single character.  If the piece of string is not valid or if the *s == '\0',
393
   it returns 1.  */
394
character_iterator_t
395
po_charset_character_iterator (const char *canon_charset)
396
0
{
397
0
  if (canon_charset == utf8)
398
0
    return utf8_character_iterator;
399
0
  if (streq (canon_charset, "GB2312")
400
0
      || streq (canon_charset, "EUC-KR"))
401
0
    return euc_character_iterator;
402
0
  if (streq (canon_charset, "EUC-JP"))
403
0
    return euc_jp_character_iterator;
404
0
  if (streq (canon_charset, "EUC-TW"))
405
0
    return euc_tw_character_iterator;
406
0
  if (streq (canon_charset, "BIG5"))
407
0
    return big5_character_iterator;
408
0
  if (streq (canon_charset, "BIG5-HKSCS"))
409
0
    return big5hkscs_character_iterator;
410
0
  if (streq (canon_charset, "GBK"))
411
0
    return gbk_character_iterator;
412
0
  if (streq (canon_charset, "GB18030"))
413
0
    return gb18030_character_iterator;
414
0
  if (streq (canon_charset, "SHIFT_JIS"))
415
0
    return shift_jis_character_iterator;
416
0
  if (streq (canon_charset, "JOHAB"))
417
0
    return johab_character_iterator;
418
0
  return char_iterator;
419
0
}