mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-10-30 10:45:40 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			271 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			271 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (C) 1998-2017 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);
 | |
|     }
 | |
| }
 |