1 | /* Copyright (C) 1998-2021 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 | |