Coverage Report

Created: 2026-11-12 07:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wget/lib/c32rtomb.c
Line
Count
Source
1
/* Convert 32-bit wide character to multibyte character.
2
   Copyright (C) 2020-2026 Free Software Foundation, Inc.
3
4
   This file is free software: you can redistribute it and/or modify
5
   it under the terms of the GNU Lesser General Public License as
6
   published by the Free Software Foundation; either version 2.1 of the
7
   License, or (at your option) any later version.
8
9
   This file 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 Lesser General Public License for more details.
13
14
   You should have received a copy of the GNU Lesser General Public License
15
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16
17
/* Written by Bruno Haible <bruno@clisp.org>, 2020.  */
18
19
#include <config.h>
20
21
/* Specification.  */
22
#include <uchar.h>
23
24
#include <errno.h>
25
#include <wchar.h>
26
27
#include "attribute.h" /* FALLTHROUGH */
28
#include "localcharset.h"
29
#include "streq-opt.h"
30
31
#if GL_CHAR32_T_IS_UNICODE
32
# include "lc-charset-unicode.h"
33
#endif
34
35
#if (MBRTOC32_IN_C_LOCALE_MAYBE_LIKE_ISO_8859 \
36
     || MBRTOC32_IN_C_LOCALE_MAYBE_EILSEQ \
37
     || (HAVE_WORKING_MBRTOC32 && HAVE_WORKING_C32RTOMB && !_GL_WCHAR_T_IS_UCS4) \
38
     || _GL_SMALL_WCHAR_T \
39
     || (GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION))
40
# include "hard-locale.h"
41
# include <locale.h>
42
#endif
43
44
size_t
45
c32rtomb (char *s, char32_t wc, mbstate_t *ps)
46
#undef c32rtomb
47
0
{
48
0
#if !GNULIB_defined_mbstate_t && HAVE_WORKING_MBRTOC32 && HAVE_WORKING_C32RTOMB
49
50
# if C32RTOMB_RETVAL_BUG
51
  if (s == NULL)
52
    /* We know the NUL wide character corresponds to the NUL character.  */
53
    return 1;
54
# endif
55
56
0
# if MBRTOC32_IN_C_LOCALE_MAYBE_LIKE_ISO_8859 /* OpenBSD */ \
57
     || MBRTOC32_IN_C_LOCALE_MAYBE_EILSEQ /* glibc */ \
58
     || !_GL_WCHAR_T_IS_UCS4 /* NetBSD ≥ 11 */
59
0
  if ((wc >= 0xDF80 && wc <= 0xDFFF) && !hard_locale (LC_CTYPE))
60
0
    {
61
      /* In the "C" locale, map the code points U+DF80..U+DFFF back to the bytes
62
         0x80..0xFF, for consistency with the mbrtoc32 and btoc32 functions.  */
63
0
      s[0] = (unsigned char) (wc - 0xDF00);
64
0
      return 1;
65
0
    }
66
0
# endif
67
68
0
  return c32rtomb (s, wc, ps);
69
70
#elif !GNULIB_defined_mbstate_t && _GL_SMALL_WCHAR_T /* Cygwin, mingw, MSVC */
71
72
  if (s == NULL)
73
    return wcrtomb (NULL, 0, ps);
74
  else
75
    {
76
      /* Special-case all encodings that may produce wide character values
77
         > WCHAR_MAX.  */
78
      const char *encoding = locale_charset ();
79
      if (STREQ_OPT (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0))
80
        {
81
          /* Special-case the UTF-8 encoding.  Assume that the wide-character
82
             encoding in a UTF-8 locale is UCS-2 or, equivalently, UTF-16.  */
83
          if (wc < 0x80)
84
            {
85
              s[0] = (unsigned char) wc;
86
              return 1;
87
            }
88
          else
89
            {
90
              int count;
91
92
              if (wc < 0x800)
93
                count = 2;
94
              else if (wc < 0x10000)
95
                {
96
                  if (wc < 0xd800 || wc >= 0xe000)
97
                    count = 3;
98
                  else
99
                    {
100
                      errno = EILSEQ;
101
                      return (size_t)(-1);
102
                    }
103
                }
104
              else if (wc < 0x110000)
105
                count = 4;
106
              else
107
                {
108
                  errno = EILSEQ;
109
                  return (size_t)(-1);
110
                }
111
112
              switch (count) /* note: code falls through cases! */
113
                {
114
                case 4: s[3] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x10000;
115
                  FALLTHROUGH;
116
                case 3: s[2] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x800;
117
                  FALLTHROUGH;
118
                case 2: s[1] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0xc0;
119
              /*case 1:*/ s[0] = wc;
120
                }
121
              return count;
122
            }
123
        }
124
      else if (!hard_locale (LC_CTYPE))
125
        {
126
          /* In the "C" locale, map the code points U+DF80..U+DFFF back
127
             to the bytes 0x80..0xFF, for consistency with the mbrtoc32 and
128
             btoc32 functions.  */
129
          if (wc >= 0x00 && wc <= 0x7F)
130
            {
131
              s[0] = (unsigned char) wc;
132
              return 1;
133
            }
134
          else if (wc >= 0xDF80 && wc <= 0xDFFF)
135
            {
136
              s[0] = (unsigned char) (wc - 0xDF00);
137
              return 1;
138
            }
139
          else
140
            {
141
              errno = EILSEQ;
142
              return (size_t)(-1);
143
            }
144
        }
145
      else
146
        {
147
          if ((wchar_t) wc == wc)
148
            return wcrtomb (s, (wchar_t) wc, ps);
149
          else
150
            {
151
              errno = EILSEQ;
152
              return (size_t)(-1);
153
            }
154
        }
155
    }
156
157
#else
158
159
  /* char32_t and wchar_t are equivalent.  */
160
# if GL_CHAR32_T_IS_UNICODE && GL_CHAR32_T_VS_WCHAR_T_NEEDS_CONVERSION
161
  if ((wc >= 0xDF80 && wc <= 0xDFFF) && !hard_locale (LC_CTYPE))
162
    {
163
      /* In the "C" locale, map the code points U+DF80..U+DFFF back to the bytes
164
         0x80..0xFF, for consistency with the mbrtoc32 and btoc32 functions.  */
165
      s[0] = (unsigned char) (wc - 0xDF00);
166
      return 1;
167
    }
168
  if (wc != 0)
169
    {
170
      wc = unicode_to_locale_encoding (wc);
171
      if (wc == 0)
172
        {
173
          errno = EILSEQ;
174
          return (size_t)(-1);
175
        }
176
    }
177
# endif
178
  return wcrtomb (s, (wchar_t) wc, ps);
179
180
#endif
181
0
}