1 /* Copyright (C) 1995-1999, 2000-2007 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 Lesser General Public License as published by
6    the Free Software Foundation; either version 2.1 of the License, or
7    (at your option) 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 Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16 
17 /* Tell glibc's <string.h> to provide a prototype for stpcpy().
18    This must come before <config.h> because <config.h> may include
19    <features.h>, and once <features.h> has been included, it's too late.  */
20 #ifndef _GNU_SOURCE
21 # define _GNU_SOURCE	1
22 #endif
23 
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27 
28 #include <string.h>
29 
30 #if defined _LIBC || defined HAVE_ARGZ_H
31 # include <argz.h>
32 #endif
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <stdlib.h>
36 
37 #include "loadinfo.h"
38 
39 /* On some strange systems still no definition of NULL is found.  Sigh!  */
40 #ifndef NULL
41 # if defined __STDC__ && __STDC__
42 #  define NULL ((void *) 0)
43 # else
44 #  define NULL 0
45 # endif
46 #endif
47 
48 /* @@ end of prolog @@ */
49 
50 #ifdef _LIBC
51 /* Rename the non ANSI C functions.  This is required by the standard
52    because some ANSI C functions will require linking with this object
53    file and the name space must not be polluted.  */
54 # ifndef stpcpy
55 #  define stpcpy(dest, src) __stpcpy(dest, src)
56 # endif
57 #else
58 # ifndef HAVE_STPCPY
59 static char *stpcpy (char *dest, const char *src);
60 # endif
61 #endif
62 
63 /* Pathname support.
64    ISSLASH(C)           tests whether C is a directory separator character.
65    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
66                         it may be concatenated to a directory pathname.
67  */
68 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
69   /* Win32, Cygwin, OS/2, DOS */
70 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
71 # define HAS_DEVICE(P) \
72     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
73      && (P)[1] == ':')
74 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
75 #else
76   /* Unix */
77 # define ISSLASH(C) ((C) == '/')
78 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
79 #endif
80 
81 /* Define function which are usually not available.  */
82 
83 #ifdef _LIBC
84 # define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
85 #elif defined HAVE_ARGZ_COUNT
86 # undef __argz_count
87 # define __argz_count argz_count
88 #else
89 /* Returns the number of strings in ARGZ.  */
90 static size_t
argz_count__(const char * argz,size_t len)91 argz_count__ (const char *argz, size_t len)
92 {
93   size_t count = 0;
94   while (len > 0)
95     {
96       size_t part_len = strlen (argz);
97       argz += part_len + 1;
98       len -= part_len + 1;
99       count++;
100     }
101   return count;
102 }
103 # undef __argz_count
104 # define __argz_count(argz, len) argz_count__ (argz, len)
105 #endif	/* !_LIBC && !HAVE_ARGZ_COUNT */
106 
107 #ifdef _LIBC
108 # define __argz_stringify(argz, len, sep) \
109   INTUSE(__argz_stringify) (argz, len, sep)
110 #elif defined HAVE_ARGZ_STRINGIFY
111 # undef __argz_stringify
112 # define __argz_stringify argz_stringify
113 #else
114 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
115    except the last into the character SEP.  */
116 static void
argz_stringify__(char * argz,size_t len,int sep)117 argz_stringify__ (char *argz, size_t len, int sep)
118 {
119   while (len > 0)
120     {
121       size_t part_len = strlen (argz);
122       argz += part_len;
123       len -= part_len + 1;
124       if (len > 0)
125 	*argz++ = sep;
126     }
127 }
128 # undef __argz_stringify
129 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
130 #endif	/* !_LIBC && !HAVE_ARGZ_STRINGIFY */
131 
132 #ifdef _LIBC
133 #elif defined HAVE_ARGZ_NEXT
134 # undef __argz_next
135 # define __argz_next argz_next
136 #else
137 static char *
argz_next__(char * argz,size_t argz_len,const char * entry)138 argz_next__ (char *argz, size_t argz_len, const char *entry)
139 {
140   if (entry)
141     {
142       if (entry < argz + argz_len)
143         entry = strchr (entry, '\0') + 1;
144 
145       return entry >= argz + argz_len ? NULL : (char *) entry;
146     }
147   else
148     if (argz_len > 0)
149       return argz;
150     else
151       return 0;
152 }
153 # undef __argz_next
154 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
155 #endif	/* !_LIBC && !HAVE_ARGZ_NEXT */
156 
157 
158 /* Return number of bits set in X.  */
159 static inline int
pop(int x)160 pop (int x)
161 {
162   /* We assume that no more than 16 bits are used.  */
163   x = ((x & ~0x5555) >> 1) + (x & 0x5555);
164   x = ((x & ~0x3333) >> 2) + (x & 0x3333);
165   x = ((x >> 4) + x) & 0x0f0f;
166   x = ((x >> 8) + x) & 0xff;
167 
168   return x;
169 }
170 
171 
172 struct loaded_l10nfile *
_nl_make_l10nflist(struct loaded_l10nfile ** l10nfile_list,const char * dirlist,size_t dirlist_len,int mask,const char * language,const char * territory,const char * codeset,const char * normalized_codeset,const char * modifier,const char * filename,int do_allocate)173 _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
174 		    const char *dirlist, size_t dirlist_len,
175 		    int mask, const char *language, const char *territory,
176 		    const char *codeset, const char *normalized_codeset,
177 		    const char *modifier,
178 		    const char *filename, int do_allocate)
179 {
180   char *abs_filename;
181   struct loaded_l10nfile **lastp;
182   struct loaded_l10nfile *retval;
183   char *cp;
184   size_t dirlist_count;
185   size_t entries;
186   int cnt;
187 
188   /* If LANGUAGE contains an absolute directory specification, we ignore
189      DIRLIST.  */
190   if (IS_ABSOLUTE_PATH (language))
191     dirlist_len = 0;
192 
193   /* Allocate room for the full file name.  */
194   abs_filename = (char *) malloc (dirlist_len
195 				  + strlen (language)
196 				  + ((mask & XPG_TERRITORY) != 0
197 				     ? strlen (territory) + 1 : 0)
198 				  + ((mask & XPG_CODESET) != 0
199 				     ? strlen (codeset) + 1 : 0)
200 				  + ((mask & XPG_NORM_CODESET) != 0
201 				     ? strlen (normalized_codeset) + 1 : 0)
202 				  + ((mask & XPG_MODIFIER) != 0
203 				     ? strlen (modifier) + 1 : 0)
204 				  + 1 + strlen (filename) + 1);
205 
206   if (abs_filename == NULL)
207     return NULL;
208 
209   /* Construct file name.  */
210   cp = abs_filename;
211   if (dirlist_len > 0)
212     {
213       memcpy (cp, dirlist, dirlist_len);
214       __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
215       cp += dirlist_len;
216       cp[-1] = '/';
217     }
218 
219   cp = stpcpy (cp, language);
220 
221   if ((mask & XPG_TERRITORY) != 0)
222     {
223       *cp++ = '_';
224       cp = stpcpy (cp, territory);
225     }
226   if ((mask & XPG_CODESET) != 0)
227     {
228       *cp++ = '.';
229       cp = stpcpy (cp, codeset);
230     }
231   if ((mask & XPG_NORM_CODESET) != 0)
232     {
233       *cp++ = '.';
234       cp = stpcpy (cp, normalized_codeset);
235     }
236   if ((mask & XPG_MODIFIER) != 0)
237     {
238       *cp++ = '@';
239       cp = stpcpy (cp, modifier);
240     }
241 
242   *cp++ = '/';
243   stpcpy (cp, filename);
244 
245   /* Look in list of already loaded domains whether it is already
246      available.  */
247   lastp = l10nfile_list;
248   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
249     if (retval->filename != NULL)
250       {
251 	int compare = strcmp (retval->filename, abs_filename);
252 	if (compare == 0)
253 	  /* We found it!  */
254 	  break;
255 	if (compare < 0)
256 	  {
257 	    /* It's not in the list.  */
258 	    retval = NULL;
259 	    break;
260 	  }
261 
262 	lastp = &retval->next;
263       }
264 
265   if (retval != NULL || do_allocate == 0)
266     {
267       free (abs_filename);
268       return retval;
269     }
270 
271   dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
272 
273   /* Allocate a new loaded_l10nfile.  */
274   retval =
275     (struct loaded_l10nfile *)
276     malloc (sizeof (*retval)
277 	    + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
278 	       * sizeof (struct loaded_l10nfile *)));
279   if (retval == NULL)
280     {
281       free (abs_filename);
282       return NULL;
283     }
284 
285   retval->filename = abs_filename;
286 
287   /* We set retval->data to NULL here; it is filled in later.
288      Setting retval->decided to 1 here means that retval does not
289      correspond to a real file (dirlist_count > 1) or is not worth
290      looking up (if an unnormalized codeset was specified).  */
291   retval->decided = (dirlist_count > 1
292 		     || ((mask & XPG_CODESET) != 0
293 			 && (mask & XPG_NORM_CODESET) != 0));
294   retval->data = NULL;
295 
296   retval->next = *lastp;
297   *lastp = retval;
298 
299   entries = 0;
300   /* Recurse to fill the inheritance list of RETVAL.
301      If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
302      entry does not correspond to a real file; retval->filename contains
303      colons.  In this case we loop across all elements of DIRLIST and
304      across all bit patterns dominated by MASK.
305      If the DIRLIST is a single directory or entirely redundant (i.e.
306      DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
307      MASK, excluding MASK itself.
308      In either case, we loop down from MASK to 0.  This has the effect
309      that the extra bits in the locale name are dropped in this order:
310      first the modifier, then the territory, then the codeset, then the
311      normalized_codeset.  */
312   for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
313     if ((cnt & ~mask) == 0
314 	&& !((cnt & XPG_CODESET) != 0 && (cnt & XPG_NORM_CODESET) != 0))
315       {
316 	if (dirlist_count > 1)
317 	  {
318 	    /* Iterate over all elements of the DIRLIST.  */
319 	    char *dir = NULL;
320 
321 	    while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
322 		   != NULL)
323 	      retval->successor[entries++]
324 		= _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
325 				      cnt, language, territory, codeset,
326 				      normalized_codeset, modifier, filename,
327 				      1);
328 	  }
329 	else
330 	  retval->successor[entries++]
331 	    = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
332 				  cnt, language, territory, codeset,
333 				  normalized_codeset, modifier, filename, 1);
334       }
335   retval->successor[entries] = NULL;
336 
337   return retval;
338 }
339 
340 /* Normalize codeset name.  There is no standard for the codeset
341    names.  Normalization allows the user to use any of the common
342    names.  The return value is dynamically allocated and has to be
343    freed by the caller.  */
344 const char *
_nl_normalize_codeset(const char * codeset,size_t name_len)345 _nl_normalize_codeset (const char *codeset, size_t name_len)
346 {
347   size_t len = 0;
348   int only_digit = 1;
349   char *retval;
350   char *wp;
351   size_t cnt;
352 
353   for (cnt = 0; cnt < name_len; ++cnt)
354     if (isalnum ((unsigned char) codeset[cnt]))
355       {
356 	++len;
357 
358 	if (isalpha ((unsigned char) codeset[cnt]))
359 	  only_digit = 0;
360       }
361 
362   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
363 
364   if (retval != NULL)
365     {
366       if (only_digit)
367 	wp = stpcpy (retval, "iso");
368       else
369 	wp = retval;
370 
371       for (cnt = 0; cnt < name_len; ++cnt)
372 	if (isalpha ((unsigned char) codeset[cnt]))
373 	  *wp++ = tolower ((unsigned char) codeset[cnt]);
374 	else if (isdigit ((unsigned char) codeset[cnt]))
375 	  *wp++ = codeset[cnt];
376 
377       *wp = '\0';
378     }
379 
380   return (const char *) retval;
381 }
382 
383 
384 /* @@ begin of epilog @@ */
385 
386 /* We don't want libintl.a to depend on any other library.  So we
387    avoid the non-standard function stpcpy.  In GNU C Library this
388    function is available, though.  Also allow the symbol HAVE_STPCPY
389    to be defined.  */
390 #if !_LIBC && !HAVE_STPCPY
391 static char *
stpcpy(char * dest,const char * src)392 stpcpy (char *dest, const char *src)
393 {
394   while ((*dest++ = *src++) != '\0')
395     /* Do nothing. */ ;
396   return dest - 1;
397 }
398 #endif
399