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