1 /* Copyright (C) 1998-2021 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 *
__wcsmbs_getfct(const char * to,const char * from,size_t * nstepsp)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 extract_charset_name(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.  */
__libc_rwlock_define(extern,__libc_setlocale_lock attribute_hidden)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
__wcsmbs_clone_conv(struct gconv_fcts * copy)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 					&copy->towc->__counter);
236   if (copy->tomb->__shlib_handle != NULL)
237     overflow |= __builtin_add_overflow (copy->tomb->__counter, 1,
238 					&copy->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
__wcsmbs_named_conv(struct gconv_fcts * copy,const char * name)250 __wcsmbs_named_conv (struct gconv_fcts *copy, const char *name)
251 {
252   copy->towc = __wcsmbs_getfct ("INTERNAL", name, &copy->towc_nsteps);
253   if (copy->towc == NULL)
254     return 1;
255 
256   copy->tomb = __wcsmbs_getfct (name, "INTERNAL", &copy->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
_nl_cleanup_ctype(struct __locale_data * locale)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