7ffb4ab590381ab2bc92636d877c638cc66607fc
[kopensolaris-gnu/glibc.git] / intl / l10nflist.c
1 /* Copyright (C) 1995-2002, 2004 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     return NULL;
274
275   retval->filename = abs_filename;
276   /* If more than one directory is in the list this is a pseudo-entry
277      which just references others.  We do not try to load data for it,
278      ever.  */
279   retval->decided = (__argz_count (dirlist, dirlist_len) != 1
280                      || ((mask & XPG_CODESET) != 0
281                          && (mask & XPG_NORM_CODESET) != 0));
282   retval->data = NULL;
283
284   if (last == NULL)
285     {
286       retval->next = *l10nfile_list;
287       *l10nfile_list = retval;
288     }
289   else
290     {
291       retval->next = last->next;
292       last->next = retval;
293     }
294
295   entries = 0;
296   /* If the DIRLIST is a real list the RETVAL entry corresponds not to
297      a real file.  So we have to use the DIRLIST separation mechanism
298      of the inner loop.  */
299   cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
300   for (; cnt >= 0; --cnt)
301     if ((cnt & ~mask) == 0)
302       {
303         /* Iterate over all elements of the DIRLIST.  */
304         char *dir = NULL;
305
306         while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
307                != NULL)
308           retval->successor[entries++]
309             = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
310                                   language, territory, codeset,
311                                   normalized_codeset, modifier, filename, 1);
312       }
313   retval->successor[entries] = NULL;
314
315   return retval;
316 }
317 \f
318 /* Normalize codeset name.  There is no standard for the codeset
319    names.  Normalization allows the user to use any of the common
320    names.  The return value is dynamically allocated and has to be
321    freed by the caller.  */
322 const char *
323 _nl_normalize_codeset (codeset, name_len)
324      const char *codeset;
325      size_t name_len;
326 {
327   int len = 0;
328   int only_digit = 1;
329   char *retval;
330   char *wp;
331   size_t cnt;
332
333   for (cnt = 0; cnt < name_len; ++cnt)
334     if (isalnum ((unsigned char) codeset[cnt]))
335       {
336         ++len;
337
338         if (isalpha ((unsigned char) codeset[cnt]))
339           only_digit = 0;
340       }
341
342   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
343
344   if (retval != NULL)
345     {
346       if (only_digit)
347         wp = stpcpy (retval, "iso");
348       else
349         wp = retval;
350
351       for (cnt = 0; cnt < name_len; ++cnt)
352         if (isalpha ((unsigned char) codeset[cnt]))
353           *wp++ = tolower ((unsigned char) codeset[cnt]);
354         else if (isdigit ((unsigned char) codeset[cnt]))
355           *wp++ = codeset[cnt];
356
357       *wp = '\0';
358     }
359
360   return (const char *) retval;
361 }
362
363
364 /* @@ begin of epilog @@ */
365
366 /* We don't want libintl.a to depend on any other library.  So we
367    avoid the non-standard function stpcpy.  In GNU C Library this
368    function is available, though.  Also allow the symbol HAVE_STPCPY
369    to be defined.  */
370 #if !_LIBC && !HAVE_STPCPY
371 static char *
372 stpcpy (dest, src)
373      char *dest;
374      const char *src;
375 {
376   while ((*dest++ = *src++) != '\0')
377     /* Do nothing. */ ;
378   return dest - 1;
379 }
380 #endif