01e7e74956f65786b544c480c483e81e396f94df
[kopensolaris-gnu/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, 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      int do_allocate;
193 {
194   char *abs_filename;
195   struct loaded_l10nfile *last = NULL;
196   struct loaded_l10nfile *retval;
197   char *cp;
198   size_t entries;
199   int cnt;
200
201   /* Allocate room for the full file name.  */
202   abs_filename = (char *) malloc (dirlist_len
203                                   + strlen (language)
204                                   + ((mask & TERRITORY) != 0
205                                      ? strlen (territory) + 1 : 0)
206                                   + ((mask & XPG_CODESET) != 0
207                                      ? strlen (codeset) + 1 : 0)
208                                   + ((mask & XPG_NORM_CODESET) != 0
209                                      ? strlen (normalized_codeset) + 1 : 0)
210                                   + (((mask & XPG_MODIFIER) != 0
211                                       || (mask & CEN_AUDIENCE) != 0)
212                                      ? strlen (modifier) + 1 : 0)
213                                   + ((mask & CEN_SPECIAL) != 0
214                                      ? strlen (special) + 1 : 0)
215                                   + (((mask & CEN_SPONSOR) != 0
216                                       || (mask & CEN_REVISION) != 0)
217                                      ? (1 + ((mask & CEN_SPONSOR) != 0
218                                              ? strlen (sponsor) + 1 : 0)
219                                         + ((mask & CEN_REVISION) != 0
220                                            ? strlen (revision) + 1 : 0)) : 0)
221                                   + 1 + strlen (filename) + 1);
222
223   if (abs_filename == NULL)
224     return NULL;
225
226   retval = NULL;
227   last = NULL;
228
229   /* Construct file name.  */
230   memcpy (abs_filename, dirlist, dirlist_len);
231   __argz_stringify (abs_filename, dirlist_len, ':');
232   cp = abs_filename + (dirlist_len - 1);
233   *cp++ = '/';
234   cp = stpcpy (cp, language);
235
236   if ((mask & TERRITORY) != 0)
237     {
238       *cp++ = '_';
239       cp = stpcpy (cp, territory);
240     }
241   if ((mask & XPG_CODESET) != 0)
242     {
243       *cp++ = '.';
244       cp = stpcpy (cp, codeset);
245     }
246   if ((mask & XPG_NORM_CODESET) != 0)
247     {
248       *cp++ = '.';
249       cp = stpcpy (cp, normalized_codeset);
250     }
251   if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
252     {
253       /* This component can be part of both syntaces but has different
254          leading characters.  For CEN we use `+', else `@'.  */
255       *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
256       cp = stpcpy (cp, modifier);
257     }
258   if ((mask & CEN_SPECIAL) != 0)
259     {
260       *cp++ = '+';
261       cp = stpcpy (cp, special);
262     }
263   if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
264     {
265       *cp++ = ',';
266       if ((mask & CEN_SPONSOR) != 0)
267         cp = stpcpy (cp, sponsor);
268       if ((mask & CEN_REVISION) != 0)
269         {
270           *cp++ = '_';
271           cp = stpcpy (cp, revision);
272         }
273     }
274
275   *cp++ = '/';
276   stpcpy (cp, filename);
277
278   /* Look in list of already loaded domains whether it is already
279      available.  */
280   last = NULL;
281   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
282     if (retval->filename != NULL)
283       {
284         int compare = strcmp (retval->filename, abs_filename);
285         if (compare == 0)
286           /* We found it!  */
287           break;
288         if (compare < 0)
289           {
290             /* It's not in the list.  */
291             retval = NULL;
292             break;
293           }
294
295         last = retval;
296       }
297
298   if (retval != NULL || do_allocate == 0)
299     {
300       free (abs_filename);
301       return retval;
302     }
303
304   retval = (struct loaded_l10nfile *)
305     malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
306                                 * (1 << pop (mask))
307                                 * sizeof (struct loaded_l10nfile *)));
308   if (retval == NULL)
309     return NULL;
310
311   retval->filename = abs_filename;
312   retval->decided = (__argz_count (dirlist, dirlist_len) != 1
313                      || ((mask & XPG_CODESET) != 0
314                          && (mask & XPG_NORM_CODESET) != 0));
315   retval->data = NULL;
316
317   if (last == NULL)
318     {
319       retval->next = *l10nfile_list;
320       *l10nfile_list = retval;
321     }
322   else
323     {
324       retval->next = last->next;
325       last->next = retval;
326     }
327
328   entries = 0;
329   /* If the DIRLIST is a real list the RETVAL entry corresponds not to
330      a real file.  So we have to use the DIRLIST separation mechanism
331      of the inner loop.  */
332   cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
333   for (; cnt >= 0; --cnt)
334     if ((cnt & ~mask) == 0
335         && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
336         && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
337       {
338         /* Iterate over all elements of the DIRLIST.  */
339         char *dir = NULL;
340
341         while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
342                != NULL)
343           retval->successor[entries++]
344             = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
345                                   language, territory, codeset,
346                                   normalized_codeset, modifier, special,
347                                   sponsor, revision, filename, 1);
348       }
349   retval->successor[entries] = NULL;
350
351   return retval;
352 }
353 \f
354 /* Normalize codeset name.  There is no standard for the codeset
355    names.  Normalization allows the user to use any of the common
356    names.  The return value is dynamically allocated and has to be
357    freed by the caller.  */
358 const char *
359 _nl_normalize_codeset (codeset, name_len)
360      const char *codeset;
361      size_t name_len;
362 {
363   int len = 0;
364   int only_digit = 1;
365   char *retval;
366   char *wp;
367   size_t cnt;
368
369   for (cnt = 0; cnt < name_len; ++cnt)
370     if (isalnum (codeset[cnt]))
371       {
372         ++len;
373
374         if (isalpha (codeset[cnt]))
375           only_digit = 0;
376       }
377
378   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
379
380   if (retval != NULL)
381     {
382       if (only_digit)
383         wp = stpcpy (retval, "iso");
384       else
385         wp = retval;
386
387       for (cnt = 0; cnt < name_len; ++cnt)
388         if (isalpha (codeset[cnt]))
389           *wp++ = _tolower (codeset[cnt]);
390         else if (isdigit (codeset[cnt]))
391           *wp++ = codeset[cnt];
392
393       *wp = '\0';
394     }
395
396   return (const char *) retval;
397 }
398
399
400 /* @@ begin of epilog @@ */
401
402 /* We don't want libintl.a to depend on any other library.  So we
403    avoid the non-standard function stpcpy.  In GNU C Library this
404    function is available, though.  Also allow the symbol HAVE_STPCPY
405    to be defined.  */
406 #if !_LIBC && !HAVE_STPCPY
407 static char *
408 stpcpy (dest, src)
409      char *dest;
410      const char *src;
411 {
412   while ((*dest++ = *src++) != '\0')
413     /* Do nothing. */ ;
414   return dest - 1;
415 }
416 #endif