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