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