| 1 | /* Copyright (C) 1998-2020 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 | <https://www.gnu.org/licenses/>.  */ | 
|---|
| 18 |  | 
|---|
| 19 | #include <ctype.h> | 
|---|
| 20 | #include <langinfo.h> | 
|---|
| 21 | #include <limits.h> | 
|---|
| 22 | #include <stdlib.h> | 
|---|
| 23 | #include <stdio.h> | 
|---|
| 24 | #include <string.h> | 
|---|
| 25 |  | 
|---|
| 26 | #include <locale/localeinfo.h> | 
|---|
| 27 | #include <wcsmbsload.h> | 
|---|
| 28 | #include <libc-lock.h> | 
|---|
| 29 |  | 
|---|
| 30 |  | 
|---|
| 31 | /* These are the descriptions for the default conversion functions.  */ | 
|---|
| 32 | static const struct __gconv_step to_wc = | 
|---|
| 33 | { | 
|---|
| 34 | .__shlib_handle = NULL, | 
|---|
| 35 | .__modname = NULL, | 
|---|
| 36 | .__counter = INT_MAX, | 
|---|
| 37 | .__from_name = (char *) "ANSI_X3.4-1968//TRANSLIT", | 
|---|
| 38 | .__to_name = (char *) "INTERNAL", | 
|---|
| 39 | .__fct = __gconv_transform_ascii_internal, | 
|---|
| 40 | .__btowc_fct = __gconv_btwoc_ascii, | 
|---|
| 41 | .__init_fct = NULL, | 
|---|
| 42 | .__end_fct = NULL, | 
|---|
| 43 | .__min_needed_from = 1, | 
|---|
| 44 | .__max_needed_from = 1, | 
|---|
| 45 | .__min_needed_to = 4, | 
|---|
| 46 | .__max_needed_to = 4, | 
|---|
| 47 | .__stateful = 0, | 
|---|
| 48 | .__data = NULL | 
|---|
| 49 | }; | 
|---|
| 50 |  | 
|---|
| 51 | static const struct __gconv_step to_mb = | 
|---|
| 52 | { | 
|---|
| 53 | .__shlib_handle = NULL, | 
|---|
| 54 | .__modname = NULL, | 
|---|
| 55 | .__counter = INT_MAX, | 
|---|
| 56 | .__from_name = (char *) "INTERNAL", | 
|---|
| 57 | .__to_name = (char *) "ANSI_X3.4-1968//TRANSLIT", | 
|---|
| 58 | .__fct = __gconv_transform_internal_ascii, | 
|---|
| 59 | .__btowc_fct = NULL, | 
|---|
| 60 | .__init_fct = NULL, | 
|---|
| 61 | .__end_fct = NULL, | 
|---|
| 62 | .__min_needed_from = 4, | 
|---|
| 63 | .__max_needed_from = 4, | 
|---|
| 64 | .__min_needed_to = 1, | 
|---|
| 65 | .__max_needed_to = 1, | 
|---|
| 66 | .__stateful = 0, | 
|---|
| 67 | .__data = NULL | 
|---|
| 68 | }; | 
|---|
| 69 |  | 
|---|
| 70 |  | 
|---|
| 71 | /* For the default locale we only have to handle ANSI_X3.4-1968.  */ | 
|---|
| 72 | const struct gconv_fcts __wcsmbs_gconv_fcts_c = | 
|---|
| 73 | { | 
|---|
| 74 | .towc = (struct __gconv_step *) &to_wc, | 
|---|
| 75 | .towc_nsteps = 1, | 
|---|
| 76 | .tomb = (struct __gconv_step *) &to_mb, | 
|---|
| 77 | .tomb_nsteps = 1, | 
|---|
| 78 | }; | 
|---|
| 79 |  | 
|---|
| 80 |  | 
|---|
| 81 | attribute_hidden | 
|---|
| 82 | struct __gconv_step * | 
|---|
| 83 | __wcsmbs_getfct (const char *to, const char *from, size_t *nstepsp) | 
|---|
| 84 | { | 
|---|
| 85 | size_t nsteps; | 
|---|
| 86 | struct __gconv_step *result; | 
|---|
| 87 | #if 0 | 
|---|
| 88 | size_t nstateful; | 
|---|
| 89 | size_t cnt; | 
|---|
| 90 | #endif | 
|---|
| 91 |  | 
|---|
| 92 | if (__gconv_find_transform (to, from, &result, &nsteps, 0) != __GCONV_OK) | 
|---|
| 93 | /* Loading the conversion step is not possible.  */ | 
|---|
| 94 | return NULL; | 
|---|
| 95 |  | 
|---|
| 96 | /* Maybe it is someday necessary to allow more than one step. | 
|---|
| 97 | Currently this is not the case since the conversions handled here | 
|---|
| 98 | are from and to INTERNAL and there always is a converted for | 
|---|
| 99 | that.  It the directly following code is enabled the libio | 
|---|
| 100 | functions will have to allocate appropriate __gconv_step_data | 
|---|
| 101 | elements instead of only one.  */ | 
|---|
| 102 | #if 0 | 
|---|
| 103 | /* Count the number of stateful conversions.  Since we will only | 
|---|
| 104 | have one 'mbstate_t' object available we can only deal with one | 
|---|
| 105 | stateful conversion.  */ | 
|---|
| 106 | nstateful = 0; | 
|---|
| 107 | for (cnt = 0; cnt < nsteps; ++cnt) | 
|---|
| 108 | if (result[cnt].__stateful) | 
|---|
| 109 | ++nstateful; | 
|---|
| 110 | if (nstateful > 1) | 
|---|
| 111 | #else | 
|---|
| 112 | if (nsteps > 1) | 
|---|
| 113 | #endif | 
|---|
| 114 | { | 
|---|
| 115 | /* We cannot handle this case.  */ | 
|---|
| 116 | __gconv_close_transform (result, nsteps); | 
|---|
| 117 | result = NULL; | 
|---|
| 118 | } | 
|---|
| 119 | else | 
|---|
| 120 | *nstepsp = nsteps; | 
|---|
| 121 |  | 
|---|
| 122 | return result; | 
|---|
| 123 | } | 
|---|
| 124 |  | 
|---|
| 125 |  | 
|---|
| 126 | /* Extract from the given locale name the character set portion.  Since | 
|---|
| 127 | only the XPG form of the name includes this information we don't have | 
|---|
| 128 | to take care for the CEN form.  */ | 
|---|
| 129 | #define (str) \ | 
|---|
| 130 | ({									      \ | 
|---|
| 131 | const char *cp = str;						      \ | 
|---|
| 132 | char *result = NULL;						      \ | 
|---|
| 133 | \ | 
|---|
| 134 | cp += strcspn (cp, "@.+,");						      \ | 
|---|
| 135 | if (*cp == '.')							      \ | 
|---|
| 136 | {									      \ | 
|---|
| 137 | const char *endp = ++cp;					      \ | 
|---|
| 138 | while (*endp != '\0' && *endp != '@')				      \ | 
|---|
| 139 | ++endp;							      \ | 
|---|
| 140 | if (endp != cp)							      \ | 
|---|
| 141 | result = strndupa (cp, endp - cp);				      \ | 
|---|
| 142 | }									      \ | 
|---|
| 143 | result;								      \ | 
|---|
| 144 | }) | 
|---|
| 145 |  | 
|---|
| 146 |  | 
|---|
| 147 | /* Some of the functions here must not be used while setlocale is called.  */ | 
|---|
| 148 | __libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden) | 
|---|
| 149 |  | 
|---|
| 150 | /* Load conversion functions for the currently selected locale.  */ | 
|---|
| 151 | void | 
|---|
| 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 | __wcsmbs_clone_conv (struct gconv_fcts *copy) | 
|---|
| 219 | { | 
|---|
| 220 | const struct gconv_fcts *orig; | 
|---|
| 221 |  | 
|---|
| 222 | orig = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE)); | 
|---|
| 223 |  | 
|---|
| 224 | /* Copy the data.  */ | 
|---|
| 225 | *copy = *orig; | 
|---|
| 226 |  | 
|---|
| 227 | /* Now increment the usage counters.  Note: This assumes | 
|---|
| 228 | copy->*_nsteps == 1.  The current locale holds a reference, so it | 
|---|
| 229 | is still there after acquiring the lock.  */ | 
|---|
| 230 |  | 
|---|
| 231 | __libc_lock_lock (__gconv_lock); | 
|---|
| 232 |  | 
|---|
| 233 | bool overflow = false; | 
|---|
| 234 | if (copy->towc->__shlib_handle != NULL) | 
|---|
| 235 | overflow |= __builtin_add_overflow (copy->towc->__counter, 1, | 
|---|
| 236 | ©->towc->__counter); | 
|---|
| 237 | if (copy->tomb->__shlib_handle != NULL) | 
|---|
| 238 | overflow |= __builtin_add_overflow (copy->tomb->__counter, 1, | 
|---|
| 239 | ©->tomb->__counter); | 
|---|
| 240 |  | 
|---|
| 241 | __libc_lock_unlock (__gconv_lock); | 
|---|
| 242 |  | 
|---|
| 243 | if (overflow) | 
|---|
| 244 | __libc_fatal ( "\ | 
|---|
| 245 | Fatal glibc error: gconv module reference counter overflow\n"); | 
|---|
| 246 | } | 
|---|
| 247 |  | 
|---|
| 248 |  | 
|---|
| 249 | /* Get converters for named charset.  */ | 
|---|
| 250 | int | 
|---|
| 251 | __wcsmbs_named_conv (struct gconv_fcts *copy, const char *name) | 
|---|
| 252 | { | 
|---|
| 253 | copy->towc = __wcsmbs_getfct ( "INTERNAL", name, ©->towc_nsteps); | 
|---|
| 254 | if (copy->towc == NULL) | 
|---|
| 255 | return 1; | 
|---|
| 256 |  | 
|---|
| 257 | copy->tomb = __wcsmbs_getfct (name, "INTERNAL", ©->tomb_nsteps); | 
|---|
| 258 | if (copy->tomb == NULL) | 
|---|
| 259 | { | 
|---|
| 260 | __gconv_close_transform (copy->towc, copy->towc_nsteps); | 
|---|
| 261 | return 1; | 
|---|
| 262 | } | 
|---|
| 263 |  | 
|---|
| 264 | return 0; | 
|---|
| 265 | } | 
|---|
| 266 |  | 
|---|
| 267 | void | 
|---|
| 268 | _nl_cleanup_ctype (struct __locale_data *locale) | 
|---|
| 269 | { | 
|---|
| 270 | const struct gconv_fcts *const data = locale->private.ctype; | 
|---|
| 271 | if (data != NULL) | 
|---|
| 272 | { | 
|---|
| 273 | locale->private.ctype = NULL; | 
|---|
| 274 | locale->private.cleanup = NULL; | 
|---|
| 275 |  | 
|---|
| 276 | /* Free the old conversions.  */ | 
|---|
| 277 | __gconv_close_transform (data->tomb, data->tomb_nsteps); | 
|---|
| 278 | __gconv_close_transform (data->towc, data->towc_nsteps); | 
|---|
| 279 | free ((char *) data); | 
|---|
| 280 | } | 
|---|
| 281 | } | 
|---|
| 282 |  | 
|---|