Updated from ../gpl2lgpl.sed
[kopensolaris-gnu/glibc.git] / intl / l10nflist.c
1 /* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
2 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
3
4 This file is part of the GNU C Library.  Its master source is NOT part of
5 the C library, however.  The master source lives in /gd/gnu/lib.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with the GNU C Library; see the file COPYING.LIB.  If
19 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
20 Cambridge, MA 02139, USA.  */
21
22 #if defined _LIBC && (defined __ARGZ_COUNT || defined __ARGZ_STRINGIFY)
23 # include <argz.h>
24 #endif
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "loadinfo.h"
30
31 /* Define function which are usually not available.  */
32
33 #if !defined _LIBC && !defined __ARGZ_COUNT
34 /* Returns the number of strings in ARGZ.  */
35 static size_t __argz_count __P ((const char *argz, size_t len));
36
37 static size_t
38 __argz_count (argz, len)
39      const char *argz;
40      size_t len;
41 {
42   size_t count = 0;
43   while (len > 0)
44     {
45       size_t part_len = strlen(argz);
46       argz += part_len + 1;
47       len -= part_len + 1;
48       count++;
49     }
50   return count;
51 }
52 #endif  /* !_LIBC && !__ARGZ_COUNT */
53
54 #if !defined _LIBC && !defined __ARGZ_STRINGIFY
55 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
56    except the last into the character SEP.  */
57 static void __argz_stringify __P ((char *argz, size_t len, int sep));
58
59 static void
60 __argz_stringify (argz, len, sep)
61      char *argz;
62      size_t len;
63      int sep;
64 {
65   while (len > 0)
66     {
67       size_t part_len = strlen(argz);
68       argz += part_len;
69       len -= part_len + 1;
70       if (len > 0)
71         *argz++ = sep;
72     }
73 }
74 #endif  /* !_LIBC && !__ARGZ_COUNT */
75
76 #if !defined _LIBC && !defined __ARGZ_NEXT
77 static char *
78 __argz_next (char *argz, size_t argz_len, const char *entry)
79 {
80   if (entry)
81     {
82       if (entry < argz + argz_len)
83         entry = strchr (entry, '\0') + 1;
84
85       return entry >= argz + argz_len ? NULL : (char *) entry;
86     }
87   else
88     if (argz_len > 0)
89       return argz;
90     else
91       return 0;
92 }
93 #endif
94
95
96 /* Return number of bits set in X.  */
97 static int pop __P ((int x));
98
99 static inline int
100 pop (x)
101      int x;
102 {
103   /* We assume that no more than 16 bits are used.  */
104   x = ((x & ~0x5555) >> 1) + (x & 0x5555);
105   x = ((x & ~0x3333) >> 2) + (x & 0x3333);
106   x = ((x >> 4) + x) & 0x0f0f;
107   x = ((x >> 8) + x) & 0xff;
108
109   return x;
110 }
111
112 \f
113 struct loaded_l10nfile *
114 _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
115                     territory, codeset, normalized_codeset, modifier, special,
116                     sponsor, revision, filename, do_allocate)
117      struct loaded_l10nfile **l10nfile_list;
118      const char *dirlist;
119      size_t dirlist_len;
120      int mask;
121      const char *language;
122      const char *territory;
123      const char *codeset;
124      const char *normalized_codeset;
125      const char *modifier;
126      const char *special;
127      const char *sponsor;
128      const char *revision;
129      const char *filename;
130      int do_allocate;
131 {
132   char *abs_filename;
133   struct loaded_l10nfile *last = NULL;
134   struct loaded_l10nfile *retval;
135   char *cp;
136   size_t entries;
137   int cnt;
138
139   /* Allocate room for the full file name.  */
140   abs_filename = (char *) malloc (dirlist_len
141                                   + strlen (language)
142                                   + ((mask & TERRITORY) != 0
143                                      ? strlen (territory) + 1 : 0)
144                                   + ((mask & XPG_CODESET) != 0
145                                      ? strlen (codeset) + 1 : 0)
146                                   + ((mask & XPG_NORM_CODESET) != 0
147                                      ? strlen (normalized_codeset) + 1 : 0)
148                                   + (((mask & XPG_MODIFIER) != 0
149                                       || (mask & CEN_AUDIENCE) != 0) ?
150                                      strlen (modifier) + 1 : 0)
151                                   + ((mask & CEN_SPECIAL) != 0
152                                      ? strlen (special) + 1 : 0)
153                                   + ((mask & CEN_SPONSOR) != 0
154                                      ? strlen (sponsor) + 1 : 0)
155                                   + ((mask & CEN_REVISION) != 0
156                                      ? strlen (revision) + 1 : 0)
157                                   + 1 + strlen (filename) + 1);
158
159   if (abs_filename == NULL)
160     return NULL;
161
162   retval = NULL;
163   last = NULL;
164
165   /* Construct file name.  */
166   memcpy (abs_filename, dirlist, dirlist_len);
167   __argz_stringify (abs_filename, dirlist_len, ':');
168   cp = abs_filename + (dirlist_len - 1);
169   *cp++ = '/';
170   cp = stpcpy (cp, language);
171
172   if ((mask & TERRITORY) != 0)
173     {
174       *cp++ = '_';
175       cp = stpcpy (cp, territory);
176     }
177   if ((mask & XPG_CODESET) != 0)
178     {
179       *cp++ = '.';
180       cp = stpcpy (cp, codeset);
181     }
182   if ((mask & XPG_NORM_CODESET) != 0)
183     {
184       *cp++ = '.';
185       cp = stpcpy (cp, normalized_codeset);
186     }
187   if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
188     {
189       /* This component can be part of both syntaces but has different
190          leading characters.  For CEN we use `+', else `@'.  */
191       *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
192       cp = stpcpy (cp, modifier);
193     }
194   if ((mask & CEN_SPECIAL) != 0)
195     {
196       *cp++ = '+';
197       cp = stpcpy (cp, special);
198     }
199   if ((mask & CEN_SPONSOR) != 0)
200     {
201       *cp++ = ',';
202       cp = stpcpy (cp, sponsor);
203     }
204   if ((mask & CEN_REVISION) != 0)
205     {
206       *cp++ = '_';
207       cp = stpcpy (cp, revision);
208     }
209
210   *cp++ = '/';
211   stpcpy (cp, filename);
212
213   /* Look in list of already loaded domains whether it is already
214      available.  */
215   last = NULL;
216   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
217     if (retval->filename != NULL)
218       {
219         int compare = strcmp (retval->filename, abs_filename);
220         if (compare == 0)
221           /* We found it!  */
222           break;
223         if (compare < 0)
224           {
225             /* It's not in the list.  */
226             retval = NULL;
227             break;
228           }
229
230         last = retval;
231       }
232
233   if (retval != NULL || do_allocate == 0)
234     {
235       free (abs_filename);
236       return retval;
237     }
238
239   retval = (struct loaded_l10nfile *)
240     malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
241                                 * (1 << pop (mask))
242                                 * sizeof (struct loaded_l10nfile *)));
243   if (retval == NULL)
244     return NULL;
245
246   retval->filename = abs_filename;
247   retval->decided = (__argz_count (dirlist, dirlist_len) != 1
248                      || ((mask & XPG_CODESET) != 0
249                          && (mask & XPG_NORM_CODESET) != 0));
250   retval->data = NULL;
251
252   if (last == NULL)
253     {
254       retval->next = *l10nfile_list;
255       *l10nfile_list = retval;
256     }
257   else
258     {
259       retval->next = last->next;
260       last->next = retval;
261     }
262
263   entries = 0;
264   /* If the DIRLIST is a real list the RETVAL entry correcponds not to
265      a real file.  So we have to use the DIRLIST separation machanism
266      of the inner loop.  */
267   cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
268   for (; cnt >= 0; --cnt)
269     if ((cnt & ~mask) == 0
270         && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
271         && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
272       {
273         /* Iterate over all elements of the DIRLIST.  */
274         char *dir = NULL;
275
276         while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
277                != NULL)
278           retval->successor[entries++]
279             = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
280                                   language, territory, codeset,
281                                   normalized_codeset, modifier, special,
282                                   sponsor, revision, filename, 1);
283       }
284   retval->successor[entries] = NULL;
285
286   return retval;
287 }
288 \f
289 /* Normalize codeset name.  There is no standard for the codeset
290    names.  Normalization allows the user to use any of the common
291    names.  */
292 const char *
293 _nl_normalize_codeset (codeset, name_len)
294      const char *codeset;
295      size_t name_len;
296 {
297   int len = 0;
298   int only_digit = 1;
299   char *retval;
300   char *wp;
301   size_t cnt;
302
303   for (cnt = 0; cnt < name_len; ++cnt)
304     if (isalnum (codeset[cnt]))
305       {
306         ++len;
307
308         if (isalpha (codeset[cnt]))
309           only_digit = 0;
310       }
311
312   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
313
314   if (retval != NULL)
315     {
316       if (only_digit)
317         wp = stpcpy (retval, "ISO");
318       else
319         wp = retval;
320
321       for (cnt = 0; cnt < name_len; ++cnt)
322         if (isalpha (codeset[cnt]))
323           *wp++ = tolower (codeset[cnt]);
324         else if (isdigit (codeset[cnt]))
325           *wp++ = codeset[cnt];
326
327       *wp = '\0';
328     }
329
330   return (const char *) retval;
331 }