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