Update.
[platform/upstream/glibc.git] / intl / l10nflist.c
1 /* Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
2    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    The GNU C Library 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 GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23
24 #if defined HAVE_STRING_H || defined _LIBC
25 # ifndef _GNU_SOURCE
26 #  define _GNU_SOURCE   1
27 # endif
28 # include <string.h>
29 #else
30 # include <strings.h>
31 # ifndef memcpy
32 #  define memcpy(Dst, Src, Num) bcopy (Src, Dst, Num)
33 # endif
34 #endif
35 #if !HAVE_STRCHR && !defined _LIBC
36 # ifndef strchr
37 #  define strchr index
38 # endif
39 #endif
40
41 #if defined _LIBC || defined HAVE_ARGZ_H
42 # include <argz.h>
43 #endif
44 #include <ctype.h>
45 #include <sys/types.h>
46
47 #if defined STDC_HEADERS || defined _LIBC
48 # include <stdlib.h>
49 #endif
50
51 #include "loadinfo.h"
52
53 /* On some strange systems still no definition of NULL is found.  Sigh!  */
54 #ifndef NULL
55 # if defined __STDC__ && __STDC__
56 #  define NULL ((void *) 0)
57 # else
58 #  define NULL 0
59 # endif
60 #endif
61
62 /* @@ end of prolog @@ */
63
64 #ifdef _LIBC
65 /* Rename the non ANSI C functions.  This is required by the standard
66    because some ANSI C functions will require linking with this object
67    file and the name space must not be polluted.  */
68 # ifndef stpcpy
69 #  define stpcpy(dest, src) __stpcpy(dest, src)
70 # endif
71 #else
72 # ifndef HAVE_STPCPY
73 static char *stpcpy PARAMS ((char *dest, const char *src));
74 # endif
75 #endif
76
77 /* Define function which are usually not available.  */
78
79 #if !defined _LIBC && !defined HAVE___ARGZ_COUNT
80 /* Returns the number of strings in ARGZ.  */
81 static size_t argz_count__ PARAMS ((const char *argz, size_t len));
82
83 static size_t
84 argz_count__ (argz, len)
85      const char *argz;
86      size_t len;
87 {
88   size_t count = 0;
89   while (len > 0)
90     {
91       size_t part_len = strlen (argz);
92       argz += part_len + 1;
93       len -= part_len + 1;
94       count++;
95     }
96   return count;
97 }
98 # undef __argz_count
99 # define __argz_count(argz, len) argz_count__ (argz, len)
100 #endif  /* !_LIBC && !HAVE___ARGZ_COUNT */
101
102 #if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
103 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
104    except the last into the character SEP.  */
105 static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep));
106
107 static void
108 argz_stringify__ (argz, len, sep)
109      char *argz;
110      size_t len;
111      int sep;
112 {
113   while (len > 0)
114     {
115       size_t part_len = strlen (argz);
116       argz += part_len;
117       len -= part_len + 1;
118       if (len > 0)
119         *argz++ = sep;
120     }
121 }
122 # undef __argz_stringify
123 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
124 #endif  /* !_LIBC && !HAVE___ARGZ_STRINGIFY */
125
126 #if !defined _LIBC && !defined HAVE___ARGZ_NEXT
127 static char *argz_next__ PARAMS ((char *argz, size_t argz_len,
128                                   const char *entry));
129
130 static char *
131 argz_next__ (argz, argz_len, entry)
132      char *argz;
133      size_t argz_len;
134      const char *entry;
135 {
136   if (entry)
137     {
138       if (entry < argz + argz_len)
139         entry = strchr (entry, '\0') + 1;
140
141       return entry >= argz + argz_len ? NULL : (char *) entry;
142     }
143   else
144     if (argz_len > 0)
145       return argz;
146     else
147       return 0;
148 }
149 # undef __argz_next
150 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
151 #endif  /* !_LIBC && !HAVE___ARGZ_NEXT */
152
153
154 /* Return number of bits set in X.  */
155 static int pop PARAMS ((int x));
156
157 static inline int
158 pop (x)
159      int x;
160 {
161   /* We assume that no more than 16 bits are used.  */
162   x = ((x & ~0x5555) >> 1) + (x & 0x5555);
163   x = ((x & ~0x3333) >> 2) + (x & 0x3333);
164   x = ((x >> 4) + x) & 0x0f0f;
165   x = ((x >> 8) + x) & 0xff;
166
167   return x;
168 }
169
170 \f
171 struct loaded_l10nfile *
172 _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
173                     territory, codeset, normalized_codeset, modifier, special,
174                     sponsor, revision, filename, do_allocate)
175      struct loaded_l10nfile **l10nfile_list;
176      const char *dirlist;
177      size_t dirlist_len;
178      int mask;
179      const char *language;
180      const char *territory;
181      const char *codeset;
182      const char *normalized_codeset;
183      const char *modifier;
184      const char *special;
185      const char *sponsor;
186      const char *revision;
187      const char *filename;
188      int do_allocate;
189 {
190   char *abs_filename;
191   struct loaded_l10nfile *last = NULL;
192   struct loaded_l10nfile *retval;
193   char *cp;
194   size_t entries;
195   int cnt;
196
197   /* Allocate room for the full file name.  */
198   abs_filename = (char *) malloc (dirlist_len
199                                   + strlen (language)
200                                   + ((mask & TERRITORY) != 0
201                                      ? strlen (territory) + 1 : 0)
202                                   + ((mask & XPG_CODESET) != 0
203                                      ? strlen (codeset) + 1 : 0)
204                                   + ((mask & XPG_NORM_CODESET) != 0
205                                      ? strlen (normalized_codeset) + 1 : 0)
206                                   + (((mask & XPG_MODIFIER) != 0
207                                       || (mask & CEN_AUDIENCE) != 0)
208                                      ? strlen (modifier) + 1 : 0)
209                                   + ((mask & CEN_SPECIAL) != 0
210                                      ? strlen (special) + 1 : 0)
211                                   + (((mask & CEN_SPONSOR) != 0
212                                       || (mask & CEN_REVISION) != 0)
213                                      ? (1 + ((mask & CEN_SPONSOR) != 0
214                                              ? strlen (sponsor) + 1 : 0)
215                                         + ((mask & CEN_REVISION) != 0
216                                            ? strlen (revision) + 1 : 0)) : 0)
217                                   + 1 + strlen (filename) + 1);
218
219   if (abs_filename == NULL)
220     return NULL;
221
222   retval = NULL;
223   last = NULL;
224
225   /* Construct file name.  */
226   memcpy (abs_filename, dirlist, dirlist_len);
227   __argz_stringify (abs_filename, dirlist_len, ':');
228   cp = abs_filename + (dirlist_len - 1);
229   *cp++ = '/';
230   cp = stpcpy (cp, language);
231
232   if ((mask & TERRITORY) != 0)
233     {
234       *cp++ = '_';
235       cp = stpcpy (cp, territory);
236     }
237   if ((mask & XPG_CODESET) != 0)
238     {
239       *cp++ = '.';
240       cp = stpcpy (cp, codeset);
241     }
242   if ((mask & XPG_NORM_CODESET) != 0)
243     {
244       *cp++ = '.';
245       cp = stpcpy (cp, normalized_codeset);
246     }
247   if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
248     {
249       /* This component can be part of both syntaces but has different
250          leading characters.  For CEN we use `+', else `@'.  */
251       *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
252       cp = stpcpy (cp, modifier);
253     }
254   if ((mask & CEN_SPECIAL) != 0)
255     {
256       *cp++ = '+';
257       cp = stpcpy (cp, special);
258     }
259   if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
260     {
261       *cp++ = ',';
262       if ((mask & CEN_SPONSOR) != 0)
263         cp = stpcpy (cp, sponsor);
264       if ((mask & CEN_REVISION) != 0)
265         {
266           *cp++ = '_';
267           cp = stpcpy (cp, revision);
268         }
269     }
270
271   *cp++ = '/';
272   stpcpy (cp, filename);
273
274   /* Look in list of already loaded domains whether it is already
275      available.  */
276   last = NULL;
277   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
278     if (retval->filename != NULL)
279       {
280         int compare = strcmp (retval->filename, abs_filename);
281         if (compare == 0)
282           /* We found it!  */
283           break;
284         if (compare < 0)
285           {
286             /* It's not in the list.  */
287             retval = NULL;
288             break;
289           }
290
291         last = retval;
292       }
293
294   if (retval != NULL || do_allocate == 0)
295     {
296       free (abs_filename);
297       return retval;
298     }
299
300   retval = (struct loaded_l10nfile *)
301     malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
302                                 * (1 << pop (mask))
303                                 * sizeof (struct loaded_l10nfile *)));
304   if (retval == NULL)
305     return NULL;
306
307   retval->filename = abs_filename;
308   retval->decided = (__argz_count (dirlist, dirlist_len) != 1
309                      || ((mask & XPG_CODESET) != 0
310                          && (mask & XPG_NORM_CODESET) != 0));
311   retval->data = NULL;
312
313   if (last == NULL)
314     {
315       retval->next = *l10nfile_list;
316       *l10nfile_list = retval;
317     }
318   else
319     {
320       retval->next = last->next;
321       last->next = retval;
322     }
323
324   entries = 0;
325   /* If the DIRLIST is a real list the RETVAL entry corresponds not to
326      a real file.  So we have to use the DIRLIST separation mechanism
327      of the inner loop.  */
328   cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
329   for (; cnt >= 0; --cnt)
330     if ((cnt & ~mask) == 0
331         && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
332         && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
333       {
334         /* Iterate over all elements of the DIRLIST.  */
335         char *dir = NULL;
336
337         while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
338                != NULL)
339           retval->successor[entries++]
340             = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
341                                   language, territory, codeset,
342                                   normalized_codeset, modifier, special,
343                                   sponsor, revision, filename, 1);
344       }
345   retval->successor[entries] = NULL;
346
347   return retval;
348 }
349 \f
350 /* Normalize codeset name.  There is no standard for the codeset
351    names.  Normalization allows the user to use any of the common
352    names.  The return value is dynamically allocated and has to be
353    freed by the caller.  */
354 const char *
355 _nl_normalize_codeset (codeset, name_len)
356      const char *codeset;
357      size_t name_len;
358 {
359   int len = 0;
360   int only_digit = 1;
361   char *retval;
362   char *wp;
363   size_t cnt;
364
365   for (cnt = 0; cnt < name_len; ++cnt)
366     if (isalnum (codeset[cnt]))
367       {
368         ++len;
369
370         if (isalpha (codeset[cnt]))
371           only_digit = 0;
372       }
373
374   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
375
376   if (retval != NULL)
377     {
378       if (only_digit)
379         wp = stpcpy (retval, "iso");
380       else
381         wp = retval;
382
383       for (cnt = 0; cnt < name_len; ++cnt)
384         if (isalpha (codeset[cnt]))
385           *wp++ = _tolower (codeset[cnt]);
386         else if (isdigit (codeset[cnt]))
387           *wp++ = codeset[cnt];
388
389       *wp = '\0';
390     }
391
392   return (const char *) retval;
393 }
394
395
396 /* @@ begin of epilog @@ */
397
398 /* We don't want libintl.a to depend on any other library.  So we
399    avoid the non-standard function stpcpy.  In GNU C Library this
400    function is available, though.  Also allow the symbol HAVE_STPCPY
401    to be defined.  */
402 #if !_LIBC && !HAVE_STPCPY
403 static char *
404 stpcpy (dest, src)
405      char *dest;
406      const char *src;
407 {
408   while ((*dest++ = *src++) != '\0')
409     /* Do nothing. */ ;
410   return dest - 1;
411 }
412 #endif