1 /* Copyright (C) 1995-2020 Free Software Foundation, Inc.
2 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8
9 This program 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
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /* Tell glibc's <string.h> to provide a prototype for stpcpy().
18 This must come before <config.h> because <config.h> may include
19 <features.h>, and once <features.h> has been included, it's too late. */
20 #ifndef _GNU_SOURCE
21 # define _GNU_SOURCE 1
22 #endif
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <string.h>
29
30 #if defined _LIBC
31 # include <argz.h>
32 #endif
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <stdlib.h>
36 #if defined _WIN32 && !defined __CYGWIN__
37 # include <wchar.h>
38 #endif
39
40 #include "loadinfo.h"
41
42 /* On some strange systems still no definition of NULL is found. Sigh! */
43 #ifndef NULL
44 # if defined __STDC__ && __STDC__
45 # define NULL ((void *) 0)
46 # else
47 # define NULL 0
48 # endif
49 #endif
50
51 /* @@ end of prolog @@ */
52
53 #ifdef _LIBC
54 /* Rename the non ANSI C functions. This is required by the standard
55 because some ANSI C functions will require linking with this object
56 file and the name space must not be polluted. */
57 # ifndef stpcpy
58 # define stpcpy(dest, src) __stpcpy(dest, src)
59 # endif
60 #else
61 # ifndef HAVE_STPCPY
62 static char *stpcpy (char *dest, const char *src);
63 # endif
64 #endif
65
66 #ifdef _LIBC
67 # define IS_ABSOLUTE_FILE_NAME(P) ((P)[0] == '/')
68 # define IS_RELATIVE_FILE_NAME(P) (! IS_ABSOLUTE_FILE_NAME (P))
69 #else
70 # include "filename.h"
71 #endif
72
73 /* Return number of bits set in X. */
74 #ifndef ARCH_POP
75 static inline int
pop(int x)76 pop (int x)
77 {
78 /* We assume that no more than 16 bits are used. */
79 x = ((x & ~0x5555) >> 1) + (x & 0x5555);
80 x = ((x & ~0x3333) >> 2) + (x & 0x3333);
81 x = ((x >> 4) + x) & 0x0f0f;
82 x = ((x >> 8) + x) & 0xff;
83
84 return x;
85 }
86 #endif
87
88
89 struct loaded_l10nfile *
_nl_make_l10nflist(struct loaded_l10nfile ** l10nfile_list,const char * dirlist,size_t dirlist_len,const wchar_t * wdirlist,size_t wdirlist_len,int mask,const char * language,const char * territory,const char * codeset,const char * normalized_codeset,const char * modifier,const char * filename,int do_allocate)90 _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
91 const char *dirlist, size_t dirlist_len,
92 #if defined _WIN32 && !defined __CYGWIN__
93 const wchar_t *wdirlist, size_t wdirlist_len,
94 #endif
95 int mask, const char *language, const char *territory,
96 const char *codeset, const char *normalized_codeset,
97 const char *modifier,
98 const char *filename, int do_allocate)
99 {
100 char *abs_filename;
101 #if defined _WIN32 && !defined __CYGWIN__
102 wchar_t *abs_wfilename;
103 #endif
104 struct loaded_l10nfile **lastp;
105 struct loaded_l10nfile *retval;
106 size_t dirlist_count;
107 size_t entries;
108 int cnt;
109
110 /* If LANGUAGE contains an absolute directory specification, we ignore
111 DIRLIST and WDIRLIST. */
112 if (!IS_RELATIVE_FILE_NAME (language))
113 {
114 dirlist_len = 0;
115 #if defined _WIN32 && !defined __CYGWIN__
116 wdirlist_len = 0;
117 #endif
118 }
119
120 /* Allocate room for the full file name. */
121 abs_filename = (char *) malloc (dirlist_len
122 + strlen (language)
123 + ((mask & XPG_TERRITORY) != 0
124 ? strlen (territory) + 1 : 0)
125 + ((mask & XPG_CODESET) != 0
126 ? strlen (codeset) + 1 : 0)
127 + ((mask & XPG_NORM_CODESET) != 0
128 ? strlen (normalized_codeset) + 1 : 0)
129 + ((mask & XPG_MODIFIER) != 0
130 ? strlen (modifier) + 1 : 0)
131 + 1 + strlen (filename) + 1);
132
133 if (abs_filename == NULL)
134 return NULL;
135
136 /* Construct file name. */
137 {
138 char *cp;
139
140 cp = abs_filename;
141 if (dirlist_len > 0)
142 {
143 memcpy (cp, dirlist, dirlist_len);
144 #ifdef _LIBC
145 __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
146 #endif
147 cp += dirlist_len;
148 cp[-1] = '/';
149 }
150
151 cp = stpcpy (cp, language);
152
153 if ((mask & XPG_TERRITORY) != 0)
154 {
155 *cp++ = '_';
156 cp = stpcpy (cp, territory);
157 }
158 if ((mask & XPG_CODESET) != 0)
159 {
160 *cp++ = '.';
161 cp = stpcpy (cp, codeset);
162 }
163 if ((mask & XPG_NORM_CODESET) != 0)
164 {
165 *cp++ = '.';
166 cp = stpcpy (cp, normalized_codeset);
167 }
168 if ((mask & XPG_MODIFIER) != 0)
169 {
170 *cp++ = '@';
171 cp = stpcpy (cp, modifier);
172 }
173
174 *cp++ = '/';
175 stpcpy (cp, filename);
176 }
177
178 #if defined _WIN32 && !defined __CYGWIN__
179 /* Construct wide-char file name. */
180 if (wdirlist_len > 0)
181 {
182 /* Since dirlist_len == 0, just concatenate wdirlist and abs_filename. */
183 /* An upper bound for wcslen (mbstowcs (abs_filename)). */
184 size_t abs_filename_bound = mbstowcs (NULL, abs_filename, 0);
185 if (abs_filename_bound == (size_t)-1)
186 {
187 free (abs_filename);
188 return NULL;
189 }
190
191 /* Allocate and fill abs_wfilename. */
192 abs_wfilename =
193 (wchar_t *)
194 malloc ((wdirlist_len + abs_filename_bound + 1) * sizeof (wchar_t));
195 if (abs_wfilename == NULL)
196 {
197 free (abs_filename);
198 return NULL;
199 }
200 wmemcpy (abs_wfilename, wdirlist, wdirlist_len - 1);
201 abs_wfilename[wdirlist_len - 1] = L'/';
202 if (mbstowcs (abs_wfilename + wdirlist_len, abs_filename,
203 abs_filename_bound + 1)
204 > abs_filename_bound)
205 {
206 free (abs_filename);
207 free (abs_wfilename);
208 return NULL;
209 }
210
211 free (abs_filename);
212 abs_filename = NULL;
213 }
214 else
215 abs_wfilename = NULL;
216 #endif
217
218 /* Look in list of already loaded domains whether it is already
219 available. */
220 lastp = l10nfile_list;
221 for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
222 if (retval->filename != NULL
223 #if defined _WIN32 && !defined __CYGWIN__
224 || retval->wfilename != NULL
225 #endif
226 )
227 {
228 int compare =
229 #if defined _WIN32 && !defined __CYGWIN__
230 abs_wfilename != NULL
231 ? retval->wfilename != NULL && wcscmp (retval->wfilename, abs_wfilename)
232 : retval->filename != NULL && strcmp (retval->filename, abs_filename);
233 #else
234 strcmp (retval->filename, abs_filename);
235 #endif
236 if (compare == 0)
237 /* We found it! */
238 break;
239 if (compare < 0)
240 {
241 /* It's not in the list. */
242 retval = NULL;
243 break;
244 }
245
246 lastp = &retval->next;
247 }
248
249 if (retval != NULL || do_allocate == 0)
250 {
251 free (abs_filename);
252 #if defined _WIN32 && !defined __CYGWIN__
253 free (abs_wfilename);
254 #endif
255 return retval;
256 }
257
258 #ifdef _LIBC
259 dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
260 #else
261 dirlist_count = 1;
262 #endif
263
264 /* Allocate a new loaded_l10nfile. */
265 retval =
266 (struct loaded_l10nfile *)
267 malloc (sizeof (*retval)
268 + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
269 * sizeof (struct loaded_l10nfile *)));
270 if (retval == NULL)
271 {
272 free (abs_filename);
273 #if defined _WIN32 && !defined __CYGWIN__
274 free (abs_wfilename);
275 #endif
276 return NULL;
277 }
278
279 retval->filename = abs_filename;
280 #if defined _WIN32 && !defined __CYGWIN__
281 retval->wfilename = abs_wfilename;
282 #endif
283
284 /* We set retval->data to NULL here; it is filled in later.
285 Setting retval->decided to 1 here means that retval does not
286 correspond to a real file (dirlist_count > 1) or is not worth
287 looking up (if an unnormalized codeset was specified). */
288 retval->decided = (dirlist_count > 1
289 || ((mask & XPG_CODESET) != 0
290 && (mask & XPG_NORM_CODESET) != 0));
291 retval->data = NULL;
292
293 retval->next = *lastp;
294 *lastp = retval;
295
296 entries = 0;
297 /* Recurse to fill the inheritance list of RETVAL.
298 If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
299 entry does not correspond to a real file; retval->filename contains
300 colons. In this case we loop across all elements of DIRLIST and
301 across all bit patterns dominated by MASK.
302 If the DIRLIST is a single directory or entirely redundant (i.e.
303 DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
304 MASK, excluding MASK itself.
305 In either case, we loop down from MASK to 0. This has the effect
306 that the extra bits in the locale name are dropped in this order:
307 first the modifier, then the territory, then the codeset, then the
308 normalized_codeset. */
309 for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
310 if ((cnt & ~mask) == 0
311 && !((cnt & XPG_CODESET) != 0 && (cnt & XPG_NORM_CODESET) != 0))
312 {
313 #ifdef _LIBC
314 if (dirlist_count > 1)
315 {
316 /* Iterate over all elements of the DIRLIST. */
317 char *dir = NULL;
318
319 while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
320 != NULL)
321 retval->successor[entries++]
322 = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
323 cnt, language, territory, codeset,
324 normalized_codeset, modifier, filename,
325 1);
326 }
327 else
328 #endif
329 retval->successor[entries++]
330 = _nl_make_l10nflist (l10nfile_list,
331 dirlist, dirlist_len,
332 #if defined _WIN32 && !defined __CYGWIN__
333 wdirlist, wdirlist_len,
334 #endif
335 cnt, language, territory, codeset,
336 normalized_codeset, modifier, filename, 1);
337 }
338 retval->successor[entries] = NULL;
339
340 return retval;
341 }
342
343 /* Normalize codeset name. There is no standard for the codeset
344 names. Normalization allows the user to use any of the common
345 names. The return value is dynamically allocated and has to be
346 freed by the caller. */
347 const char *
_nl_normalize_codeset(const char * codeset,size_t name_len)348 _nl_normalize_codeset (const char *codeset, size_t name_len)
349 {
350 size_t len = 0;
351 int only_digit = 1;
352 char *retval;
353 char *wp;
354 size_t cnt;
355
356 for (cnt = 0; cnt < name_len; ++cnt)
357 if (isalnum ((unsigned char) codeset[cnt]))
358 {
359 ++len;
360
361 if (isalpha ((unsigned char) codeset[cnt]))
362 only_digit = 0;
363 }
364
365 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
366
367 if (retval != NULL)
368 {
369 if (only_digit)
370 wp = stpcpy (retval, "iso");
371 else
372 wp = retval;
373
374 for (cnt = 0; cnt < name_len; ++cnt)
375 if (isalpha ((unsigned char) codeset[cnt]))
376 *wp++ = tolower ((unsigned char) codeset[cnt]);
377 else if (isdigit ((unsigned char) codeset[cnt]))
378 *wp++ = codeset[cnt];
379
380 *wp = '\0';
381 }
382
383 return (const char *) retval;
384 }
385
386
387 /* @@ begin of epilog @@ */
388
389 /* We don't want libintl.a to depend on any other library. So we
390 avoid the non-standard function stpcpy. In GNU C Library this
391 function is available, though. Also allow the symbol HAVE_STPCPY
392 to be defined. */
393 #if !_LIBC && !HAVE_STPCPY
394 static char *
stpcpy(char * dest,const char * src)395 stpcpy (char *dest, const char *src)
396 {
397 while ((*dest++ = *src++) != '\0')
398 /* Do nothing. */ ;
399 return dest - 1;
400 }
401 #endif
402