1 | /* Copyright (C) 1998-2022 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 (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 (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 | /* Acquire the lock. */ |
154 | __libc_rwlock_wrlock (__libc_setlocale_lock); |
155 | |
156 | /* We should repeat the test since while we waited some other thread |
157 | might have run this function. */ |
158 | if (__glibc_likely (new_category->private.ctype == NULL)) |
159 | { |
160 | /* We must find the real functions. */ |
161 | const char *charset_name; |
162 | const char *complete_name; |
163 | struct gconv_fcts *new_fcts; |
164 | int use_translit; |
165 | |
166 | /* Allocate the gconv_fcts structure. */ |
167 | new_fcts = calloc (1, sizeof *new_fcts); |
168 | if (new_fcts == NULL) |
169 | goto failed; |
170 | |
171 | /* Get name of charset of the locale. */ |
172 | charset_name = new_category->values[_NL_ITEM_INDEX(CODESET)].string; |
173 | |
174 | /* Does the user want transliteration? */ |
175 | use_translit = new_category->use_translit; |
176 | |
177 | /* Normalize the name and add the slashes necessary for a |
178 | complete lookup. */ |
179 | complete_name = norm_add_slashes (charset_name, |
180 | use_translit ? "TRANSLIT" : "" ); |
181 | |
182 | /* It is not necessary to use transliteration in this direction |
183 | since the internal character set is supposed to be able to |
184 | represent all others. */ |
185 | new_fcts->towc = __wcsmbs_getfct ("INTERNAL" , complete_name, |
186 | &new_fcts->towc_nsteps); |
187 | if (new_fcts->towc != NULL) |
188 | new_fcts->tomb = __wcsmbs_getfct (complete_name, "INTERNAL" , |
189 | &new_fcts->tomb_nsteps); |
190 | |
191 | /* If any of the conversion functions is not available we don't |
192 | use any since this would mean we cannot convert back and |
193 | forth. NB: NEW_FCTS was allocated with calloc. */ |
194 | if (new_fcts->tomb == NULL) |
195 | { |
196 | if (new_fcts->towc != NULL) |
197 | __gconv_close_transform (new_fcts->towc, new_fcts->towc_nsteps); |
198 | |
199 | free (new_fcts); |
200 | |
201 | failed: |
202 | new_category->private.ctype = &__wcsmbs_gconv_fcts_c; |
203 | } |
204 | else |
205 | { |
206 | new_category->private.ctype = new_fcts; |
207 | new_category->private.cleanup = &_nl_cleanup_ctype; |
208 | } |
209 | } |
210 | |
211 | __libc_rwlock_unlock (__libc_setlocale_lock); |
212 | } |
213 | |
214 | |
215 | /* Clone the current conversion function set. */ |
216 | void |
217 | __wcsmbs_clone_conv (struct gconv_fcts *copy) |
218 | { |
219 | const struct gconv_fcts *orig; |
220 | |
221 | orig = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE)); |
222 | |
223 | /* Copy the data. */ |
224 | *copy = *orig; |
225 | |
226 | /* Now increment the usage counters. Note: This assumes |
227 | copy->*_nsteps == 1. The current locale holds a reference, so it |
228 | is still there after acquiring the lock. */ |
229 | |
230 | __libc_lock_lock (__gconv_lock); |
231 | |
232 | bool overflow = false; |
233 | if (copy->towc->__shlib_handle != NULL) |
234 | overflow |= __builtin_add_overflow (copy->towc->__counter, 1, |
235 | ©->towc->__counter); |
236 | if (copy->tomb->__shlib_handle != NULL) |
237 | overflow |= __builtin_add_overflow (copy->tomb->__counter, 1, |
238 | ©->tomb->__counter); |
239 | |
240 | __libc_lock_unlock (__gconv_lock); |
241 | |
242 | if (overflow) |
243 | __libc_fatal ("\ |
244 | Fatal glibc error: gconv module reference counter overflow\n" ); |
245 | } |
246 | |
247 | |
248 | /* Get converters for named charset. */ |
249 | int |
250 | __wcsmbs_named_conv (struct gconv_fcts *copy, const char *name) |
251 | { |
252 | copy->towc = __wcsmbs_getfct ("INTERNAL" , name, ©->towc_nsteps); |
253 | if (copy->towc == NULL) |
254 | return 1; |
255 | |
256 | copy->tomb = __wcsmbs_getfct (name, "INTERNAL" , ©->tomb_nsteps); |
257 | if (copy->tomb == NULL) |
258 | { |
259 | __gconv_close_transform (copy->towc, copy->towc_nsteps); |
260 | return 1; |
261 | } |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | void |
267 | _nl_cleanup_ctype (struct __locale_data *locale) |
268 | { |
269 | const struct gconv_fcts *const data = locale->private.ctype; |
270 | if (data != NULL) |
271 | { |
272 | locale->private.ctype = NULL; |
273 | locale->private.cleanup = NULL; |
274 | |
275 | /* Free the old conversions. */ |
276 | __gconv_close_transform (data->tomb, data->tomb_nsteps); |
277 | __gconv_close_transform (data->towc, data->towc_nsteps); |
278 | free ((char *) data); |
279 | } |
280 | } |
281 | |