|  | /* Copyright (C) 1998-2016 Free Software Foundation, Inc. | 
|  | This file is part of the GNU C Library. | 
|  | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. | 
|  |  | 
|  | 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/>.  */ | 
|  |  | 
|  | #include <ctype.h> | 
|  | #include <langinfo.h> | 
|  | #include <limits.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <locale/localeinfo.h> | 
|  | #include <wcsmbsload.h> | 
|  | #include <libc-lock.h> | 
|  |  | 
|  |  | 
|  | /* These are the descriptions for the default conversion functions.  */ | 
|  | static const struct __gconv_step to_wc = | 
|  | { | 
|  | .__shlib_handle = NULL, | 
|  | .__modname = NULL, | 
|  | .__counter = INT_MAX, | 
|  | .__from_name = (char *) "ANSI_X3.4-1968//TRANSLIT", | 
|  | .__to_name = (char *) "INTERNAL", | 
|  | .__fct = __gconv_transform_ascii_internal, | 
|  | .__btowc_fct = __gconv_btwoc_ascii, | 
|  | .__init_fct = NULL, | 
|  | .__end_fct = NULL, | 
|  | .__min_needed_from = 1, | 
|  | .__max_needed_from = 1, | 
|  | .__min_needed_to = 4, | 
|  | .__max_needed_to = 4, | 
|  | .__stateful = 0, | 
|  | .__data = NULL | 
|  | }; | 
|  |  | 
|  | static const struct __gconv_step to_mb = | 
|  | { | 
|  | .__shlib_handle = NULL, | 
|  | .__modname = NULL, | 
|  | .__counter = INT_MAX, | 
|  | .__from_name = (char *) "INTERNAL", | 
|  | .__to_name = (char *) "ANSI_X3.4-1968//TRANSLIT", | 
|  | .__fct = __gconv_transform_internal_ascii, | 
|  | .__btowc_fct = NULL, | 
|  | .__init_fct = NULL, | 
|  | .__end_fct = NULL, | 
|  | .__min_needed_from = 4, | 
|  | .__max_needed_from = 4, | 
|  | .__min_needed_to = 1, | 
|  | .__max_needed_to = 1, | 
|  | .__stateful = 0, | 
|  | .__data = NULL | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* For the default locale we only have to handle ANSI_X3.4-1968.  */ | 
|  | const struct gconv_fcts __wcsmbs_gconv_fcts_c = | 
|  | { | 
|  | .towc = (struct __gconv_step *) &to_wc, | 
|  | .towc_nsteps = 1, | 
|  | .tomb = (struct __gconv_step *) &to_mb, | 
|  | .tomb_nsteps = 1, | 
|  | }; | 
|  |  | 
|  |  | 
|  | attribute_hidden | 
|  | struct __gconv_step * | 
|  | __wcsmbs_getfct (const char *to, const char *from, size_t *nstepsp) | 
|  | { | 
|  | size_t nsteps; | 
|  | struct __gconv_step *result; | 
|  | #if 0 | 
|  | size_t nstateful; | 
|  | size_t cnt; | 
|  | #endif | 
|  |  | 
|  | if (__gconv_find_transform (to, from, &result, &nsteps, 0) != __GCONV_OK) | 
|  | /* Loading the conversion step is not possible.  */ | 
|  | return NULL; | 
|  |  | 
|  | /* Maybe it is someday necessary to allow more than one step. | 
|  | Currently this is not the case since the conversions handled here | 
|  | are from and to INTERNAL and there always is a converted for | 
|  | that.  It the directly following code is enabled the libio | 
|  | functions will have to allocate appropriate __gconv_step_data | 
|  | elements instead of only one.  */ | 
|  | #if 0 | 
|  | /* Count the number of stateful conversions.  Since we will only | 
|  | have one 'mbstate_t' object available we can only deal with one | 
|  | stateful conversion.  */ | 
|  | nstateful = 0; | 
|  | for (cnt = 0; cnt < nsteps; ++cnt) | 
|  | if (result[cnt].__stateful) | 
|  | ++nstateful; | 
|  | if (nstateful > 1) | 
|  | #else | 
|  | if (nsteps > 1) | 
|  | #endif | 
|  | { | 
|  | /* We cannot handle this case.  */ | 
|  | __gconv_close_transform (result, nsteps); | 
|  | result = NULL; | 
|  | } | 
|  | else | 
|  | *nstepsp = nsteps; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Extract from the given locale name the character set portion.  Since | 
|  | only the XPG form of the name includes this information we don't have | 
|  | to take care for the CEN form.  */ | 
|  | #define extract_charset_name(str) \ | 
|  | ({									      \ | 
|  | const char *cp = str;						      \ | 
|  | char *result = NULL;						      \ | 
|  | \ | 
|  | cp += strcspn (cp, "@.+,");						      \ | 
|  | if (*cp == '.')							      \ | 
|  | {									      \ | 
|  | const char *endp = ++cp;					      \ | 
|  | while (*endp != '\0' && *endp != '@')				      \ | 
|  | ++endp;							      \ | 
|  | if (endp != cp)							      \ | 
|  | result = strndupa (cp, endp - cp);				      \ | 
|  | }									      \ | 
|  | result;								      \ | 
|  | }) | 
|  |  | 
|  |  | 
|  | /* Some of the functions here must not be used while setlocale is called.  */ | 
|  | __libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden) | 
|  |  | 
|  | /* Load conversion functions for the currently selected locale.  */ | 
|  | void | 
|  | internal_function | 
|  | __wcsmbs_load_conv (struct __locale_data *new_category) | 
|  | { | 
|  | /* Acquire the lock.  */ | 
|  | __libc_rwlock_wrlock (__libc_setlocale_lock); | 
|  |  | 
|  | /* We should repeat the test since while we waited some other thread | 
|  | might have run this function.  */ | 
|  | if (__glibc_likely (new_category->private.ctype == NULL)) | 
|  | { | 
|  | /* We must find the real functions.  */ | 
|  | const char *charset_name; | 
|  | const char *complete_name; | 
|  | struct gconv_fcts *new_fcts; | 
|  | int use_translit; | 
|  |  | 
|  | /* Allocate the gconv_fcts structure.  */ | 
|  | new_fcts = calloc (1, sizeof *new_fcts); | 
|  | if (new_fcts == NULL) | 
|  | goto failed; | 
|  |  | 
|  | /* Get name of charset of the locale.  */ | 
|  | charset_name = new_category->values[_NL_ITEM_INDEX(CODESET)].string; | 
|  |  | 
|  | /* Does the user want transliteration?  */ | 
|  | use_translit = new_category->use_translit; | 
|  |  | 
|  | /* Normalize the name and add the slashes necessary for a | 
|  | complete lookup.  */ | 
|  | complete_name = norm_add_slashes (charset_name, | 
|  | use_translit ? "TRANSLIT" : ""); | 
|  |  | 
|  | /* It is not necessary to use transliteration in this direction | 
|  | since the internal character set is supposed to be able to | 
|  | represent all others.  */ | 
|  | new_fcts->towc = __wcsmbs_getfct ("INTERNAL", complete_name, | 
|  | &new_fcts->towc_nsteps); | 
|  | if (new_fcts->towc != NULL) | 
|  | new_fcts->tomb = __wcsmbs_getfct (complete_name, "INTERNAL", | 
|  | &new_fcts->tomb_nsteps); | 
|  |  | 
|  | /* If any of the conversion functions is not available we don't | 
|  | use any since this would mean we cannot convert back and | 
|  | forth.  NB: NEW_FCTS was allocated with calloc.  */ | 
|  | if (new_fcts->tomb == NULL) | 
|  | { | 
|  | if (new_fcts->towc != NULL) | 
|  | __gconv_close_transform (new_fcts->towc, new_fcts->towc_nsteps); | 
|  |  | 
|  | free (new_fcts); | 
|  |  | 
|  | failed: | 
|  | new_category->private.ctype = &__wcsmbs_gconv_fcts_c; | 
|  | } | 
|  | else | 
|  | { | 
|  | new_category->private.ctype = new_fcts; | 
|  | new_category->private.cleanup = &_nl_cleanup_ctype; | 
|  | } | 
|  | } | 
|  |  | 
|  | __libc_rwlock_unlock (__libc_setlocale_lock); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Clone the current conversion function set.  */ | 
|  | void | 
|  | internal_function | 
|  | __wcsmbs_clone_conv (struct gconv_fcts *copy) | 
|  | { | 
|  | const struct gconv_fcts *orig; | 
|  |  | 
|  | orig = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE)); | 
|  |  | 
|  | /* Copy the data.  */ | 
|  | *copy = *orig; | 
|  |  | 
|  | /* Now increment the usage counters. | 
|  | Note: This assumes copy->*_nsteps == 1.  */ | 
|  | if (copy->towc->__shlib_handle != NULL) | 
|  | ++copy->towc->__counter; | 
|  | if (copy->tomb->__shlib_handle != NULL) | 
|  | ++copy->tomb->__counter; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Get converters for named charset.  */ | 
|  | int | 
|  | internal_function | 
|  | __wcsmbs_named_conv (struct gconv_fcts *copy, const char *name) | 
|  | { | 
|  | copy->towc = __wcsmbs_getfct ("INTERNAL", name, ©->towc_nsteps); | 
|  | if (copy->towc == NULL) | 
|  | return 1; | 
|  |  | 
|  | copy->tomb = __wcsmbs_getfct (name, "INTERNAL", ©->tomb_nsteps); | 
|  | if (copy->tomb == NULL) | 
|  | { | 
|  | __gconv_close_transform (copy->towc, copy->towc_nsteps); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void internal_function | 
|  | _nl_cleanup_ctype (struct __locale_data *locale) | 
|  | { | 
|  | const struct gconv_fcts *const data = locale->private.ctype; | 
|  | if (data != NULL) | 
|  | { | 
|  | locale->private.ctype = NULL; | 
|  | locale->private.cleanup = NULL; | 
|  |  | 
|  | /* Free the old conversions.  */ | 
|  | __gconv_close_transform (data->tomb, data->tomb_nsteps); | 
|  | __gconv_close_transform (data->towc, data->towc_nsteps); | 
|  | free ((char *) data); | 
|  | } | 
|  | } |