2004-12-17 Moritz Schulte <moritz@g10code.com>
[gnupg.git] / intl / l10nflist.c
1 /* Copyright (C) 1995-1999, 2000-2003 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 (char *dest, const char *src);
62 # endif
63 #endif
64
65 /* Pathname support.
66    ISSLASH(C)           tests whether C is a directory separator character.
67    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
68                         it may be concatenated to a directory pathname.
69  */
70 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
71   /* Win32, OS/2, DOS */
72 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
73 # define HAS_DEVICE(P) \
74     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
75      && (P)[1] == ':')
76 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
77 #else
78   /* Unix */
79 # define ISSLASH(C) ((C) == '/')
80 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
81 #endif
82
83 /* Define function which are usually not available.  */
84
85 #if !defined _LIBC && !defined HAVE___ARGZ_COUNT
86 /* Returns the number of strings in ARGZ.  */
87 static size_t
88 argz_count__ (const char *argz, size_t len)
89 {
90   size_t count = 0;
91   while (len > 0)
92     {
93       size_t part_len = strlen (argz);
94       argz += part_len + 1;
95       len -= part_len + 1;
96       count++;
97     }
98   return count;
99 }
100 # undef __argz_count
101 # define __argz_count(argz, len) argz_count__ (argz, len)
102 #else
103 # ifdef _LIBC
104 #  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
105 # endif
106 #endif  /* !_LIBC && !HAVE___ARGZ_COUNT */
107
108 #if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
109 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
110    except the last into the character SEP.  */
111 static void
112 argz_stringify__ (char *argz, size_t len, int sep)
113 {
114   while (len > 0)
115     {
116       size_t part_len = strlen (argz);
117       argz += part_len;
118       len -= part_len + 1;
119       if (len > 0)
120         *argz++ = sep;
121     }
122 }
123 # undef __argz_stringify
124 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
125 #else
126 # ifdef _LIBC
127 #  define __argz_stringify(argz, len, sep) \
128   INTUSE(__argz_stringify) (argz, len, sep)
129 # endif
130 #endif  /* !_LIBC && !HAVE___ARGZ_STRINGIFY */
131
132 #if !defined _LIBC && !defined HAVE___ARGZ_NEXT
133 static char *
134 argz_next__ (char *argz, size_t argz_len, 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 inline int
156 pop (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 (struct loaded_l10nfile **l10nfile_list,
170                     const char *dirlist, size_t dirlist_len,
171                     int mask, const char *language, const char *territory,
172                     const char *codeset, const char *normalized_codeset,
173                     const char *modifier, const char *special,
174                     const char *sponsor, const char *revision,
175                     const char *filename, int do_allocate)
176 {
177   char *abs_filename;
178   struct loaded_l10nfile **lastp;
179   struct loaded_l10nfile *retval;
180   char *cp;
181   size_t dirlist_count;
182   size_t entries;
183   int cnt;
184
185   /* If LANGUAGE contains an absolute directory specification, we ignore
186      DIRLIST.  */
187   if (IS_ABSOLUTE_PATH (language))
188     dirlist_len = 0;
189
190   /* Allocate room for the full file name.  */
191   abs_filename = (char *) malloc (dirlist_len
192                                   + strlen (language)
193                                   + ((mask & TERRITORY) != 0
194                                      ? strlen (territory) + 1 : 0)
195                                   + ((mask & XPG_CODESET) != 0
196                                      ? strlen (codeset) + 1 : 0)
197                                   + ((mask & XPG_NORM_CODESET) != 0
198                                      ? strlen (normalized_codeset) + 1 : 0)
199                                   + (((mask & XPG_MODIFIER) != 0
200                                       || (mask & CEN_AUDIENCE) != 0)
201                                      ? strlen (modifier) + 1 : 0)
202                                   + ((mask & CEN_SPECIAL) != 0
203                                      ? strlen (special) + 1 : 0)
204                                   + (((mask & CEN_SPONSOR) != 0
205                                       || (mask & CEN_REVISION) != 0)
206                                      ? (1 + ((mask & CEN_SPONSOR) != 0
207                                              ? strlen (sponsor) : 0)
208                                         + ((mask & CEN_REVISION) != 0
209                                            ? strlen (revision) + 1 : 0)) : 0)
210                                   + 1 + strlen (filename) + 1);
211
212   if (abs_filename == NULL)
213     return NULL;
214
215   /* Construct file name.  */
216   cp = abs_filename;
217   if (dirlist_len > 0)
218     {
219       memcpy (cp, dirlist, dirlist_len);
220       __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
221       cp += dirlist_len;
222       cp[-1] = '/';
223     }
224
225   cp = stpcpy (cp, language);
226
227   if ((mask & TERRITORY) != 0)
228     {
229       *cp++ = '_';
230       cp = stpcpy (cp, territory);
231     }
232   if ((mask & XPG_CODESET) != 0)
233     {
234       *cp++ = '.';
235       cp = stpcpy (cp, codeset);
236     }
237   if ((mask & XPG_NORM_CODESET) != 0)
238     {
239       *cp++ = '.';
240       cp = stpcpy (cp, normalized_codeset);
241     }
242   if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
243     {
244       /* This component can be part of both syntaces but has different
245          leading characters.  For CEN we use `+', else `@'.  */
246       *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
247       cp = stpcpy (cp, modifier);
248     }
249   if ((mask & CEN_SPECIAL) != 0)
250     {
251       *cp++ = '+';
252       cp = stpcpy (cp, special);
253     }
254   if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
255     {
256       *cp++ = ',';
257       if ((mask & CEN_SPONSOR) != 0)
258         cp = stpcpy (cp, sponsor);
259       if ((mask & CEN_REVISION) != 0)
260         {
261           *cp++ = '_';
262           cp = stpcpy (cp, revision);
263         }
264     }
265
266   *cp++ = '/';
267   stpcpy (cp, filename);
268
269   /* Look in list of already loaded domains whether it is already
270      available.  */
271   lastp = l10nfile_list;
272   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
273     if (retval->filename != NULL)
274       {
275         int compare = strcmp (retval->filename, abs_filename);
276         if (compare == 0)
277           /* We found it!  */
278           break;
279         if (compare < 0)
280           {
281             /* It's not in the list.  */
282             retval = NULL;
283             break;
284           }
285
286         lastp = &retval->next;
287       }
288
289   if (retval != NULL || do_allocate == 0)
290     {
291       free (abs_filename);
292       return retval;
293     }
294
295   dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
296
297   /* Allocate a new loaded_l10nfile.  */
298   retval =
299     (struct loaded_l10nfile *)
300     malloc (sizeof (*retval)
301             + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
302                * sizeof (struct loaded_l10nfile *)));
303   if (retval == NULL)
304     return NULL;
305
306   retval->filename = abs_filename;
307
308   /* We set retval->data to NULL here; it is filled in later.
309      Setting retval->decided to 1 here means that retval does not
310      correspond to a real file (dirlist_count > 1) or is not worth
311      looking up (if an unnormalized codeset was specified).  */
312   retval->decided = (dirlist_count > 1
313                      || ((mask & XPG_CODESET) != 0
314                          && (mask & XPG_NORM_CODESET) != 0));
315   retval->data = NULL;
316
317   retval->next = *lastp;
318   *lastp = retval;
319
320   entries = 0;
321   /* Recurse to fill the inheritance list of RETVAL.
322      If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
323      entry does not correspond to a real file; retval->filename contains
324      colons.  In this case we loop across all elements of DIRLIST and
325      across all bit patterns dominated by MASK.
326      If the DIRLIST is a single directory or entirely redundant (i.e.
327      DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
328      MASK, excluding MASK itself.
329      In either case, we loop down from MASK to 0.  This has the effect
330      that the extra bits in the locale name are dropped in this order:
331      first the modifier, then the territory, then the codeset, then the
332      normalized_codeset.  */
333   for (cnt = dirlist_count > 1 ? mask : mask - 1; 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         if (dirlist_count > 1)
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,
347                                       cnt, language, territory, codeset,
348                                       normalized_codeset, modifier, special,
349                                       sponsor, revision, filename, 1);
350           }
351         else
352           retval->successor[entries++]
353             = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
354                                   cnt, language, territory, codeset,
355                                   normalized_codeset, modifier, special,
356                                   sponsor, revision, filename, 1);
357       }
358   retval->successor[entries] = NULL;
359
360   return retval;
361 }
362 \f
363 /* Normalize codeset name.  There is no standard for the codeset
364    names.  Normalization allows the user to use any of the common
365    names.  The return value is dynamically allocated and has to be
366    freed by the caller.  */
367 const char *
368 _nl_normalize_codeset (const char *codeset, size_t name_len)
369 {
370   int len = 0;
371   int only_digit = 1;
372   char *retval;
373   char *wp;
374   size_t cnt;
375
376   for (cnt = 0; cnt < name_len; ++cnt)
377     if (isalnum ((unsigned char) codeset[cnt]))
378       {
379         ++len;
380
381         if (isalpha ((unsigned char) codeset[cnt]))
382           only_digit = 0;
383       }
384
385   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
386
387   if (retval != NULL)
388     {
389       if (only_digit)
390         wp = stpcpy (retval, "iso");
391       else
392         wp = retval;
393
394       for (cnt = 0; cnt < name_len; ++cnt)
395         if (isalpha ((unsigned char) codeset[cnt]))
396           *wp++ = tolower ((unsigned char) codeset[cnt]);
397         else if (isdigit ((unsigned char) codeset[cnt]))
398           *wp++ = codeset[cnt];
399
400       *wp = '\0';
401     }
402
403   return (const char *) retval;
404 }
405
406
407 /* @@ begin of epilog @@ */
408
409 /* We don't want libintl.a to depend on any other library.  So we
410    avoid the non-standard function stpcpy.  In GNU C Library this
411    function is available, though.  Also allow the symbol HAVE_STPCPY
412    to be defined.  */
413 #if !_LIBC && !HAVE_STPCPY
414 static char *
415 stpcpy (char *dest, const char *src)
416 {
417   while ((*dest++ = *src++) != '\0')
418     /* Do nothing. */ ;
419   return dest - 1;
420 }
421 #endif