1 /* Copyright (C) 1995-1999, 2000-2007 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 <http://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 /* Pathname support.
64 ISSLASH(C) tests whether C is a directory separator character.
65 IS_ABSOLUTE_PATH(P) tests whether P is an absolute path. If it is not,
66 it may be concatenated to a directory pathname.
67 */
68 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
69 /* Win32, Cygwin, OS/2, DOS */
70 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
71 # define HAS_DEVICE(P) \
72 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
73 && (P)[1] == ':')
74 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
75 #else
76 /* Unix */
77 # define ISSLASH(C) ((C) == '/')
78 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
79 #endif
80
81 /* Define function which are usually not available. */
82
83 #ifdef _LIBC
84 # define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
85 #elif defined HAVE_ARGZ_COUNT
86 # undef __argz_count
87 # define __argz_count argz_count
88 #else
89 /* Returns the number of strings in ARGZ. */
90 static size_t
argz_count__(const char * argz,size_t len)91 argz_count__ (const char *argz, size_t len)
92 {
93 size_t count = 0;
94 while (len > 0)
95 {
96 size_t part_len = strlen (argz);
97 argz += part_len + 1;
98 len -= part_len + 1;
99 count++;
100 }
101 return count;
102 }
103 # undef __argz_count
104 # define __argz_count(argz, len) argz_count__ (argz, len)
105 #endif /* !_LIBC && !HAVE_ARGZ_COUNT */
106
107 #ifdef _LIBC
108 # define __argz_stringify(argz, len, sep) \
109 INTUSE(__argz_stringify) (argz, len, sep)
110 #elif defined HAVE_ARGZ_STRINGIFY
111 # undef __argz_stringify
112 # define __argz_stringify argz_stringify
113 #else
114 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
115 except the last into the character SEP. */
116 static void
argz_stringify__(char * argz,size_t len,int sep)117 argz_stringify__ (char *argz, size_t len, int sep)
118 {
119 while (len > 0)
120 {
121 size_t part_len = strlen (argz);
122 argz += part_len;
123 len -= part_len + 1;
124 if (len > 0)
125 *argz++ = sep;
126 }
127 }
128 # undef __argz_stringify
129 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
130 #endif /* !_LIBC && !HAVE_ARGZ_STRINGIFY */
131
132 #ifdef _LIBC
133 #elif defined HAVE_ARGZ_NEXT
134 # undef __argz_next
135 # define __argz_next argz_next
136 #else
137 static char *
argz_next__(char * argz,size_t argz_len,const char * entry)138 argz_next__ (char *argz, size_t argz_len, const char *entry)
139 {
140 if (entry)
141 {
142 if (entry < argz + argz_len)
143 entry = strchr (entry, '\0') + 1;
144
145 return entry >= argz + argz_len ? NULL : (char *) entry;
146 }
147 else
148 if (argz_len > 0)
149 return argz;
150 else
151 return 0;
152 }
153 # undef __argz_next
154 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
155 #endif /* !_LIBC && !HAVE_ARGZ_NEXT */
156
157
158 /* Return number of bits set in X. */
159 static inline int
pop(int x)160 pop (int x)
161 {
162 /* We assume that no more than 16 bits are used. */
163 x = ((x & ~0x5555) >> 1) + (x & 0x5555);
164 x = ((x & ~0x3333) >> 2) + (x & 0x3333);
165 x = ((x >> 4) + x) & 0x0f0f;
166 x = ((x >> 8) + x) & 0xff;
167
168 return x;
169 }
170
171
172 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)173 _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
174 const char *dirlist, size_t dirlist_len,
175 int mask, const char *language, const char *territory,
176 const char *codeset, const char *normalized_codeset,
177 const char *modifier,
178 const char *filename, int do_allocate)
179 {
180 char *abs_filename;
181 struct loaded_l10nfile **lastp;
182 struct loaded_l10nfile *retval;
183 char *cp;
184 size_t dirlist_count;
185 size_t entries;
186 int cnt;
187
188 /* If LANGUAGE contains an absolute directory specification, we ignore
189 DIRLIST. */
190 if (IS_ABSOLUTE_PATH (language))
191 dirlist_len = 0;
192
193 /* Allocate room for the full file name. */
194 abs_filename = (char *) malloc (dirlist_len
195 + strlen (language)
196 + ((mask & XPG_TERRITORY) != 0
197 ? strlen (territory) + 1 : 0)
198 + ((mask & XPG_CODESET) != 0
199 ? strlen (codeset) + 1 : 0)
200 + ((mask & XPG_NORM_CODESET) != 0
201 ? strlen (normalized_codeset) + 1 : 0)
202 + ((mask & XPG_MODIFIER) != 0
203 ? strlen (modifier) + 1 : 0)
204 + 1 + strlen (filename) + 1);
205
206 if (abs_filename == NULL)
207 return NULL;
208
209 /* Construct file name. */
210 cp = abs_filename;
211 if (dirlist_len > 0)
212 {
213 memcpy (cp, dirlist, dirlist_len);
214 __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
215 cp += dirlist_len;
216 cp[-1] = '/';
217 }
218
219 cp = stpcpy (cp, language);
220
221 if ((mask & XPG_TERRITORY) != 0)
222 {
223 *cp++ = '_';
224 cp = stpcpy (cp, territory);
225 }
226 if ((mask & XPG_CODESET) != 0)
227 {
228 *cp++ = '.';
229 cp = stpcpy (cp, codeset);
230 }
231 if ((mask & XPG_NORM_CODESET) != 0)
232 {
233 *cp++ = '.';
234 cp = stpcpy (cp, normalized_codeset);
235 }
236 if ((mask & XPG_MODIFIER) != 0)
237 {
238 *cp++ = '@';
239 cp = stpcpy (cp, modifier);
240 }
241
242 *cp++ = '/';
243 stpcpy (cp, filename);
244
245 /* Look in list of already loaded domains whether it is already
246 available. */
247 lastp = l10nfile_list;
248 for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
249 if (retval->filename != NULL)
250 {
251 int compare = strcmp (retval->filename, abs_filename);
252 if (compare == 0)
253 /* We found it! */
254 break;
255 if (compare < 0)
256 {
257 /* It's not in the list. */
258 retval = NULL;
259 break;
260 }
261
262 lastp = &retval->next;
263 }
264
265 if (retval != NULL || do_allocate == 0)
266 {
267 free (abs_filename);
268 return retval;
269 }
270
271 dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
272
273 /* Allocate a new loaded_l10nfile. */
274 retval =
275 (struct loaded_l10nfile *)
276 malloc (sizeof (*retval)
277 + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
278 * sizeof (struct loaded_l10nfile *)));
279 if (retval == NULL)
280 {
281 free (abs_filename);
282 return NULL;
283 }
284
285 retval->filename = abs_filename;
286
287 /* We set retval->data to NULL here; it is filled in later.
288 Setting retval->decided to 1 here means that retval does not
289 correspond to a real file (dirlist_count > 1) or is not worth
290 looking up (if an unnormalized codeset was specified). */
291 retval->decided = (dirlist_count > 1
292 || ((mask & XPG_CODESET) != 0
293 && (mask & XPG_NORM_CODESET) != 0));
294 retval->data = NULL;
295
296 retval->next = *lastp;
297 *lastp = retval;
298
299 entries = 0;
300 /* Recurse to fill the inheritance list of RETVAL.
301 If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
302 entry does not correspond to a real file; retval->filename contains
303 colons. In this case we loop across all elements of DIRLIST and
304 across all bit patterns dominated by MASK.
305 If the DIRLIST is a single directory or entirely redundant (i.e.
306 DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
307 MASK, excluding MASK itself.
308 In either case, we loop down from MASK to 0. This has the effect
309 that the extra bits in the locale name are dropped in this order:
310 first the modifier, then the territory, then the codeset, then the
311 normalized_codeset. */
312 for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
313 if ((cnt & ~mask) == 0
314 && !((cnt & XPG_CODESET) != 0 && (cnt & XPG_NORM_CODESET) != 0))
315 {
316 if (dirlist_count > 1)
317 {
318 /* Iterate over all elements of the DIRLIST. */
319 char *dir = NULL;
320
321 while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
322 != NULL)
323 retval->successor[entries++]
324 = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
325 cnt, language, territory, codeset,
326 normalized_codeset, modifier, filename,
327 1);
328 }
329 else
330 retval->successor[entries++]
331 = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
332 cnt, language, territory, codeset,
333 normalized_codeset, modifier, filename, 1);
334 }
335 retval->successor[entries] = NULL;
336
337 return retval;
338 }
339
340 /* Normalize codeset name. There is no standard for the codeset
341 names. Normalization allows the user to use any of the common
342 names. The return value is dynamically allocated and has to be
343 freed by the caller. */
344 const char *
_nl_normalize_codeset(const char * codeset,size_t name_len)345 _nl_normalize_codeset (const char *codeset, size_t name_len)
346 {
347 size_t len = 0;
348 int only_digit = 1;
349 char *retval;
350 char *wp;
351 size_t cnt;
352
353 for (cnt = 0; cnt < name_len; ++cnt)
354 if (isalnum ((unsigned char) codeset[cnt]))
355 {
356 ++len;
357
358 if (isalpha ((unsigned char) codeset[cnt]))
359 only_digit = 0;
360 }
361
362 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
363
364 if (retval != NULL)
365 {
366 if (only_digit)
367 wp = stpcpy (retval, "iso");
368 else
369 wp = retval;
370
371 for (cnt = 0; cnt < name_len; ++cnt)
372 if (isalpha ((unsigned char) codeset[cnt]))
373 *wp++ = tolower ((unsigned char) codeset[cnt]);
374 else if (isdigit ((unsigned char) codeset[cnt]))
375 *wp++ = codeset[cnt];
376
377 *wp = '\0';
378 }
379
380 return (const char *) retval;
381 }
382
383
384 /* @@ begin of epilog @@ */
385
386 /* We don't want libintl.a to depend on any other library. So we
387 avoid the non-standard function stpcpy. In GNU C Library this
388 function is available, though. Also allow the symbol HAVE_STPCPY
389 to be defined. */
390 #if !_LIBC && !HAVE_STPCPY
391 static char *
stpcpy(char * dest,const char * src)392 stpcpy (char *dest, const char *src)
393 {
394 while ((*dest++ = *src++) != '\0')
395 /* Do nothing. */ ;
396 return dest - 1;
397 }
398 #endif
399