Some minor bug fixes, new test utilities and started support for other
[gnupg.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 /* 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 argz_count__ PARAMS ((const char *argz, size_t len));
88
89 static size_t
90 argz_count__ (argz, len)
91      const char *argz;
92      size_t len;
93 {
94   size_t count = 0;
95   while (len > 0)
96     {
97       size_t part_len = strlen (argz);
98       argz += part_len + 1;
99       len -= part_len + 1;
100       count++;
101     }
102   return count;
103 }
104 # undef __argz_count
105 # define __argz_count(argz, len) argz_count__ (argz, len)
106 #else
107 # ifdef _LIBC
108 #  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
109 # endif
110 #endif  /* !_LIBC && !HAVE___ARGZ_COUNT */
111
112 #if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
113 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
114    except the last into the character SEP.  */
115 static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep));
116
117 static void
118 argz_stringify__ (argz, len, sep)
119      char *argz;
120      size_t len;
121      int sep;
122 {
123   while (len > 0)
124     {
125       size_t part_len = strlen (argz);
126       argz += part_len;
127       len -= part_len + 1;
128       if (len > 0)
129         *argz++ = sep;
130     }
131 }
132 # undef __argz_stringify
133 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
134 #else
135 # ifdef _LIBC
136 #  define __argz_stringify(argz, len, sep) \
137   INTUSE(__argz_stringify) (argz, len, sep)
138 # endif
139 #endif  /* !_LIBC && !HAVE___ARGZ_STRINGIFY */
140
141 #if !defined _LIBC && !defined HAVE___ARGZ_NEXT
142 static char *argz_next__ PARAMS ((char *argz, size_t argz_len,
143                                   const char *entry));
144
145 static char *
146 argz_next__ (argz, argz_len, entry)
147      char *argz;
148      size_t argz_len;
149      const char *entry;
150 {
151   if (entry)
152     {
153       if (entry < argz + argz_len)
154         entry = strchr (entry, '\0') + 1;
155
156       return entry >= argz + argz_len ? NULL : (char *) entry;
157     }
158   else
159     if (argz_len > 0)
160       return argz;
161     else
162       return 0;
163 }
164 # undef __argz_next
165 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
166 #endif  /* !_LIBC && !HAVE___ARGZ_NEXT */
167
168
169 /* Return number of bits set in X.  */
170 static int pop PARAMS ((int x));
171
172 static inline int
173 pop (x)
174      int x;
175 {
176   /* We assume that no more than 16 bits are used.  */
177   x = ((x & ~0x5555) >> 1) + (x & 0x5555);
178   x = ((x & ~0x3333) >> 2) + (x & 0x3333);
179   x = ((x >> 4) + x) & 0x0f0f;
180   x = ((x >> 8) + x) & 0xff;
181
182   return x;
183 }
184
185 \f
186 struct loaded_l10nfile *
187 _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
188                     territory, codeset, normalized_codeset, modifier, special,
189                     sponsor, revision, filename, do_allocate)
190      struct loaded_l10nfile **l10nfile_list;
191      const char *dirlist;
192      size_t dirlist_len;
193      int mask;
194      const char *language;
195      const char *territory;
196      const char *codeset;
197      const char *normalized_codeset;
198      const char *modifier;
199      const char *special;
200      const char *sponsor;
201      const char *revision;
202      const char *filename;
203      int do_allocate;
204 {
205   char *abs_filename;
206   struct loaded_l10nfile **lastp;
207   struct loaded_l10nfile *retval;
208   char *cp;
209   size_t dirlist_count;
210   size_t entries;
211   int cnt;
212
213   /* If LANGUAGE contains an absolute directory specification, we ignore
214      DIRLIST.  */
215   if (IS_ABSOLUTE_PATH (language))
216     dirlist_len = 0;
217
218   /* Allocate room for the full file name.  */
219   abs_filename = (char *) malloc (dirlist_len
220                                   + strlen (language)
221                                   + ((mask & TERRITORY) != 0
222                                      ? strlen (territory) + 1 : 0)
223                                   + ((mask & XPG_CODESET) != 0
224                                      ? strlen (codeset) + 1 : 0)
225                                   + ((mask & XPG_NORM_CODESET) != 0
226                                      ? strlen (normalized_codeset) + 1 : 0)
227                                   + (((mask & XPG_MODIFIER) != 0
228                                       || (mask & CEN_AUDIENCE) != 0)
229                                      ? strlen (modifier) + 1 : 0)
230                                   + ((mask & CEN_SPECIAL) != 0
231                                      ? strlen (special) + 1 : 0)
232                                   + (((mask & CEN_SPONSOR) != 0
233                                       || (mask & CEN_REVISION) != 0)
234                                      ? (1 + ((mask & CEN_SPONSOR) != 0
235                                              ? strlen (sponsor) : 0)
236                                         + ((mask & CEN_REVISION) != 0
237                                            ? strlen (revision) + 1 : 0)) : 0)
238                                   + 1 + strlen (filename) + 1);
239
240   if (abs_filename == NULL)
241     return NULL;
242
243   /* Construct file name.  */
244   cp = abs_filename;
245   if (dirlist_len > 0)
246     {
247       memcpy (cp, dirlist, dirlist_len);
248       __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
249       cp += dirlist_len;
250       cp[-1] = '/';
251     }
252
253   cp = stpcpy (cp, language);
254
255   if ((mask & TERRITORY) != 0)
256     {
257       *cp++ = '_';
258       cp = stpcpy (cp, territory);
259     }
260   if ((mask & XPG_CODESET) != 0)
261     {
262       *cp++ = '.';
263       cp = stpcpy (cp, codeset);
264     }
265   if ((mask & XPG_NORM_CODESET) != 0)
266     {
267       *cp++ = '.';
268       cp = stpcpy (cp, normalized_codeset);
269     }
270   if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
271     {
272       /* This component can be part of both syntaces but has different
273          leading characters.  For CEN we use `+', else `@'.  */
274       *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
275       cp = stpcpy (cp, modifier);
276     }
277   if ((mask & CEN_SPECIAL) != 0)
278     {
279       *cp++ = '+';
280       cp = stpcpy (cp, special);
281     }
282   if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
283     {
284       *cp++ = ',';
285       if ((mask & CEN_SPONSOR) != 0)
286         cp = stpcpy (cp, sponsor);
287       if ((mask & CEN_REVISION) != 0)
288         {
289           *cp++ = '_';
290           cp = stpcpy (cp, revision);
291         }
292     }
293
294   *cp++ = '/';
295   stpcpy (cp, filename);
296
297   /* Look in list of already loaded domains whether it is already
298      available.  */
299   lastp = l10nfile_list;
300   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
301     if (retval->filename != NULL)
302       {
303         int compare = strcmp (retval->filename, abs_filename);
304         if (compare == 0)
305           /* We found it!  */
306           break;
307         if (compare < 0)
308           {
309             /* It's not in the list.  */
310             retval = NULL;
311             break;
312           }
313
314         lastp = &retval->next;
315       }
316
317   if (retval != NULL || do_allocate == 0)
318     {
319       free (abs_filename);
320       return retval;
321     }
322
323   dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
324
325   /* Allocate a new loaded_l10nfile.  */
326   retval =
327     (struct loaded_l10nfile *)
328     malloc (sizeof (*retval)
329             + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
330                * sizeof (struct loaded_l10nfile *)));
331   if (retval == NULL)
332     return NULL;
333
334   retval->filename = abs_filename;
335
336   /* We set retval->data to NULL here; it is filled in later.
337      Setting retval->decided to 1 here means that retval does not
338      correspond to a real file (dirlist_count > 1) or is not worth
339      looking up (if an unnormalized codeset was specified).  */
340   retval->decided = (dirlist_count > 1
341                      || ((mask & XPG_CODESET) != 0
342                          && (mask & XPG_NORM_CODESET) != 0));
343   retval->data = NULL;
344
345   retval->next = *lastp;
346   *lastp = retval;
347
348   entries = 0;
349   /* Recurse to fill the inheritance list of RETVAL.
350      If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
351      entry does not correspond to a real file; retval->filename contains
352      colons.  In this case we loop across all elements of DIRLIST and
353      across all bit patterns dominated by MASK.
354      If the DIRLIST is a single directory or entirely redundant (i.e.
355      DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
356      MASK, excluding MASK itself.
357      In either case, we loop down from MASK to 0.  This has the effect
358      that the extra bits in the locale name are dropped in this order:
359      first the modifier, then the territory, then the codeset, then the
360      normalized_codeset.  */
361   for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
362     if ((cnt & ~mask) == 0
363         && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
364         && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
365       {
366         if (dirlist_count > 1)
367           {
368             /* Iterate over all elements of the DIRLIST.  */
369             char *dir = NULL;
370
371             while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
372                    != NULL)
373               retval->successor[entries++]
374                 = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
375                                       cnt, language, territory, codeset,
376                                       normalized_codeset, modifier, special,
377                                       sponsor, revision, filename, 1);
378           }
379         else
380           retval->successor[entries++]
381             = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
382                                   cnt, language, territory, codeset,
383                                   normalized_codeset, modifier, special,
384                                   sponsor, revision, filename, 1);
385       }
386   retval->successor[entries] = NULL;
387
388   return retval;
389 }
390 \f
391 /* Normalize codeset name.  There is no standard for the codeset
392    names.  Normalization allows the user to use any of the common
393    names.  The return value is dynamically allocated and has to be
394    freed by the caller.  */
395 const char *
396 _nl_normalize_codeset (codeset, name_len)
397      const char *codeset;
398      size_t name_len;
399 {
400   int len = 0;
401   int only_digit = 1;
402   char *retval;
403   char *wp;
404   size_t cnt;
405
406   for (cnt = 0; cnt < name_len; ++cnt)
407     if (isalnum ((unsigned char) codeset[cnt]))
408       {
409         ++len;
410
411         if (isalpha ((unsigned char) codeset[cnt]))
412           only_digit = 0;
413       }
414
415   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
416
417   if (retval != NULL)
418     {
419       if (only_digit)
420         wp = stpcpy (retval, "iso");
421       else
422         wp = retval;
423
424       for (cnt = 0; cnt < name_len; ++cnt)
425         if (isalpha ((unsigned char) codeset[cnt]))
426           *wp++ = tolower ((unsigned char) codeset[cnt]);
427         else if (isdigit ((unsigned char) codeset[cnt]))
428           *wp++ = codeset[cnt];
429
430       *wp = '\0';
431     }
432
433   return (const char *) retval;
434 }
435
436
437 /* @@ begin of epilog @@ */
438
439 /* We don't want libintl.a to depend on any other library.  So we
440    avoid the non-standard function stpcpy.  In GNU C Library this
441    function is available, though.  Also allow the symbol HAVE_STPCPY
442    to be defined.  */
443 #if !_LIBC && !HAVE_STPCPY
444 static char *
445 stpcpy (dest, src)
446      char *dest;
447      const char *src;
448 {
449   while ((*dest++ = *src++) != '\0')
450     /* Do nothing. */ ;
451   return dest - 1;
452 }
453 #endif