1 /* Copyright (C) 1995-2022 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 || defined HAVE_ARGZ_H
31 # include <argz.h>
32 #endif
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <stdlib.h>
36
37 #include "loadinfo.h"
38
39 /* On some strange systems still no definition of NULL is found. Sigh! */
40 #ifndef NULL
41 # if defined __STDC__ && __STDC__
42 # define NULL ((void *) 0)
43 # else
44 # define NULL 0
45 # endif
46 #endif
47
48 /* @@ end of prolog @@ */
49
50 #ifdef _LIBC
51 /* Rename the non ANSI C functions. This is required by the standard
52 because some ANSI C functions will require linking with this object
53 file and the name space must not be polluted. */
54 # ifndef stpcpy
55 # define stpcpy(dest, src) __stpcpy(dest, src)
56 # endif
57 #else
58 # ifndef HAVE_STPCPY
59 static char *stpcpy (char *dest, const char *src);
60 # endif
61 #endif
62
63 /* Define function which are usually not available. */
64
65 #if defined HAVE_ARGZ_COUNT
66 # undef __argz_count
67 # define __argz_count argz_count
68 #else
69 /* Returns the number of strings in ARGZ. */
70 static size_t
argz_count__(const char * argz,size_t len)71 argz_count__ (const char *argz, size_t len)
72 {
73 size_t count = 0;
74 while (len > 0)
75 {
76 size_t part_len = strlen (argz);
77 argz += part_len + 1;
78 len -= part_len + 1;
79 count++;
80 }
81 return count;
82 }
83 # undef __argz_count
84 # define __argz_count(argz, len) argz_count__ (argz, len)
85 #endif /* !_LIBC && !HAVE_ARGZ_COUNT */
86
87 #if defined HAVE_ARGZ_STRINGIFY
88 # undef __argz_stringify
89 # define __argz_stringify argz_stringify
90 #else
91 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
92 except the last into the character SEP. */
93 static void
argz_stringify__(char * argz,size_t len,int sep)94 argz_stringify__ (char *argz, size_t len, int sep)
95 {
96 while (len > 0)
97 {
98 size_t part_len = strlen (argz);
99 argz += part_len;
100 len -= part_len + 1;
101 if (len > 0)
102 *argz++ = sep;
103 }
104 }
105 # undef __argz_stringify
106 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
107 #endif /* !_LIBC && !HAVE_ARGZ_STRINGIFY */
108
109 #ifdef _LIBC
110 #elif defined HAVE_ARGZ_NEXT
111 # undef __argz_next
112 # define __argz_next argz_next
113 #else
114 static char *
argz_next__(char * argz,size_t argz_len,const char * entry)115 argz_next__ (char *argz, size_t argz_len, const char *entry)
116 {
117 if (entry)
118 {
119 if (entry < argz + argz_len)
120 entry = strchr (entry, '\0') + 1;
121
122 return entry >= argz + argz_len ? NULL : (char *) entry;
123 }
124 else
125 if (argz_len > 0)
126 return argz;
127 else
128 return 0;
129 }
130 # undef __argz_next
131 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
132 #endif /* !_LIBC && !HAVE_ARGZ_NEXT */
133
134 /* Return number of bits set in X. */
135 #ifndef ARCH_POP
136 static inline int
pop(int x)137 pop (int x)
138 {
139 /* We assume that no more than 16 bits are used. */
140 x = ((x & ~0x5555) >> 1) + (x & 0x5555);
141 x = ((x & ~0x3333) >> 2) + (x & 0x3333);
142 x = ((x >> 4) + x) & 0x0f0f;
143 x = ((x >> 8) + x) & 0xff;
144
145 return x;
146 }
147 #endif
148
149
150 struct loaded_l10nfile *
_nl_make_l10nflist(struct loaded_l10nfile ** l10nfile_list,const char * dirlist,size_t dirlist_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)151 _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
152 const char *dirlist, size_t dirlist_len,
153 int mask, const char *language, const char *territory,
154 const char *codeset, const char *normalized_codeset,
155 const char *modifier,
156 const char *filename, int do_allocate)
157 {
158 char *abs_filename;
159 struct loaded_l10nfile *last = NULL;
160 struct loaded_l10nfile *retval;
161 char *cp;
162 size_t entries;
163 int cnt;
164
165 /* Allocate room for the full file name. */
166 abs_filename = (char *) malloc (dirlist_len
167 + strlen (language)
168 + ((mask & XPG_TERRITORY) != 0
169 ? strlen (territory) + 1 : 0)
170 + ((mask & XPG_CODESET) != 0
171 ? strlen (codeset) + 1 : 0)
172 + ((mask & XPG_NORM_CODESET) != 0
173 ? strlen (normalized_codeset) + 1 : 0)
174 + ((mask & XPG_MODIFIER) != 0
175 ? strlen (modifier) + 1 : 0)
176 + 1 + strlen (filename) + 1);
177
178 if (abs_filename == NULL)
179 return NULL;
180
181 retval = NULL;
182 last = NULL;
183
184 /* Construct file name. */
185 memcpy (abs_filename, dirlist, dirlist_len);
186 __argz_stringify (abs_filename, dirlist_len, ':');
187 cp = abs_filename + (dirlist_len - 1);
188 *cp++ = '/';
189 cp = stpcpy (cp, language);
190
191 if ((mask & XPG_TERRITORY) != 0)
192 {
193 *cp++ = '_';
194 cp = stpcpy (cp, territory);
195 }
196 if ((mask & XPG_CODESET) != 0)
197 {
198 *cp++ = '.';
199 cp = stpcpy (cp, codeset);
200 }
201 if ((mask & XPG_NORM_CODESET) != 0)
202 {
203 *cp++ = '.';
204 cp = stpcpy (cp, normalized_codeset);
205 }
206 if ((mask & XPG_MODIFIER) != 0)
207 {
208 *cp++ = '@';
209 cp = stpcpy (cp, modifier);
210 }
211
212 *cp++ = '/';
213 stpcpy (cp, filename);
214
215 /* Look in list of already loaded domains whether it is already
216 available. */
217 last = NULL;
218 for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
219 if (retval->filename != NULL)
220 {
221 int compare = strcmp (retval->filename, abs_filename);
222 if (compare == 0)
223 /* We found it! */
224 break;
225 if (compare < 0)
226 {
227 /* It's not in the list. */
228 retval = NULL;
229 break;
230 }
231
232 last = retval;
233 }
234
235 if (retval != NULL || do_allocate == 0)
236 {
237 free (abs_filename);
238 return retval;
239 }
240
241 retval = (struct loaded_l10nfile *)
242 malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
243 * (1 << pop (mask))
244 * sizeof (struct loaded_l10nfile *)));
245 if (retval == NULL)
246 {
247 free (abs_filename);
248 return NULL;
249 }
250
251 retval->filename = abs_filename;
252 /* If more than one directory is in the list this is a pseudo-entry
253 which just references others. We do not try to load data for it,
254 ever. */
255 retval->decided = (__argz_count (dirlist, dirlist_len) != 1
256 || ((mask & XPG_CODESET) != 0
257 && (mask & XPG_NORM_CODESET) != 0));
258 retval->data = NULL;
259
260 if (last == NULL)
261 {
262 retval->next = *l10nfile_list;
263 *l10nfile_list = retval;
264 }
265 else
266 {
267 retval->next = last->next;
268 last->next = retval;
269 }
270
271 entries = 0;
272 /* If the DIRLIST is a real list the RETVAL entry corresponds not to
273 a real file. So we have to use the DIRLIST separation mechanism
274 of the inner loop. */
275 cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
276 for (; cnt >= 0; --cnt)
277 if ((cnt & ~mask) == 0)
278 {
279 /* Iterate over all elements of the DIRLIST. */
280 char *dir = NULL;
281
282 while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
283 != NULL)
284 retval->successor[entries++]
285 = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
286 language, territory, codeset,
287 normalized_codeset, modifier, filename, 1);
288 }
289 retval->successor[entries] = NULL;
290
291 return retval;
292 }
293
294 /* Normalize codeset name. There is no standard for the codeset
295 names. Normalization allows the user to use any of the common
296 names. The return value is dynamically allocated and has to be
297 freed by the caller. */
298 const char *
_nl_normalize_codeset(const char * codeset,size_t name_len)299 _nl_normalize_codeset (const char *codeset, size_t name_len)
300 {
301 size_t len = 0;
302 int only_digit = 1;
303 char *retval;
304 char *wp;
305 size_t cnt;
306 #if !IS_IN (libc)
307 locale_t locale = newlocale (0, "C", NULL);
308 #else
309 # define locale _nl_C_locobj_ptr
310 #endif
311
312 for (cnt = 0; cnt < name_len; ++cnt)
313 if (__isalnum_l ((unsigned char) codeset[cnt], locale))
314 {
315 ++len;
316
317 if (! __isdigit_l ((unsigned char) codeset[cnt], locale))
318 only_digit = 0;
319 }
320
321 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
322
323 if (retval != NULL)
324 {
325 if (only_digit)
326 wp = stpcpy (retval, "iso");
327 else
328 wp = retval;
329
330 for (cnt = 0; cnt < name_len; ++cnt)
331 if (__isalpha_l ((unsigned char) codeset[cnt], locale))
332 *wp++ = __tolower_l ((unsigned char) codeset[cnt], locale);
333 else if (__isdigit_l ((unsigned char) codeset[cnt], locale))
334 *wp++ = codeset[cnt];
335
336 *wp = '\0';
337 }
338
339 return (const char *) retval;
340 }
341
342
343 /* @@ begin of epilog @@ */
344
345 /* We don't want libintl.a to depend on any other library. So we
346 avoid the non-standard function stpcpy. In GNU C Library this
347 function is available, though. Also allow the symbol HAVE_STPCPY
348 to be defined. */
349 #if !_LIBC && !HAVE_STPCPY
350 static char *
stpcpy(char * dest,const char * src)351 stpcpy (char *dest, const char *src)
352 {
353 while ((*dest++ = *src++) != '\0')
354 /* Do nothing. */ ;
355 return dest - 1;
356 }
357 #endif
358