1 /* Copyright (C) 1996-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library 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 GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <assert.h>
19 #include <dlfcn.h>
20 #include <errno.h>
21 #include <gconv.h>
22 #include <wchar.h>
23 #include <wcsmbsload.h>
24 
25 #include <sysdep.h>
26 
27 #ifndef EILSEQ
28 # define EILSEQ EINVAL
29 #endif
30 
31 /* This is the private state used if PS is NULL.  */
32 static mbstate_t state;
33 
34 size_t
__mbrtowc(wchar_t * pwc,const char * s,size_t n,mbstate_t * ps)35 __mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
36 {
37   wchar_t buf[1];
38   struct __gconv_step_data data;
39   int status;
40   size_t result;
41   size_t dummy;
42   const unsigned char *inbuf, *endbuf;
43   unsigned char *outbuf = (unsigned char *) (pwc ?: buf);
44   const struct gconv_fcts *fcts;
45 
46   /* Set information for this step.  */
47   data.__invocation_counter = 0;
48   data.__internal_use = 1;
49   data.__flags = __GCONV_IS_LAST;
50   data.__statep = ps ?: &state;
51 
52   /* A first special case is if S is NULL.  This means put PS in the
53      initial state.  */
54   if (s == NULL)
55     {
56       outbuf = (unsigned char *) buf;
57       s = "";
58       n = 1;
59     }
60 
61   if (n == 0)
62     return (size_t) -2;
63 
64   /* Tell where we want the result.  */
65   data.__outbuf = outbuf;
66   data.__outbufend = outbuf + sizeof (wchar_t);
67 
68   /* Get the conversion functions.  */
69   fcts = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE));
70 
71   /* Do a normal conversion.  */
72   inbuf = (const unsigned char *) s;
73   endbuf = inbuf + n;
74   if (__glibc_unlikely (endbuf < inbuf))
75     {
76       endbuf = (const unsigned char *) ~(uintptr_t) 0;
77       if (endbuf == inbuf)
78 	goto ilseq;
79     }
80   __gconv_fct fct = fcts->towc->__fct;
81 #ifdef PTR_DEMANGLE
82   if (fcts->towc->__shlib_handle != NULL)
83     PTR_DEMANGLE (fct);
84 #endif
85   status = DL_CALL_FCT (fct, (fcts->towc, &data, &inbuf, endbuf,
86 			      NULL, &dummy, 0, 1));
87 
88   /* There must not be any problems with the conversion but illegal input
89      characters.  The output buffer must be large enough, otherwise the
90      definition of MB_CUR_MAX is not correct.  All the other possible
91      errors also must not happen.  */
92   assert (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
93 	  || status == __GCONV_ILLEGAL_INPUT
94 	  || status == __GCONV_INCOMPLETE_INPUT
95 	  || status == __GCONV_FULL_OUTPUT);
96 
97   if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
98       || status == __GCONV_FULL_OUTPUT)
99     {
100       if (data.__outbuf != (unsigned char *) outbuf
101 	  && *(wchar_t *) outbuf == L'\0')
102 	{
103 	  /* The converted character is the NUL character.  */
104 	  assert (__mbsinit (data.__statep));
105 	  result = 0;
106 	}
107       else
108 	result = inbuf - (const unsigned char *) s;
109     }
110   else if (status == __GCONV_INCOMPLETE_INPUT)
111     result = (size_t) -2;
112   else
113     {
114     ilseq:
115       result = (size_t) -1;
116       __set_errno (EILSEQ);
117     }
118 
119   return result;
120 }
121 libc_hidden_def (__mbrtowc)
122 weak_alias (__mbrtowc, mbrtowc)
123 libc_hidden_weak (mbrtowc)
124