Coverage Report

Created: 2026-08-14 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wget/lib/mbrtowc.c
Line
Count
Source
1
/* Convert multibyte character to wide character.
2
   Copyright (C) 1999-2002, 2005-2026 Free Software Foundation, Inc.
3
   Written by Bruno Haible <bruno@clisp.org>, 2008.
4
5
   This file is free software: you can redistribute it and/or modify
6
   it under the terms of the GNU Lesser General Public License as
7
   published by the Free Software Foundation; either version 2.1 of the
8
   License, or (at your option) any later version.
9
10
   This file is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
   GNU Lesser General Public License for more details.
14
15
   You should have received a copy of the GNU Lesser General Public License
16
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
17
18
#include <config.h>
19
20
/* Specification.  */
21
#include <wchar.h>
22
23
#if GNULIB_defined_mbstate_t
24
/* Implement mbrtowc() on top of mbtowc() for the non-UTF-8 locales
25
   and directly for the UTF-8 locales.  */
26
27
# include <errno.h>
28
# include <stdint.h>
29
# include <stdlib.h>
30
31
# if AVOID_ANY_THREADS
32
33
/* The option '--disable-threads' explicitly requests no locking.  */
34
35
# elif defined _WIN32 && !defined __CYGWIN__
36
37
#  define WIN32_LEAN_AND_MEAN  /* avoid including junk */
38
#  include <windows.h>
39
40
# elif HAVE_PTHREAD_API
41
42
#  include <pthread.h>
43
#  if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS
44
#   include <threads.h>
45
#   pragma weak thrd_exit
46
#   define c11_threads_in_use() (thrd_exit != NULL)
47
#  else
48
#   define c11_threads_in_use() 0
49
#  endif
50
51
# elif HAVE_THREADS_H
52
53
#  include <threads.h>
54
55
# endif
56
57
# include "attribute.h"
58
# include "lc-charset-dispatch.h"
59
# include "mbtowc-lock.h"
60
61
static_assert (sizeof (mbstate_t) >= 4);
62
static char internal_state[4];
63
64
size_t
65
mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
66
{
67
# define FITS_IN_CHAR_TYPE(wc)  ((wc) <= WCHAR_MAX)
68
# include "mbrtowc-impl.h"
69
}
70
71
#else
72
/* Override the system's mbrtowc() function.  */
73
74
# include <errno.h>
75
# include <stdlib.h>
76
77
# include "attribute.h"
78
# include "localcharset.h"
79
# include "streq-opt.h"
80
81
# if MBRTOWC_IN_C_LOCALE_MAYBE_EILSEQ
82
#  include "hard-locale.h"
83
#  include <locale.h>
84
# endif
85
86
# if MBRTOWC_INVALID_UTF8_BUG || (GNULIB_WCHAR_SINGLE_LOCALE && __GLIBC__ >= 2 && !__UCLIBC__)
87
88
/* Returns 1 if the current locale is an UTF-8 locale, 0 otherwise.  */
89
static inline int
90
is_locale_utf8 (void)
91
{
92
  const char *encoding = locale_charset ();
93
  return STREQ_OPT (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0);
94
}
95
96
/* Provide a speedup by caching the value of is_locale_utf8.  */
97
static int cached_is_locale_utf8 = -1;
98
static inline int
99
is_locale_utf8_cached (void)
100
{
101
  if (cached_is_locale_utf8 < 0)
102
    cached_is_locale_utf8 = is_locale_utf8 ();
103
  return cached_is_locale_utf8;
104
}
105
106
# endif
107
108
# undef mbrtowc
109
110
size_t
111
rpl_mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
112
0
{
113
  /* It's simpler to handle the case s == NULL upfront, than to worry about
114
     this case later, before every test of pwc and n.  */
115
0
  if (s == NULL)
116
0
    {
117
0
      pwc = NULL;
118
0
      s = "";
119
0
      n = 1;
120
0
    }
121
122
# if (MBRTOWC_EMPTY_INPUT_BUG || MBRTOWC_INVALID_UTF8_BUG \
123
      || (GNULIB_WCHAR_SINGLE_LOCALE && __GLIBC__ >= 2 && !__UCLIBC__))
124
  if (n == 0)
125
    return (size_t) -2;
126
# endif
127
128
# if MBRTOWC_INVALID_UTF8_BUG || (GNULIB_WCHAR_SINGLE_LOCALE && __GLIBC__ >= 2 && !__UCLIBC__)
129
  /* Optimize the frequent case of an UTF-8 locale.
130
     Since here we are in the !GNULIB_defined_mbstate_t case, i.e. we use
131
     the system's mbstate_t type and have to provide interoperability with
132
     the system's mbsinit() function, this requires knowledge about how the
133
     system's UTF-8 mbrtowc() function stores the state.  This knowledge is
134
     platform-specific.  For simplicity, we handle only glibc and NetBSD
135
     systems.  */
136
  if (is_locale_utf8_cached ())
137
    {
138
      static mbstate_t internal_state;
139
      if (ps == NULL)
140
        ps = &internal_state;
141
      #if __GLIBC__ >= 2
142
      /* Structure of mbstate_t =
143
         { int __count; union { wint_t __wch; char __wchb[4]; } __value; }
144
         (see glibc/iconv/gconv_simple.c function utf8_internal_loop):
145
         Bits 2..0 of __count is the number of input bytes already consumed.
146
         Bits 31..8 of __count is the number of input bytes expected for the
147
         entire byte sequence.
148
         __value.__wch is the already inferrable bits of the character, of
149
         the form (x << (r*6)) when r bytes are still expected.  */
150
      #endif
151
      #ifdef __NetBSD__
152
      /* Structure of mbstate_t =
153
         union { int64_t __mbstateL; char __mbstate8[128]; }
154
         (see src/lib/libc/citrus/modules/citrus_utf8.c):
155
         { void *header; char ch[6]; int chlen; },
156
         i.e. ch[0..5] is __mbstate8[sizeof(void*)+0..sizeof(void*)+5],
157
              chlen is __mbstate8[sizeof(void*)+8..sizeof(void*)+11].  */
158
      #endif
159
160
      /* Here n > 0.  */
161
162
      size_t nstate;
163
      #if __GLIBC__ >= 2
164
      nstate = ps->__count & 7;
165
      #endif
166
      #ifdef __NetBSD__
167
      nstate = *(int *) &ps->__mbstate8[sizeof (void *) + 8];
168
      #endif
169
      char buf[4];
170
      const char *p;
171
      size_t m;
172
173
      if (nstate == 0)
174
        {
175
          p = s;
176
          m = n;
177
        }
178
      else
179
        {
180
          #if __GLIBC__ >= 2
181
          size_t t = ps->__count >> 8; /* total expected number of bytes */
182
          if (t > nstate && t <= 4)
183
            {
184
              buf[0] =
185
                (0x100 - (0x100 >> t)) | (ps->__value.__wch >> ((t - 1) * 6));
186
              if (nstate >= 2)
187
                {
188
                  buf[1] =
189
                    0x80 | ((ps->__value.__wch >> ((t - 2) * 6)) & 0x3F);
190
                  if (nstate >= 3)
191
                    {
192
                      buf[2] =
193
                        0x80 | ((ps->__value.__wch >> ((t - 3) * 6)) & 0x3F);
194
                    }
195
                }
196
            }
197
          else
198
            {
199
              errno = EINVAL;
200
              return (size_t)(-1);
201
            }
202
          #endif
203
          #ifdef __NetBSD__
204
          buf[0] = ps->__mbstate8[sizeof (void *) + 0];
205
          if (nstate >= 2)
206
            {
207
              buf[1] = ps->__mbstate8[sizeof (void *) + 1];
208
              if (nstate >= 3)
209
                {
210
                  buf[2] = ps->__mbstate8[sizeof (void *) + 2];
211
                }
212
            }
213
          #endif
214
          p = buf;
215
          m = nstate;
216
          buf[m++] = s[0];
217
          if (n >= 2 && m < 4)
218
            {
219
              buf[m++] = s[1];
220
              if (n >= 3 && m < 4)
221
                buf[m++] = s[2];
222
            }
223
        }
224
225
      /* Here m > 0.  */
226
227
      int res;
228
      {
229
#  define FITS_IN_CHAR_TYPE(wc)  ((wc) <= WCHAR_MAX)
230
#  include "mbrtowc-impl-utf8.h"
231
      }
232
233
     success:
234
      /* res >= 0 is the corrected return value of
235
         mbtowc_with_lock (&wc, p, m).  */
236
      if (nstate >= (res > 0 ? res : 1))
237
        abort ();
238
      res -= nstate;
239
      #if __GLIBC__ >= 2
240
      ps->__count = 0;
241
      #endif
242
      #ifdef __NetBSD__
243
      *(int *) &ps->__mbstate8[sizeof (void *) + 8] = 0;
244
      #endif
245
      return res;
246
247
     incomplete:
248
      /* Here 0 < m < 4.  */
249
      {
250
        #if __GLIBC__ >= 2
251
        unsigned char c = (unsigned char) p[0];
252
        if (c < 0xE0)
253
          {
254
            ps->__count = (2 << 8) | m;
255
            ps->__value.__wch = (c & 0x1F) << 6;
256
          }
257
        else if (c < 0xF0)
258
          {
259
            ps->__count = (3 << 8) | m;
260
            ps->__value.__wch =
261
              ((c & 0x0F) << 12)
262
              | (m > 1 ? ((unsigned char) p[1] & 0x3F) << 6 : 0);
263
          }
264
        else
265
          {
266
            ps->__count = (4 << 8) | m;
267
            ps->__value.__wch =
268
              ((c & 0x07) << 18)
269
              | (m > 1 ? ((unsigned char) p[1] & 0x3F) << 12 : 0)
270
              | (m > 2 ? ((unsigned char) p[2] & 0x3F) << 6 : 0);
271
          }
272
        #endif
273
        #ifdef __NetBSD__
274
        *(int *) &ps->__mbstate8[sizeof (void *) + 8] = m;
275
        ps->__mbstate8[sizeof (void *) + 0] = p[0];
276
        if (m > 1)
277
          {
278
            ps->__mbstate8[sizeof (void *) + 1] = p[1];
279
            if (m > 2)
280
              {
281
                ps->__mbstate8[sizeof (void *) + 2] = p[2];
282
              }
283
          }
284
        #endif
285
      }
286
      return (size_t)(-2);
287
288
     invalid:
289
      errno = EILSEQ;
290
      /* The conversion state is undefined, says POSIX.  */
291
      return (size_t)(-1);
292
    }
293
# endif
294
295
0
  wchar_t wc;
296
0
  if (! pwc)
297
0
    pwc = &wc;
298
299
# if MBRTOWC_RETVAL_BUG
300
  {
301
    static mbstate_t internal_state;
302
303
    /* Override mbrtowc's internal state.  We cannot call mbsinit() on the
304
       hidden internal state, but we can call it on our variable.  */
305
    if (ps == NULL)
306
      ps = &internal_state;
307
308
    if (!mbsinit (ps))
309
      {
310
        /* Parse the rest of the multibyte character byte for byte.  */
311
        size_t count = 0;
312
        for (; n > 0; s++, n--)
313
          {
314
            size_t ret = mbrtowc (&wc, s, 1, ps);
315
316
            if (ret == (size_t)(-1))
317
              return (size_t)(-1);
318
            count++;
319
            if (ret != (size_t)(-2))
320
              {
321
                /* The multibyte character has been completed.  */
322
                *pwc = wc;
323
                return (wc == 0 ? 0 : count);
324
              }
325
          }
326
        return (size_t)(-2);
327
      }
328
  }
329
# endif
330
331
0
  size_t ret;
332
# if MBRTOWC_STORES_INCOMPLETE_BUG
333
  ret = mbrtowc (&wc, s, n, ps);
334
  if (ret < (size_t) -2 && pwc != NULL)
335
    *pwc = wc;
336
# else
337
0
  ret = mbrtowc (pwc, s, n, ps);
338
0
# endif
339
340
# if MBRTOWC_NUL_RETVAL_BUG
341
  if (ret < (size_t) -2 && !*pwc)
342
    return 0;
343
# endif
344
345
0
# if MBRTOWC_IN_C_LOCALE_MAYBE_EILSEQ
346
0
  if ((size_t) -2 <= ret && n != 0 && ! hard_locale (LC_CTYPE))
347
0
    {
348
0
      unsigned char uc = *s;
349
0
      *pwc = uc;
350
0
      return 1;
351
0
    }
352
0
# endif
353
354
0
  return ret;
355
0
}
356
357
#endif