]>
Commit | Line | Data |
---|---|---|
dff8da6b | 1 | /* Copyright (C) 1998-2024 Free Software Foundation, Inc. |
4bca4c17 | 2 | This file is part of the GNU C Library. |
4bca4c17 UD |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either | |
7 | version 2.1 of the License, or (at your option) any later version. | |
4bca4c17 UD |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
41bdb6e2 | 12 | Lesser General Public License for more details. |
4bca4c17 | 13 | |
41bdb6e2 | 14 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 | 15 | License along with the GNU C Library; if not, see |
5a82c748 | 16 | <https://www.gnu.org/licenses/>. */ |
4bca4c17 | 17 | |
e4d0709f | 18 | #include <ctype.h> |
4bca4c17 UD |
19 | #include <langinfo.h> |
20 | #include <limits.h> | |
4553f228 | 21 | #include <stdlib.h> |
c9c15ac3 | 22 | #include <stdio.h> |
4553f228 | 23 | #include <string.h> |
4bca4c17 | 24 | |
4553f228 | 25 | #include <locale/localeinfo.h> |
4bca4c17 | 26 | #include <wcsmbsload.h> |
ec999b8e | 27 | #include <libc-lock.h> |
4bca4c17 UD |
28 | |
29 | ||
4bca4c17 | 30 | /* These are the descriptions for the default conversion functions. */ |
bd62cef6 | 31 | static const struct __gconv_step to_wc = |
4bca4c17 | 32 | { |
d64b6ad0 UD |
33 | .__shlib_handle = NULL, |
34 | .__modname = NULL, | |
35 | .__counter = INT_MAX, | |
129d706d UD |
36 | .__from_name = (char *) "ANSI_X3.4-1968//TRANSLIT", |
37 | .__to_name = (char *) "INTERNAL", | |
d64b6ad0 | 38 | .__fct = __gconv_transform_ascii_internal, |
f9ad060c | 39 | .__btowc_fct = __gconv_btwoc_ascii, |
d64b6ad0 UD |
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 | |
4bca4c17 UD |
48 | }; |
49 | ||
bd62cef6 | 50 | static const struct __gconv_step to_mb = |
4bca4c17 | 51 | { |
d64b6ad0 UD |
52 | .__shlib_handle = NULL, |
53 | .__modname = NULL, | |
54 | .__counter = INT_MAX, | |
129d706d UD |
55 | .__from_name = (char *) "INTERNAL", |
56 | .__to_name = (char *) "ANSI_X3.4-1968//TRANSLIT", | |
d64b6ad0 | 57 | .__fct = __gconv_transform_internal_ascii, |
f9ad060c | 58 | .__btowc_fct = NULL, |
d64b6ad0 UD |
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 | |
4bca4c17 UD |
67 | }; |
68 | ||
69 | ||
70 | /* For the default locale we only have to handle ANSI_X3.4-1968. */ | |
96310297 | 71 | const struct gconv_fcts __wcsmbs_gconv_fcts_c = |
4bca4c17 | 72 | { |
bd62cef6 | 73 | .towc = (struct __gconv_step *) &to_wc, |
129d706d | 74 | .towc_nsteps = 1, |
bd62cef6 | 75 | .tomb = (struct __gconv_step *) &to_mb, |
db6af3eb | 76 | .tomb_nsteps = 1, |
4bca4c17 UD |
77 | }; |
78 | ||
79 | ||
01beb5b9 | 80 | attribute_hidden |
c4bb5cd8 | 81 | struct __gconv_step * |
01beb5b9 | 82 | __wcsmbs_getfct (const char *to, const char *from, size_t *nstepsp) |
4bca4c17 UD |
83 | { |
84 | size_t nsteps; | |
d64b6ad0 | 85 | struct __gconv_step *result; |
129d706d | 86 | #if 0 |
d64b6ad0 UD |
87 | size_t nstateful; |
88 | size_t cnt; | |
129d706d | 89 | #endif |
4bca4c17 | 90 | |
c90a2db6 | 91 | if (__gconv_find_transform (to, from, &result, &nsteps, 0) != __GCONV_OK) |
4bca4c17 UD |
92 | /* Loading the conversion step is not possible. */ |
93 | return NULL; | |
94 | ||
129d706d UD |
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 | |
d64b6ad0 UD |
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) | |
129d706d UD |
110 | #else |
111 | if (nsteps > 1) | |
112 | #endif | |
d64b6ad0 UD |
113 | { |
114 | /* We cannot handle this case. */ | |
115 | __gconv_close_transform (result, nsteps); | |
116 | result = NULL; | |
117 | } | |
129d706d UD |
118 | else |
119 | *nstepsp = nsteps; | |
4bca4c17 UD |
120 | |
121 | return result; | |
122 | } | |
123 | ||
124 | ||
4553f228 UD |
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 | \ | |
b17277cf | 133 | cp += strcspn (cp, "@.+,"); \ |
4553f228 UD |
134 | if (*cp == '.') \ |
135 | { \ | |
a7c378d8 | 136 | const char *endp = ++cp; \ |
4553f228 UD |
137 | while (*endp != '\0' && *endp != '@') \ |
138 | ++endp; \ | |
139 | if (endp != cp) \ | |
a7c378d8 | 140 | result = strndupa (cp, endp - cp); \ |
4553f228 UD |
141 | } \ |
142 | result; \ | |
4553f228 UD |
143 | }) |
144 | ||
145 | ||
96310297 | 146 | /* Some of the functions here must not be used while setlocale is called. */ |
91e32540 | 147 | __libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden) |
d64b6ad0 | 148 | |
4bca4c17 UD |
149 | /* Load conversion functions for the currently selected locale. */ |
150 | void | |
f095bb72 | 151 | __wcsmbs_load_conv (struct __locale_data *new_category) |
4bca4c17 | 152 | { |
93ec1cf0 FW |
153 | struct lc_ctype_data *data = new_category->private; |
154 | ||
4bca4c17 | 155 | /* Acquire the lock. */ |
91e32540 | 156 | __libc_rwlock_wrlock (__libc_setlocale_lock); |
4bca4c17 | 157 | |
61d655c1 | 158 | /* We should repeat the test since while we waited some other thread |
4bca4c17 | 159 | might have run this function. */ |
93ec1cf0 | 160 | if (__glibc_likely (data->fcts == NULL)) |
4bca4c17 | 161 | { |
96310297 RM |
162 | /* We must find the real functions. */ |
163 | const char *charset_name; | |
164 | const char *complete_name; | |
165 | struct gconv_fcts *new_fcts; | |
166 | int use_translit; | |
167 | ||
168 | /* Allocate the gconv_fcts structure. */ | |
9954432e | 169 | new_fcts = calloc (1, sizeof *new_fcts); |
96310297 | 170 | if (new_fcts == NULL) |
6e606fad | 171 | goto failed; |
96310297 RM |
172 | |
173 | /* Get name of charset of the locale. */ | |
174 | charset_name = new_category->values[_NL_ITEM_INDEX(CODESET)].string; | |
175 | ||
176 | /* Does the user want transliteration? */ | |
177 | use_translit = new_category->use_translit; | |
178 | ||
179 | /* Normalize the name and add the slashes necessary for a | |
180 | complete lookup. */ | |
181 | complete_name = norm_add_slashes (charset_name, | |
db2f05ba | 182 | use_translit ? "TRANSLIT" : ""); |
96310297 RM |
183 | |
184 | /* It is not necessary to use transliteration in this direction | |
185 | since the internal character set is supposed to be able to | |
186 | represent all others. */ | |
187 | new_fcts->towc = __wcsmbs_getfct ("INTERNAL", complete_name, | |
188 | &new_fcts->towc_nsteps); | |
9954432e UD |
189 | if (new_fcts->towc != NULL) |
190 | new_fcts->tomb = __wcsmbs_getfct (complete_name, "INTERNAL", | |
191 | &new_fcts->tomb_nsteps); | |
96310297 RM |
192 | |
193 | /* If any of the conversion functions is not available we don't | |
194 | use any since this would mean we cannot convert back and | |
d3ed7225 | 195 | forth. NB: NEW_FCTS was allocated with calloc. */ |
96310297 | 196 | if (new_fcts->tomb == NULL) |
4bca4c17 | 197 | { |
96310297 RM |
198 | if (new_fcts->towc != NULL) |
199 | __gconv_close_transform (new_fcts->towc, new_fcts->towc_nsteps); | |
200 | ||
201 | free (new_fcts); | |
4bca4c17 | 202 | |
6e606fad | 203 | failed: |
93ec1cf0 | 204 | data->fcts = (void *) &__wcsmbs_gconv_fcts_c; |
6e606fad RM |
205 | } |
206 | else | |
93ec1cf0 | 207 | data->fcts = new_fcts; |
4bca4c17 UD |
208 | } |
209 | ||
91e32540 | 210 | __libc_rwlock_unlock (__libc_setlocale_lock); |
4bca4c17 | 211 | } |
d64b6ad0 UD |
212 | |
213 | ||
214 | /* Clone the current conversion function set. */ | |
215 | void | |
d64b6ad0 UD |
216 | __wcsmbs_clone_conv (struct gconv_fcts *copy) |
217 | { | |
96310297 | 218 | const struct gconv_fcts *orig; |
34e3c127 | 219 | |
96310297 | 220 | orig = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE)); |
d64b6ad0 UD |
221 | |
222 | /* Copy the data. */ | |
96310297 | 223 | *copy = *orig; |
d64b6ad0 | 224 | |
c9c15ac3 FW |
225 | /* Now increment the usage counters. Note: This assumes |
226 | copy->*_nsteps == 1. The current locale holds a reference, so it | |
227 | is still there after acquiring the lock. */ | |
228 | ||
229 | __libc_lock_lock (__gconv_lock); | |
230 | ||
231 | bool overflow = false; | |
d64b6ad0 | 232 | if (copy->towc->__shlib_handle != NULL) |
c9c15ac3 FW |
233 | overflow |= __builtin_add_overflow (copy->towc->__counter, 1, |
234 | ©->towc->__counter); | |
d64b6ad0 | 235 | if (copy->tomb->__shlib_handle != NULL) |
c9c15ac3 FW |
236 | overflow |= __builtin_add_overflow (copy->tomb->__counter, 1, |
237 | ©->tomb->__counter); | |
238 | ||
239 | __libc_lock_unlock (__gconv_lock); | |
240 | ||
241 | if (overflow) | |
242 | __libc_fatal ("\ | |
243 | Fatal glibc error: gconv module reference counter overflow\n"); | |
d64b6ad0 | 244 | } |
4e2e9999 UD |
245 | |
246 | ||
129d706d | 247 | /* Get converters for named charset. */ |
4e2e9999 | 248 | int |
4e2e9999 UD |
249 | __wcsmbs_named_conv (struct gconv_fcts *copy, const char *name) |
250 | { | |
01beb5b9 | 251 | copy->towc = __wcsmbs_getfct ("INTERNAL", name, ©->towc_nsteps); |
db6af3eb UD |
252 | if (copy->towc == NULL) |
253 | return 1; | |
254 | ||
255 | copy->tomb = __wcsmbs_getfct (name, "INTERNAL", ©->tomb_nsteps); | |
256 | if (copy->tomb == NULL) | |
4e2e9999 | 257 | { |
db6af3eb | 258 | __gconv_close_transform (copy->towc, copy->towc_nsteps); |
db6af3eb | 259 | return 1; |
4e2e9999 UD |
260 | } |
261 | ||
db6af3eb | 262 | return 0; |
129d706d | 263 | } |
4e2e9999 | 264 | |
d7ccc6c9 | 265 | void |
f095bb72 | 266 | _nl_cleanup_ctype (struct __locale_data *locale) |
129d706d | 267 | { |
93ec1cf0 FW |
268 | struct lc_ctype_data *data = locale->private; |
269 | if (data->fcts != NULL && data->fcts != &__wcsmbs_gconv_fcts_c) | |
129d706d | 270 | { |
96310297 | 271 | /* Free the old conversions. */ |
93ec1cf0 FW |
272 | __gconv_close_transform (data->fcts->tomb, data->fcts->tomb_nsteps); |
273 | __gconv_close_transform (data->fcts->towc, data->fcts->towc_nsteps); | |
274 | ||
275 | free ((void *) data->fcts); | |
276 | data->fcts = NULL; | |
277 | /* data itself is allocated within locale. */ | |
129d706d | 278 | } |
4e2e9999 | 279 | } |