|  | /* Copyright (C) 1999-2016 Free Software Foundation, Inc. | 
|  | This file is part of the GNU C Library. | 
|  |  | 
|  | The GNU C Library is free software; you can redistribute it and/or | 
|  | modify it under the terms of the GNU Lesser General Public | 
|  | License as published by the Free Software Foundation; either | 
|  | version 2.1 of the License, or (at your option) any later version. | 
|  |  | 
|  | The GNU C Library is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | Lesser General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU Lesser General Public | 
|  | License along with the GNU C Library; if not, see | 
|  | <http://www.gnu.org/licenses/>. | 
|  |  | 
|  | As a special exception, if you link the code in this file with | 
|  | files compiled with a GNU compiler to produce an executable, | 
|  | that does not cause the resulting executable to be covered by | 
|  | the GNU Lesser General Public License.  This exception does not | 
|  | however invalidate any other reasons why the executable file | 
|  | might be covered by the GNU Lesser General Public License. | 
|  | This exception applies to code released by its copyright holders | 
|  | in files containing the exception.  */ | 
|  |  | 
|  | #include <libioP.h> | 
|  | #ifdef _LIBC | 
|  | # include <dlfcn.h> | 
|  | # include <wchar.h> | 
|  | #endif | 
|  | #include <assert.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #ifdef _LIBC | 
|  | # include <langinfo.h> | 
|  | # include <locale/localeinfo.h> | 
|  | # include <wcsmbs/wcsmbsload.h> | 
|  | # include <iconv/gconv_int.h> | 
|  | # include <shlib-compat.h> | 
|  | # include <sysdep.h> | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* Prototypes of libio's codecvt functions.  */ | 
|  | static enum __codecvt_result do_out (struct _IO_codecvt *codecvt, | 
|  | __mbstate_t *statep, | 
|  | const wchar_t *from_start, | 
|  | const wchar_t *from_end, | 
|  | const wchar_t **from_stop, char *to_start, | 
|  | char *to_end, char **to_stop); | 
|  | static enum __codecvt_result do_unshift (struct _IO_codecvt *codecvt, | 
|  | __mbstate_t *statep, char *to_start, | 
|  | char *to_end, char **to_stop); | 
|  | static enum __codecvt_result do_in (struct _IO_codecvt *codecvt, | 
|  | __mbstate_t *statep, | 
|  | const char *from_start, | 
|  | const char *from_end, | 
|  | const char **from_stop, wchar_t *to_start, | 
|  | wchar_t *to_end, wchar_t **to_stop); | 
|  | static int do_encoding (struct _IO_codecvt *codecvt); | 
|  | static int do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep, | 
|  | const char *from_start, | 
|  | const char *from_end, _IO_size_t max); | 
|  | static int do_max_length (struct _IO_codecvt *codecvt); | 
|  | static int do_always_noconv (struct _IO_codecvt *codecvt); | 
|  |  | 
|  |  | 
|  | /* The functions used in `codecvt' for libio are always the same.  */ | 
|  | const struct _IO_codecvt __libio_codecvt = | 
|  | { | 
|  | .__codecvt_destr = NULL,		/* Destructor, never used.  */ | 
|  | .__codecvt_do_out = do_out, | 
|  | .__codecvt_do_unshift = do_unshift, | 
|  | .__codecvt_do_in = do_in, | 
|  | .__codecvt_do_encoding = do_encoding, | 
|  | .__codecvt_do_always_noconv = do_always_noconv, | 
|  | .__codecvt_do_length = do_length, | 
|  | .__codecvt_do_max_length = do_max_length | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* Return orientation of stream.  If mode is nonzero try to change | 
|  | the orientation first.  */ | 
|  | #undef _IO_fwide | 
|  | int | 
|  | _IO_fwide (_IO_FILE *fp, int mode) | 
|  | { | 
|  | /* Normalize the value.  */ | 
|  | mode = mode < 0 ? -1 : (mode == 0 ? 0 : 1); | 
|  |  | 
|  | #if defined SHARED && defined _LIBC \ | 
|  | && SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) | 
|  | if (__builtin_expect (&_IO_stdin_used == NULL, 0) | 
|  | && (fp == _IO_stdin || fp == _IO_stdout || fp == _IO_stderr)) | 
|  | /* This is for a stream in the glibc 2.0 format.  */ | 
|  | return -1; | 
|  | #endif | 
|  |  | 
|  | /* The orientation already has been determined.  */ | 
|  | if (fp->_mode != 0 | 
|  | /* Or the caller simply wants to know about the current orientation.  */ | 
|  | || mode == 0) | 
|  | return fp->_mode; | 
|  |  | 
|  | /* Set the orientation appropriately.  */ | 
|  | if (mode > 0) | 
|  | { | 
|  | struct _IO_codecvt *cc = fp->_codecvt = &fp->_wide_data->_codecvt; | 
|  |  | 
|  | fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end; | 
|  | fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base; | 
|  |  | 
|  | /* Get the character conversion functions based on the currently | 
|  | selected locale for LC_CTYPE.  */ | 
|  | #ifdef _LIBC | 
|  | { | 
|  | /* Clear the state.  We start all over again.  */ | 
|  | memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t)); | 
|  | memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t)); | 
|  |  | 
|  | struct gconv_fcts fcts; | 
|  | __wcsmbs_clone_conv (&fcts); | 
|  | assert (fcts.towc_nsteps == 1); | 
|  | assert (fcts.tomb_nsteps == 1); | 
|  |  | 
|  | /* The functions are always the same.  */ | 
|  | *cc = __libio_codecvt; | 
|  |  | 
|  | cc->__cd_in.__cd.__nsteps = fcts.towc_nsteps; | 
|  | cc->__cd_in.__cd.__steps = fcts.towc; | 
|  |  | 
|  | cc->__cd_in.__cd.__data[0].__invocation_counter = 0; | 
|  | cc->__cd_in.__cd.__data[0].__internal_use = 1; | 
|  | cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST; | 
|  | cc->__cd_in.__cd.__data[0].__statep = &fp->_wide_data->_IO_state; | 
|  |  | 
|  | cc->__cd_out.__cd.__nsteps = fcts.tomb_nsteps; | 
|  | cc->__cd_out.__cd.__steps = fcts.tomb; | 
|  |  | 
|  | cc->__cd_out.__cd.__data[0].__invocation_counter = 0; | 
|  | cc->__cd_out.__cd.__data[0].__internal_use = 1; | 
|  | cc->__cd_out.__cd.__data[0].__flags | 
|  | = __GCONV_IS_LAST | __GCONV_TRANSLIT; | 
|  | cc->__cd_out.__cd.__data[0].__statep = &fp->_wide_data->_IO_state; | 
|  | } | 
|  | #else | 
|  | # ifdef _GLIBCPP_USE_WCHAR_T | 
|  | { | 
|  | /* Determine internal and external character sets. | 
|  |  | 
|  | XXX For now we make our life easy: we assume a fixed internal | 
|  | encoding (as most sane systems have; hi HP/UX!).  If somebody | 
|  | cares about systems which changing internal charsets they | 
|  | should come up with a solution for the determination of the | 
|  | currently used internal character set.  */ | 
|  | const char *internal_ccs = _G_INTERNAL_CCS; | 
|  | const char *external_ccs = NULL; | 
|  |  | 
|  | #  ifdef HAVE_NL_LANGINFO | 
|  | external_ccs = nl_langinfo (CODESET); | 
|  | #  endif | 
|  | if (external_ccs == NULL) | 
|  | external_ccs = "ISO-8859-1"; | 
|  |  | 
|  | cc->__cd_in = iconv_open (internal_ccs, external_ccs); | 
|  | if (cc->__cd_in != (iconv_t) -1) | 
|  | cc->__cd_out = iconv_open (external_ccs, internal_ccs); | 
|  |  | 
|  | if (cc->__cd_in == (iconv_t) -1 || cc->__cd_out == (iconv_t) -1) | 
|  | { | 
|  | if (cc->__cd_in != (iconv_t) -1) | 
|  | iconv_close (cc->__cd_in); | 
|  | /* XXX */ | 
|  | abort (); | 
|  | } | 
|  | } | 
|  | # else | 
|  | #  error "somehow determine this from LC_CTYPE" | 
|  | # endif | 
|  | #endif | 
|  |  | 
|  | /* From now on use the wide character callback functions.  */ | 
|  | _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable; | 
|  | } | 
|  |  | 
|  | /* Set the mode now.  */ | 
|  | fp->_mode = mode; | 
|  |  | 
|  | return mode; | 
|  | } | 
|  |  | 
|  |  | 
|  | static enum __codecvt_result | 
|  | do_out (struct _IO_codecvt *codecvt, __mbstate_t *statep, | 
|  | const wchar_t *from_start, const wchar_t *from_end, | 
|  | const wchar_t **from_stop, char *to_start, char *to_end, | 
|  | char **to_stop) | 
|  | { | 
|  | enum __codecvt_result result; | 
|  |  | 
|  | #ifdef _LIBC | 
|  | struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps; | 
|  | int status; | 
|  | size_t dummy; | 
|  | const unsigned char *from_start_copy = (unsigned char *) from_start; | 
|  |  | 
|  | codecvt->__cd_out.__cd.__data[0].__outbuf = (unsigned char *) to_start; | 
|  | codecvt->__cd_out.__cd.__data[0].__outbufend = (unsigned char *) to_end; | 
|  | codecvt->__cd_out.__cd.__data[0].__statep = statep; | 
|  |  | 
|  | __gconv_fct fct = gs->__fct; | 
|  | #ifdef PTR_DEMANGLE | 
|  | if (gs->__shlib_handle != NULL) | 
|  | PTR_DEMANGLE (fct); | 
|  | #endif | 
|  |  | 
|  | status = DL_CALL_FCT (fct, | 
|  | (gs, codecvt->__cd_out.__cd.__data, &from_start_copy, | 
|  | (const unsigned char *) from_end, NULL, | 
|  | &dummy, 0, 0)); | 
|  |  | 
|  | *from_stop = (wchar_t *) from_start_copy; | 
|  | *to_stop = (char *) codecvt->__cd_out.__cd.__data[0].__outbuf; | 
|  |  | 
|  | switch (status) | 
|  | { | 
|  | case __GCONV_OK: | 
|  | case __GCONV_EMPTY_INPUT: | 
|  | result = __codecvt_ok; | 
|  | break; | 
|  |  | 
|  | case __GCONV_FULL_OUTPUT: | 
|  | case __GCONV_INCOMPLETE_INPUT: | 
|  | result = __codecvt_partial; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | result = __codecvt_error; | 
|  | break; | 
|  | } | 
|  | #else | 
|  | # ifdef _GLIBCPP_USE_WCHAR_T | 
|  | size_t res; | 
|  | const char *from_start_copy = (const char *) from_start; | 
|  | size_t from_len = from_end - from_start; | 
|  | char *to_start_copy = to_start; | 
|  | size_t to_len = to_end - to_start; | 
|  | res = iconv (codecvt->__cd_out, &from_start_copy, &from_len, | 
|  | &to_start_copy, &to_len); | 
|  |  | 
|  | if (res == 0 || from_len == 0) | 
|  | result = __codecvt_ok; | 
|  | else if (to_len < codecvt->__codecvt_do_max_length (codecvt)) | 
|  | result = __codecvt_partial; | 
|  | else | 
|  | result = __codecvt_error; | 
|  |  | 
|  | # else | 
|  | /* Decide what to do.  */ | 
|  | result = __codecvt_error; | 
|  | # endif | 
|  | #endif | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | static enum __codecvt_result | 
|  | do_unshift (struct _IO_codecvt *codecvt, __mbstate_t *statep, | 
|  | char *to_start, char *to_end, char **to_stop) | 
|  | { | 
|  | enum __codecvt_result result; | 
|  |  | 
|  | #ifdef _LIBC | 
|  | struct __gconv_step *gs = codecvt->__cd_out.__cd.__steps; | 
|  | int status; | 
|  | size_t dummy; | 
|  |  | 
|  | codecvt->__cd_out.__cd.__data[0].__outbuf = (unsigned char *) to_start; | 
|  | codecvt->__cd_out.__cd.__data[0].__outbufend = (unsigned char *) to_end; | 
|  | codecvt->__cd_out.__cd.__data[0].__statep = statep; | 
|  |  | 
|  | __gconv_fct fct = gs->__fct; | 
|  | #ifdef PTR_DEMANGLE | 
|  | if (gs->__shlib_handle != NULL) | 
|  | PTR_DEMANGLE (fct); | 
|  | #endif | 
|  |  | 
|  | status = DL_CALL_FCT (fct, | 
|  | (gs, codecvt->__cd_out.__cd.__data, NULL, NULL, | 
|  | NULL, &dummy, 1, 0)); | 
|  |  | 
|  | *to_stop = (char *) codecvt->__cd_out.__cd.__data[0].__outbuf; | 
|  |  | 
|  | switch (status) | 
|  | { | 
|  | case __GCONV_OK: | 
|  | case __GCONV_EMPTY_INPUT: | 
|  | result = __codecvt_ok; | 
|  | break; | 
|  |  | 
|  | case __GCONV_FULL_OUTPUT: | 
|  | case __GCONV_INCOMPLETE_INPUT: | 
|  | result = __codecvt_partial; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | result = __codecvt_error; | 
|  | break; | 
|  | } | 
|  | #else | 
|  | # ifdef _GLIBCPP_USE_WCHAR_T | 
|  | size_t res; | 
|  | char *to_start_copy = (char *) to_start; | 
|  | size_t to_len = to_end - to_start; | 
|  |  | 
|  | res = iconv (codecvt->__cd_out, NULL, NULL, &to_start_copy, &to_len); | 
|  |  | 
|  | if (res == 0) | 
|  | result = __codecvt_ok; | 
|  | else if (to_len < codecvt->__codecvt_do_max_length (codecvt)) | 
|  | result = __codecvt_partial; | 
|  | else | 
|  | result = __codecvt_error; | 
|  | # else | 
|  | /* Decide what to do.  */ | 
|  | result = __codecvt_error; | 
|  | # endif | 
|  | #endif | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | static enum __codecvt_result | 
|  | do_in (struct _IO_codecvt *codecvt, __mbstate_t *statep, | 
|  | const char *from_start, const char *from_end, const char **from_stop, | 
|  | wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop) | 
|  | { | 
|  | enum __codecvt_result result; | 
|  |  | 
|  | #ifdef _LIBC | 
|  | struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps; | 
|  | int status; | 
|  | size_t dummy; | 
|  | const unsigned char *from_start_copy = (unsigned char *) from_start; | 
|  |  | 
|  | codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_start; | 
|  | codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) to_end; | 
|  | codecvt->__cd_in.__cd.__data[0].__statep = statep; | 
|  |  | 
|  | __gconv_fct fct = gs->__fct; | 
|  | #ifdef PTR_DEMANGLE | 
|  | if (gs->__shlib_handle != NULL) | 
|  | PTR_DEMANGLE (fct); | 
|  | #endif | 
|  |  | 
|  | status = DL_CALL_FCT (fct, | 
|  | (gs, codecvt->__cd_in.__cd.__data, &from_start_copy, | 
|  | (const unsigned char *) from_end, NULL, | 
|  | &dummy, 0, 0)); | 
|  |  | 
|  | *from_stop = (const char *) from_start_copy; | 
|  | *to_stop = (wchar_t *) codecvt->__cd_in.__cd.__data[0].__outbuf; | 
|  |  | 
|  | switch (status) | 
|  | { | 
|  | case __GCONV_OK: | 
|  | case __GCONV_EMPTY_INPUT: | 
|  | result = __codecvt_ok; | 
|  | break; | 
|  |  | 
|  | case __GCONV_FULL_OUTPUT: | 
|  | case __GCONV_INCOMPLETE_INPUT: | 
|  | result = __codecvt_partial; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | result = __codecvt_error; | 
|  | break; | 
|  | } | 
|  | #else | 
|  | # ifdef _GLIBCPP_USE_WCHAR_T | 
|  | size_t res; | 
|  | const char *from_start_copy = (const char *) from_start; | 
|  | size_t from_len = from_end - from_start; | 
|  | char *to_start_copy = (char *) from_start; | 
|  | size_t to_len = to_end - to_start; | 
|  |  | 
|  | res = iconv (codecvt->__cd_in, &from_start_copy, &from_len, | 
|  | &to_start_copy, &to_len); | 
|  |  | 
|  | if (res == 0) | 
|  | result = __codecvt_ok; | 
|  | else if (to_len == 0) | 
|  | result = __codecvt_partial; | 
|  | else if (from_len < codecvt->__codecvt_do_max_length (codecvt)) | 
|  | result = __codecvt_partial; | 
|  | else | 
|  | result = __codecvt_error; | 
|  | # else | 
|  | /* Decide what to do.  */ | 
|  | result = __codecvt_error; | 
|  | # endif | 
|  | #endif | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | do_encoding (struct _IO_codecvt *codecvt) | 
|  | { | 
|  | #ifdef _LIBC | 
|  | /* See whether the encoding is stateful.  */ | 
|  | if (codecvt->__cd_in.__cd.__steps[0].__stateful) | 
|  | return -1; | 
|  | /* Fortunately not.  Now determine the input bytes for the conversion | 
|  | necessary for each wide character.  */ | 
|  | if (codecvt->__cd_in.__cd.__steps[0].__min_needed_from | 
|  | != codecvt->__cd_in.__cd.__steps[0].__max_needed_from) | 
|  | /* Not a constant value.  */ | 
|  | return 0; | 
|  |  | 
|  | return codecvt->__cd_in.__cd.__steps[0].__min_needed_from; | 
|  | #else | 
|  | /* Worst case scenario.  */ | 
|  | return -1; | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | do_always_noconv (struct _IO_codecvt *codecvt) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep, | 
|  | const char *from_start, const char *from_end, _IO_size_t max) | 
|  | { | 
|  | int result; | 
|  | #ifdef _LIBC | 
|  | const unsigned char *cp = (const unsigned char *) from_start; | 
|  | wchar_t to_buf[max]; | 
|  | struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps; | 
|  | size_t dummy; | 
|  |  | 
|  | codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_buf; | 
|  | codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) &to_buf[max]; | 
|  | codecvt->__cd_in.__cd.__data[0].__statep = statep; | 
|  |  | 
|  | __gconv_fct fct = gs->__fct; | 
|  | #ifdef PTR_DEMANGLE | 
|  | if (gs->__shlib_handle != NULL) | 
|  | PTR_DEMANGLE (fct); | 
|  | #endif | 
|  |  | 
|  | DL_CALL_FCT (fct, | 
|  | (gs, codecvt->__cd_in.__cd.__data, &cp, | 
|  | (const unsigned char *) from_end, NULL, | 
|  | &dummy, 0, 0)); | 
|  |  | 
|  | result = cp - (const unsigned char *) from_start; | 
|  | #else | 
|  | # ifdef _GLIBCPP_USE_WCHAR_T | 
|  | const char *from_start_copy = (const char *) from_start; | 
|  | size_t from_len = from_end - from_start; | 
|  | wchar_t to_buf[max]; | 
|  | size_t res; | 
|  | char *to_start = (char *) to_buf; | 
|  |  | 
|  | res = iconv (codecvt->__cd_in, &from_start_copy, &from_len, | 
|  | &to_start, &max); | 
|  |  | 
|  | result = from_start_copy - (char *) from_start; | 
|  | # else | 
|  | /* Decide what to do.  */ | 
|  | result = 0; | 
|  | # endif | 
|  | #endif | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | do_max_length (struct _IO_codecvt *codecvt) | 
|  | { | 
|  | #ifdef _LIBC | 
|  | return codecvt->__cd_in.__cd.__steps[0].__max_needed_from; | 
|  | #else | 
|  | return MB_CUR_MAX; | 
|  | #endif | 
|  | } |