chiark / gitweb /
Apply https://sourceware.org/git/?p=glibc.git;a=commit;h=d5dd6189d506068ed11c8bfa1e1e...
[eglibc.git] / intl / l10nflist.c
1 /* Copyright (C) 1995-2002, 2004, 2005 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
4
5    The GNU C Library is free software; you can redistribute it and/or
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.
9
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
13    Lesser General Public License for more details.
14
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.  */
19
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
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <string.h>
32
33 #if defined _LIBC || defined HAVE_ARGZ_H
34 # include <argz.h>
35 #endif
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <stdlib.h>
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 PARAMS ((char *dest, const char *src));
63 # endif
64 #endif
65
66 /* Define function which are usually not available.  */
67
68 #if !defined _LIBC && !defined HAVE___ARGZ_COUNT
69 /* Returns the number of strings in ARGZ.  */
70 static size_t argz_count__ PARAMS ((const char *argz, size_t len));
71
72 static size_t
73 argz_count__ (argz, len)
74      const char *argz;
75      size_t len;
76 {
77   size_t count = 0;
78   while (len > 0)
79     {
80       size_t part_len = strlen (argz);
81       argz += part_len + 1;
82       len -= part_len + 1;
83       count++;
84     }
85   return count;
86 }
87 # undef __argz_count
88 # define __argz_count(argz, len) argz_count__ (argz, len)
89 #else
90 # ifdef _LIBC
91 #  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
92 # endif
93 #endif  /* !_LIBC && !HAVE___ARGZ_COUNT */
94
95 #if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
96 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
97    except the last into the character SEP.  */
98 static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep));
99
100 static void
101 argz_stringify__ (argz, len, sep)
102      char *argz;
103      size_t len;
104      int sep;
105 {
106   while (len > 0)
107     {
108       size_t part_len = strlen (argz);
109       argz += part_len;
110       len -= part_len + 1;
111       if (len > 0)
112         *argz++ = sep;
113     }
114 }
115 # undef __argz_stringify
116 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
117 #else
118 # ifdef _LIBC
119 #  define __argz_stringify(argz, len, sep) \
120   INTUSE(__argz_stringify) (argz, len, sep)
121 # endif
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));
127
128 static char *
129 argz_next__ (argz, argz_len, entry)
130      char *argz;
131      size_t argz_len;
132      const char *entry;
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 }
147 # undef __argz_next
148 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
149 #endif  /* !_LIBC && !HAVE___ARGZ_NEXT */
150
151
152 /* Return number of bits set in X.  */
153 static int pop PARAMS ((int x));
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
168 \f
169 struct loaded_l10nfile *
170 _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
171                     territory, codeset, normalized_codeset, modifier,
172                     filename, do_allocate)
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;
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)
195                                   + ((mask & XPG_TERRITORY) != 0
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)
201                                   + ((mask & XPG_MODIFIER) != 0
202                                      ? strlen (modifier) + 1 : 0)
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
218   if ((mask & XPG_TERRITORY) != 0)
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     }
233   if ((mask & XPG_MODIFIER) != 0)
234     {
235       *cp++ = '@';
236       cp = stpcpy (cp, modifier);
237     }
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)
273     {
274       free (abs_filename);
275       return NULL;
276     }
277
278   retval->filename = abs_filename;
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.  */
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;
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
301      of the inner loop.  */
302   cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
303   for (; cnt >= 0; --cnt)
304     if ((cnt & ~mask) == 0)
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,
314                                   normalized_codeset, modifier, filename, 1);
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
323    names.  The return value is dynamically allocated and has to be
324    freed by the caller.  */
325 const char *
326 _nl_normalize_codeset (codeset, name_len)
327      const char *codeset;
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)
337     if (isalnum ((unsigned char) codeset[cnt]))
338       {
339         ++len;
340
341         if (isalpha ((unsigned char) codeset[cnt]))
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)
350         wp = stpcpy (retval, "iso");
351       else
352         wp = retval;
353
354       for (cnt = 0; cnt < name_len; ++cnt)
355         if (isalpha ((unsigned char) codeset[cnt]))
356           *wp++ = tolower ((unsigned char) codeset[cnt]);
357         else if (isdigit ((unsigned char) codeset[cnt]))
358           *wp++ = codeset[cnt];
359
360       *wp = '\0';
361     }
362
363   return (const char *) retval;
364 }
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