| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Copyright (C) 1998-2016 Free Software Foundation, Inc. | 
 | 2 |    This file is part of the GNU C Library. | 
 | 3 |    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. | 
 | 4 |  | 
 | 5 |    The GNU C Library is free software; you can redistribute it and/or | 
 | 6 |    modify it under the terms of the GNU Lesser General Public | 
 | 7 |    License as published by the Free Software Foundation; either | 
 | 8 |    version 2.1 of the License, or (at your option) any later version. | 
 | 9 |  | 
 | 10 |    The GNU C Library is distributed in the hope that it will be useful, | 
 | 11 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 12 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 | 13 |    Lesser General Public License for more details. | 
 | 14 |  | 
 | 15 |    You should have received a copy of the GNU Lesser General Public | 
 | 16 |    License along with the GNU C Library; if not, see | 
 | 17 |    <http://www.gnu.org/licenses/>.  */ | 
 | 18 |  | 
 | 19 | #include <ctype.h> | 
 | 20 | #include <langinfo.h> | 
 | 21 | #include <limits.h> | 
 | 22 | #include <stdlib.h> | 
 | 23 | #include <string.h> | 
 | 24 |  | 
 | 25 | #include <locale/localeinfo.h> | 
 | 26 | #include <wcsmbsload.h> | 
 | 27 | #include <libc-lock.h> | 
 | 28 |  | 
 | 29 |  | 
 | 30 | /* These are the descriptions for the default conversion functions.  */ | 
 | 31 | static const struct __gconv_step to_wc = | 
 | 32 | { | 
 | 33 |   .__shlib_handle = NULL, | 
 | 34 |   .__modname = NULL, | 
 | 35 |   .__counter = INT_MAX, | 
 | 36 |   .__from_name = (char *) "ANSI_X3.4-1968//TRANSLIT", | 
 | 37 |   .__to_name = (char *) "INTERNAL", | 
 | 38 |   .__fct = __gconv_transform_ascii_internal, | 
 | 39 |   .__btowc_fct = __gconv_btwoc_ascii, | 
 | 40 |   .__init_fct = NULL, | 
 | 41 |   .__end_fct = NULL, | 
 | 42 |   .__min_needed_from = 1, | 
 | 43 |   .__max_needed_from = 1, | 
 | 44 |   .__min_needed_to = 4, | 
 | 45 |   .__max_needed_to = 4, | 
 | 46 |   .__stateful = 0, | 
 | 47 |   .__data = NULL | 
 | 48 | }; | 
 | 49 |  | 
 | 50 | static const struct __gconv_step to_mb = | 
 | 51 | { | 
 | 52 |   .__shlib_handle = NULL, | 
 | 53 |   .__modname = NULL, | 
 | 54 |   .__counter = INT_MAX, | 
 | 55 |   .__from_name = (char *) "INTERNAL", | 
 | 56 |   .__to_name = (char *) "ANSI_X3.4-1968//TRANSLIT", | 
 | 57 |   .__fct = __gconv_transform_internal_ascii, | 
 | 58 |   .__btowc_fct = NULL, | 
 | 59 |   .__init_fct = NULL, | 
 | 60 |   .__end_fct = NULL, | 
 | 61 |   .__min_needed_from = 4, | 
 | 62 |   .__max_needed_from = 4, | 
 | 63 |   .__min_needed_to = 1, | 
 | 64 |   .__max_needed_to = 1, | 
 | 65 |   .__stateful = 0, | 
 | 66 |   .__data = NULL | 
 | 67 | }; | 
 | 68 |  | 
 | 69 |  | 
 | 70 | /* For the default locale we only have to handle ANSI_X3.4-1968.  */ | 
 | 71 | const struct gconv_fcts __wcsmbs_gconv_fcts_c = | 
 | 72 | { | 
 | 73 |   .towc = (struct __gconv_step *) &to_wc, | 
 | 74 |   .towc_nsteps = 1, | 
 | 75 |   .tomb = (struct __gconv_step *) &to_mb, | 
 | 76 |   .tomb_nsteps = 1, | 
 | 77 | }; | 
 | 78 |  | 
 | 79 |  | 
 | 80 | attribute_hidden | 
 | 81 | struct __gconv_step * | 
 | 82 | __wcsmbs_getfct (const char *to, const char *from, size_t *nstepsp) | 
 | 83 | { | 
 | 84 |   size_t nsteps; | 
 | 85 |   struct __gconv_step *result; | 
 | 86 | #if 0 | 
 | 87 |   size_t nstateful; | 
 | 88 |   size_t cnt; | 
 | 89 | #endif | 
 | 90 |  | 
 | 91 |   if (__gconv_find_transform (to, from, &result, &nsteps, 0) != __GCONV_OK) | 
 | 92 |     /* Loading the conversion step is not possible.  */ | 
 | 93 |     return NULL; | 
 | 94 |  | 
 | 95 |   /* Maybe it is someday necessary to allow more than one step. | 
 | 96 |      Currently this is not the case since the conversions handled here | 
 | 97 |      are from and to INTERNAL and there always is a converted for | 
 | 98 |      that.  It the directly following code is enabled the libio | 
 | 99 |      functions will have to allocate appropriate __gconv_step_data | 
 | 100 |      elements instead of only one.  */ | 
 | 101 | #if 0 | 
 | 102 |   /* Count the number of stateful conversions.  Since we will only | 
 | 103 |      have one 'mbstate_t' object available we can only deal with one | 
 | 104 |      stateful conversion.  */ | 
 | 105 |   nstateful = 0; | 
 | 106 |   for (cnt = 0; cnt < nsteps; ++cnt) | 
 | 107 |     if (result[cnt].__stateful) | 
 | 108 |       ++nstateful; | 
 | 109 |   if (nstateful > 1) | 
 | 110 | #else | 
 | 111 |   if (nsteps > 1) | 
 | 112 | #endif | 
 | 113 |     { | 
 | 114 |       /* We cannot handle this case.  */ | 
 | 115 |       __gconv_close_transform (result, nsteps); | 
 | 116 |       result = NULL; | 
 | 117 |     } | 
 | 118 |   else | 
 | 119 |     *nstepsp = nsteps; | 
 | 120 |  | 
 | 121 |   return result; | 
 | 122 | } | 
 | 123 |  | 
 | 124 |  | 
 | 125 | /* Extract from the given locale name the character set portion.  Since | 
 | 126 |    only the XPG form of the name includes this information we don't have | 
 | 127 |    to take care for the CEN form.  */ | 
 | 128 | #define extract_charset_name(str) \ | 
 | 129 |   ({									      \ | 
 | 130 |     const char *cp = str;						      \ | 
 | 131 |     char *result = NULL;						      \ | 
 | 132 | 									      \ | 
 | 133 |     cp += strcspn (cp, "@.+,");						      \ | 
 | 134 |     if (*cp == '.')							      \ | 
 | 135 |       {									      \ | 
 | 136 | 	const char *endp = ++cp;					      \ | 
 | 137 | 	while (*endp != '\0' && *endp != '@')				      \ | 
 | 138 | 	  ++endp;							      \ | 
 | 139 | 	if (endp != cp)							      \ | 
 | 140 | 	  result = strndupa (cp, endp - cp);				      \ | 
 | 141 |       }									      \ | 
 | 142 |     result;								      \ | 
 | 143 |   }) | 
 | 144 |  | 
 | 145 |  | 
 | 146 | /* Some of the functions here must not be used while setlocale is called.  */ | 
 | 147 | __libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden) | 
 | 148 |  | 
 | 149 | /* Load conversion functions for the currently selected locale.  */ | 
 | 150 | void | 
 | 151 | internal_function | 
 | 152 | __wcsmbs_load_conv (struct __locale_data *new_category) | 
 | 153 | { | 
 | 154 |   /* Acquire the lock.  */ | 
 | 155 |   __libc_rwlock_wrlock (__libc_setlocale_lock); | 
 | 156 |  | 
 | 157 |   /* We should repeat the test since while we waited some other thread | 
 | 158 |      might have run this function.  */ | 
 | 159 |   if (__glibc_likely (new_category->private.ctype == NULL)) | 
 | 160 |     { | 
 | 161 |       /* We must find the real functions.  */ | 
 | 162 |       const char *charset_name; | 
 | 163 |       const char *complete_name; | 
 | 164 |       struct gconv_fcts *new_fcts; | 
 | 165 |       int use_translit; | 
 | 166 |  | 
 | 167 |       /* Allocate the gconv_fcts structure.  */ | 
 | 168 |       new_fcts = calloc (1, sizeof *new_fcts); | 
 | 169 |       if (new_fcts == NULL) | 
 | 170 | 	goto failed; | 
 | 171 |  | 
 | 172 |       /* Get name of charset of the locale.  */ | 
 | 173 |       charset_name = new_category->values[_NL_ITEM_INDEX(CODESET)].string; | 
 | 174 |  | 
 | 175 |       /* Does the user want transliteration?  */ | 
 | 176 |       use_translit = new_category->use_translit; | 
 | 177 |  | 
 | 178 |       /* Normalize the name and add the slashes necessary for a | 
 | 179 | 	 complete lookup.  */ | 
 | 180 |       complete_name = norm_add_slashes (charset_name, | 
 | 181 | 					use_translit ? "TRANSLIT" : ""); | 
 | 182 |  | 
 | 183 |       /* It is not necessary to use transliteration in this direction | 
 | 184 | 	 since the internal character set is supposed to be able to | 
 | 185 | 	 represent all others.  */ | 
 | 186 |       new_fcts->towc = __wcsmbs_getfct ("INTERNAL", complete_name, | 
 | 187 | 					&new_fcts->towc_nsteps); | 
 | 188 |       if (new_fcts->towc != NULL) | 
 | 189 | 	new_fcts->tomb = __wcsmbs_getfct (complete_name, "INTERNAL", | 
 | 190 | 					  &new_fcts->tomb_nsteps); | 
 | 191 |  | 
 | 192 |       /* If any of the conversion functions is not available we don't | 
 | 193 | 	 use any since this would mean we cannot convert back and | 
 | 194 | 	 forth.  NB: NEW_FCTS was allocated with calloc.  */ | 
 | 195 |       if (new_fcts->tomb == NULL) | 
 | 196 | 	{ | 
 | 197 | 	  if (new_fcts->towc != NULL) | 
 | 198 | 	    __gconv_close_transform (new_fcts->towc, new_fcts->towc_nsteps); | 
 | 199 |  | 
 | 200 | 	  free (new_fcts); | 
 | 201 |  | 
 | 202 | 	failed: | 
 | 203 | 	  new_category->private.ctype = &__wcsmbs_gconv_fcts_c; | 
 | 204 | 	} | 
 | 205 |       else | 
 | 206 | 	{ | 
 | 207 | 	  new_category->private.ctype = new_fcts; | 
 | 208 | 	  new_category->private.cleanup = &_nl_cleanup_ctype; | 
 | 209 | 	} | 
 | 210 |     } | 
 | 211 |  | 
 | 212 |   __libc_rwlock_unlock (__libc_setlocale_lock); | 
 | 213 | } | 
 | 214 |  | 
 | 215 |  | 
 | 216 | /* Clone the current conversion function set.  */ | 
 | 217 | void | 
 | 218 | internal_function | 
 | 219 | __wcsmbs_clone_conv (struct gconv_fcts *copy) | 
 | 220 | { | 
 | 221 |   const struct gconv_fcts *orig; | 
 | 222 |  | 
 | 223 |   orig = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE)); | 
 | 224 |  | 
 | 225 |   /* Copy the data.  */ | 
 | 226 |   *copy = *orig; | 
 | 227 |  | 
 | 228 |   /* Now increment the usage counters. | 
 | 229 |      Note: This assumes copy->*_nsteps == 1.  */ | 
 | 230 |   if (copy->towc->__shlib_handle != NULL) | 
 | 231 |     ++copy->towc->__counter; | 
 | 232 |   if (copy->tomb->__shlib_handle != NULL) | 
 | 233 |     ++copy->tomb->__counter; | 
 | 234 | } | 
 | 235 |  | 
 | 236 |  | 
 | 237 | /* Get converters for named charset.  */ | 
 | 238 | int | 
 | 239 | internal_function | 
 | 240 | __wcsmbs_named_conv (struct gconv_fcts *copy, const char *name) | 
 | 241 | { | 
 | 242 |   copy->towc = __wcsmbs_getfct ("INTERNAL", name, ©->towc_nsteps); | 
 | 243 |   if (copy->towc == NULL) | 
 | 244 |     return 1; | 
 | 245 |  | 
 | 246 |   copy->tomb = __wcsmbs_getfct (name, "INTERNAL", ©->tomb_nsteps); | 
 | 247 |   if (copy->tomb == NULL) | 
 | 248 |     { | 
 | 249 |       __gconv_close_transform (copy->towc, copy->towc_nsteps); | 
 | 250 |       return 1; | 
 | 251 |     } | 
 | 252 |  | 
 | 253 |   return 0; | 
 | 254 | } | 
 | 255 |  | 
 | 256 | void internal_function | 
 | 257 | _nl_cleanup_ctype (struct __locale_data *locale) | 
 | 258 | { | 
 | 259 |   const struct gconv_fcts *const data = locale->private.ctype; | 
 | 260 |   if (data != NULL) | 
 | 261 |     { | 
 | 262 |       locale->private.ctype = NULL; | 
 | 263 |       locale->private.cleanup = NULL; | 
 | 264 |  | 
 | 265 |       /* Free the old conversions.  */ | 
 | 266 |       __gconv_close_transform (data->tomb, data->tomb_nsteps); | 
 | 267 |       __gconv_close_transform (data->towc, data->towc_nsteps); | 
 | 268 |       free ((char *) data); | 
 | 269 |     } | 
 | 270 | } |