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