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
argz_count__(argz,len)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
argz_stringify__(argz,len,sep)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 *
argz_next__(argz,argz_len,entry)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
pop(x)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 
168 struct loaded_l10nfile *
_nl_make_l10nflist(l10nfile_list,dirlist,dirlist_len,mask,language,territory,codeset,normalized_codeset,modifier,special,sponsor,revision,filename,do_allocate)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 
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 *
_nl_normalize_codeset(codeset,name_len)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 *
stpcpy(dest,src)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
410