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