summaryrefslogtreecommitdiff
path: root/wcsmbs/mbrtoc16.c
diff options
context:
space:
mode:
Diffstat (limited to 'wcsmbs/mbrtoc16.c')
-rw-r--r--wcsmbs/mbrtoc16.c75
1 files changed, 34 insertions, 41 deletions
diff --git a/wcsmbs/mbrtoc16.c b/wcsmbs/mbrtoc16.c
index df970fba4f..f5ed2b4ac9 100644
--- a/wcsmbs/mbrtoc16.c
+++ b/wcsmbs/mbrtoc16.c
@@ -30,12 +30,6 @@
# define EILSEQ EINVAL
#endif
-#if __STDC__ >= 201000L
-# define U(c) U##c
-#else
-# define U(c) L##c
-#endif
-
/* This is the private state used if PS is NULL. */
static mbstate_t state;
@@ -46,6 +40,11 @@ mbrtoc16 (char16_t *pc16, const char *s, size_t n, mbstate_t *ps)
if (ps == NULL)
ps = &state;
+ /* The standard text does not say that S being NULL means the state
+ is reset even if the second half of a surrogate still have to be
+ returned. In fact, the error code description indicates
+ otherwise. Therefore always first try to return a second
+ half. */
if (ps->__count & 0x80000000)
{
/* We have to return the second word for a surrogate. */
@@ -55,13 +54,13 @@ mbrtoc16 (char16_t *pc16, const char *s, size_t n, mbstate_t *ps)
return (size_t) -3;
}
- char16_t buf[2];
+ wchar_t wc;
struct __gconv_step_data data;
int status;
size_t result;
size_t dummy;
const unsigned char *inbuf, *endbuf;
- unsigned char *outbuf = (unsigned char *) buf;
+ unsigned char *outbuf = (unsigned char *) &wc;
const struct gconv_fcts *fcts;
/* Set information for this step. */
@@ -75,14 +74,14 @@ mbrtoc16 (char16_t *pc16, const char *s, size_t n, mbstate_t *ps)
initial state. */
if (s == NULL)
{
- outbuf = (unsigned char *) buf;
+ pc16 = NULL;
s = "";
n = 1;
}
/* Tell where we want the result. */
data.__outbuf = outbuf;
- data.__outbufend = outbuf + sizeof (char16_t);
+ data.__outbufend = outbuf + sizeof (wchar_t);
/* Get the conversion functions. */
fcts = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE));
@@ -91,28 +90,20 @@ mbrtoc16 (char16_t *pc16, const char *s, size_t n, mbstate_t *ps)
inbuf = (const unsigned char *) s;
endbuf = inbuf + n;
if (__builtin_expect (endbuf < inbuf, 0))
- endbuf = (const unsigned char *) ~(uintptr_t) 0;
- __gconv_fct fct = fcts->toc16->__fct;
+ {
+ endbuf = (const unsigned char *) ~(uintptr_t) 0;
+ if (endbuf == inbuf)
+ goto ilseq;
+ }
+ __gconv_fct fct = fcts->towc->__fct;
#ifdef PTR_DEMANGLE
- if (fcts->toc16->__shlib_handle != NULL)
+ if (fcts->towc->__shlib_handle != NULL)
PTR_DEMANGLE (fct);
#endif
- /* We first have to check whether the character can be represented
- without a surrogate. If we immediately pass in a buffer large
- enough to hold two char16_t values and the first character does
- not require a surrogate the routine will try to convert more
- input if N is larger then needed for the first character. */
- status = DL_CALL_FCT (fct, (fcts->toc16, &data, &inbuf, endbuf,
+ status = DL_CALL_FCT (fct, (fcts->towc, &data, &inbuf, endbuf,
NULL, &dummy, 0, 1));
- if (status == __GCONV_FULL_OUTPUT && data.__outbuf == outbuf)
- {
- data.__outbufend = outbuf + 2 * sizeof (char16_t);
- status = DL_CALL_FCT (fct, (fcts->toc16, &data, &inbuf, endbuf,
- NULL, &dummy, 0, 1));
- }
-
/* There must not be any problems with the conversion but illegal input
characters. The output buffer must be large enough, otherwise the
definition of MB_CUR_MAX is not correct. All the other possible
@@ -125,33 +116,35 @@ mbrtoc16 (char16_t *pc16, const char *s, size_t n, mbstate_t *ps)
if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
|| status == __GCONV_FULL_OUTPUT)
{
- if (pc16 != NULL)
- *pc16 = buf[0];
+ result = inbuf - (const unsigned char *) s;
- if (data.__outbuf != outbuf && *(char16_t *) outbuf == U('\0'))
+ if (wc < 0x10000)
{
- /* The converted character is the NUL character. */
- assert (__mbsinit (data.__statep));
- result = 0;
+ if (pc16 != NULL)
+ *pc16 = wc;
+
+ if (data.__outbuf != outbuf && wc == L'\0')
+ {
+ /* The converted character is the NUL character. */
+ assert (__mbsinit (data.__statep));
+ result = 0;
+ }
}
else
{
- result = inbuf - (const unsigned char *) s;
+ /* This is a surrogate. */
+ if (pc16 != NULL)
+ *pc16 = 0xd7c0 + (wc >> 10);
- if (data.__outbuf != outbuf + 2)
- {
- /* This is a surrogate. */
- assert (buf[0] >= 0xd800 && buf[0] <= 0xdfff);
- assert (buf[1] >= 0xdc00 && buf[1] <= 0xdfff);
- ps->__count |= 0x80000000;
- ps->__value.__wch = buf[1];
- }
+ ps->__count |= 0x80000000;
+ ps->__value.__wch = 0xdc00 + (wc & 0x3ff);
}
}
else if (status == __GCONV_INCOMPLETE_INPUT)
result = (size_t) -2;
else
{
+ ilseq:
result = (size_t) -1;
__set_errno (EILSEQ);
}