1 | /* Copyright (C) 1998-2024 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
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. |
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 |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <ctype.h> |
19 | #include <langinfo.h> |
20 | #include <limits.h> |
21 | #include <stdlib.h> |
22 | #include <stdio.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 (toset: to, fromset: from, handle: &result, nsteps: &nsteps, flags: 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 (steps: 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 (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 | __wcsmbs_load_conv (struct __locale_data *new_category) |
152 | { |
153 | struct lc_ctype_data *data = new_category->private; |
154 | |
155 | /* Acquire the lock. */ |
156 | __libc_rwlock_wrlock (__libc_setlocale_lock); |
157 | |
158 | /* We should repeat the test since while we waited some other thread |
159 | might have run this function. */ |
160 | if (__glibc_likely (data->fcts == NULL)) |
161 | { |
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. */ |
169 | new_fcts = calloc (nmemb: 1, size: sizeof *new_fcts); |
170 | if (new_fcts == NULL) |
171 | goto failed; |
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, |
182 | use_translit ? "TRANSLIT" : "" ); |
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 (to: "INTERNAL" , from: complete_name, |
188 | nstepsp: &new_fcts->towc_nsteps); |
189 | if (new_fcts->towc != NULL) |
190 | new_fcts->tomb = __wcsmbs_getfct (to: complete_name, from: "INTERNAL" , |
191 | nstepsp: &new_fcts->tomb_nsteps); |
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 |
195 | forth. NB: NEW_FCTS was allocated with calloc. */ |
196 | if (new_fcts->tomb == NULL) |
197 | { |
198 | if (new_fcts->towc != NULL) |
199 | __gconv_close_transform (steps: new_fcts->towc, nsteps: new_fcts->towc_nsteps); |
200 | |
201 | free (ptr: new_fcts); |
202 | |
203 | failed: |
204 | data->fcts = (void *) &__wcsmbs_gconv_fcts_c; |
205 | } |
206 | else |
207 | data->fcts = new_fcts; |
208 | } |
209 | |
210 | __libc_rwlock_unlock (__libc_setlocale_lock); |
211 | } |
212 | |
213 | |
214 | /* Clone the current conversion function set. */ |
215 | void |
216 | __wcsmbs_clone_conv (struct gconv_fcts *copy) |
217 | { |
218 | const struct gconv_fcts *orig; |
219 | |
220 | orig = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE)); |
221 | |
222 | /* Copy the data. */ |
223 | *copy = *orig; |
224 | |
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; |
232 | if (copy->towc->__shlib_handle != NULL) |
233 | overflow |= __builtin_add_overflow (copy->towc->__counter, 1, |
234 | ©->towc->__counter); |
235 | if (copy->tomb->__shlib_handle != NULL) |
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" ); |
244 | } |
245 | |
246 | |
247 | /* Get converters for named charset. */ |
248 | int |
249 | __wcsmbs_named_conv (struct gconv_fcts *copy, const char *name) |
250 | { |
251 | copy->towc = __wcsmbs_getfct (to: "INTERNAL" , from: name, nstepsp: ©->towc_nsteps); |
252 | if (copy->towc == NULL) |
253 | return 1; |
254 | |
255 | copy->tomb = __wcsmbs_getfct (to: name, from: "INTERNAL" , nstepsp: ©->tomb_nsteps); |
256 | if (copy->tomb == NULL) |
257 | { |
258 | __gconv_close_transform (steps: copy->towc, nsteps: copy->towc_nsteps); |
259 | return 1; |
260 | } |
261 | |
262 | return 0; |
263 | } |
264 | |
265 | void |
266 | _nl_cleanup_ctype (struct __locale_data *locale) |
267 | { |
268 | struct lc_ctype_data *data = locale->private; |
269 | if (data->fcts != NULL && data->fcts != &__wcsmbs_gconv_fcts_c) |
270 | { |
271 | /* Free the old conversions. */ |
272 | __gconv_close_transform (steps: data->fcts->tomb, nsteps: data->fcts->tomb_nsteps); |
273 | __gconv_close_transform (steps: data->fcts->towc, nsteps: data->fcts->towc_nsteps); |
274 | |
275 | free (ptr: (void *) data->fcts); |
276 | data->fcts = NULL; |
277 | /* data itself is allocated within locale. */ |
278 | } |
279 | } |
280 | |