1 /*	$NetBSD: l10nflist.c,v 1.1.1.1 2016/01/14 00:11:28 christos Exp $	*/
2 
3 /* Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc.
4    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
5 
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU Library General Public License as published
8    by the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15 
16    You should have received a copy of the GNU Library General Public
17    License along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19    USA.  */
20 
21 /* Tell glibc's <string.h> to provide a prototype for stpcpy().
22    This must come before <config.h> because <config.h> may include
23    <features.h>, and once <features.h> has been included, it's too late.  */
24 #ifndef _GNU_SOURCE
25 # define _GNU_SOURCE	1
26 #endif
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 #include <string.h>
33 
34 #if defined _LIBC || defined HAVE_ARGZ_H
35 # include <argz.h>
36 #endif
37 #include <ctype.h>
38 #include <sys/types.h>
39 #include <stdlib.h>
40 
41 #include "loadinfo.h"
42 
43 /* On some strange systems still no definition of NULL is found.  Sigh!  */
44 #ifndef NULL
45 # if defined __STDC__ && __STDC__
46 #  define NULL ((void *) 0)
47 # else
48 #  define NULL 0
49 # endif
50 #endif
51 
52 /* @@ end of prolog @@ */
53 
54 #ifdef _LIBC
55 /* Rename the non ANSI C functions.  This is required by the standard
56    because some ANSI C functions will require linking with this object
57    file and the name space must not be polluted.  */
58 # ifndef stpcpy
59 #  define stpcpy(dest, src) __stpcpy(dest, src)
60 # endif
61 #else
62 # ifndef HAVE_STPCPY
63 static char *stpcpy (char *dest, const char *src);
64 # endif
65 #endif
66 
67 /* Pathname support.
68    ISSLASH(C)           tests whether C is a directory separator character.
69    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
70                         it may be concatenated to a directory pathname.
71  */
72 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
73   /* Win32, OS/2, DOS */
74 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
75 # define HAS_DEVICE(P) \
76     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
77      && (P)[1] == ':')
78 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
79 #else
80   /* Unix */
81 # define ISSLASH(C) ((C) == '/')
82 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
83 #endif
84 
85 /* Define function which are usually not available.  */
86 
87 #if !defined _LIBC && !defined HAVE___ARGZ_COUNT
88 /* Returns the number of strings in ARGZ.  */
89 static size_t
argz_count__(const char * argz,size_t len)90 argz_count__ (const char *argz, size_t len)
91 {
92   size_t count = 0;
93   while (len > 0)
94     {
95       size_t part_len = strlen (argz);
96       argz += part_len + 1;
97       len -= part_len + 1;
98       count++;
99     }
100   return count;
101 }
102 # undef __argz_count
103 # define __argz_count(argz, len) argz_count__ (argz, len)
104 #else
105 # ifdef _LIBC
106 #  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
107 # endif
108 #endif	/* !_LIBC && !HAVE___ARGZ_COUNT */
109 
110 #if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
111 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
112    except the last into the character SEP.  */
113 static void
argz_stringify__(char * argz,size_t len,int sep)114 argz_stringify__ (char *argz, size_t len, int sep)
115 {
116   while (len > 0)
117     {
118       size_t part_len = strlen (argz);
119       argz += part_len;
120       len -= part_len + 1;
121       if (len > 0)
122 	*argz++ = sep;
123     }
124 }
125 # undef __argz_stringify
126 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
127 #else
128 # ifdef _LIBC
129 #  define __argz_stringify(argz, len, sep) \
130   INTUSE(__argz_stringify) (argz, len, sep)
131 # endif
132 #endif	/* !_LIBC && !HAVE___ARGZ_STRINGIFY */
133 
134 #if !defined _LIBC && !defined HAVE___ARGZ_NEXT
135 static char *
argz_next__(char * argz,size_t argz_len,const char * entry)136 argz_next__ (char *argz, size_t argz_len, const char *entry)
137 {
138   if (entry)
139     {
140       if (entry < argz + argz_len)
141         entry = strchr (entry, '\0') + 1;
142 
143       return entry >= argz + argz_len ? NULL : (char *) entry;
144     }
145   else
146     if (argz_len > 0)
147       return argz;
148     else
149       return 0;
150 }
151 # undef __argz_next
152 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
153 #endif	/* !_LIBC && !HAVE___ARGZ_NEXT */
154 
155 
156 /* Return number of bits set in X.  */
157 static inline int
pop(int x)158 pop (int x)
159 {
160   /* We assume that no more than 16 bits are used.  */
161   x = ((x & ~0x5555) >> 1) + (x & 0x5555);
162   x = ((x & ~0x3333) >> 2) + (x & 0x3333);
163   x = ((x >> 4) + x) & 0x0f0f;
164   x = ((x >> 8) + x) & 0xff;
165 
166   return x;
167 }
168 
169 
170 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 * special,const char * sponsor,const char * revision,const char * filename,int do_allocate)171 _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
172 		    const char *dirlist, size_t dirlist_len,
173 		    int mask, const char *language, const char *territory,
174 		    const char *codeset, const char *normalized_codeset,
175 		    const char *modifier, const char *special,
176 		    const char *sponsor, const char *revision,
177 		    const char *filename, int do_allocate)
178 {
179   char *abs_filename;
180   struct loaded_l10nfile **lastp;
181   struct loaded_l10nfile *retval;
182   char *cp;
183   size_t dirlist_count;
184   size_t entries;
185   int cnt;
186 
187   /* If LANGUAGE contains an absolute directory specification, we ignore
188      DIRLIST.  */
189   if (IS_ABSOLUTE_PATH (language))
190     dirlist_len = 0;
191 
192   /* Allocate room for the full file name.  */
193   abs_filename = (char *) malloc (dirlist_len
194 				  + strlen (language)
195 				  + ((mask & TERRITORY) != 0
196 				     ? strlen (territory) + 1 : 0)
197 				  + ((mask & XPG_CODESET) != 0
198 				     ? strlen (codeset) + 1 : 0)
199 				  + ((mask & XPG_NORM_CODESET) != 0
200 				     ? strlen (normalized_codeset) + 1 : 0)
201 				  + (((mask & XPG_MODIFIER) != 0
202 				      || (mask & CEN_AUDIENCE) != 0)
203 				     ? strlen (modifier) + 1 : 0)
204 				  + ((mask & CEN_SPECIAL) != 0
205 				     ? strlen (special) + 1 : 0)
206 				  + (((mask & CEN_SPONSOR) != 0
207 				      || (mask & CEN_REVISION) != 0)
208 				     ? (1 + ((mask & CEN_SPONSOR) != 0
209 					     ? strlen (sponsor) : 0)
210 					+ ((mask & CEN_REVISION) != 0
211 					   ? strlen (revision) + 1 : 0)) : 0)
212 				  + 1 + strlen (filename) + 1);
213 
214   if (abs_filename == NULL)
215     return NULL;
216 
217   /* Construct file name.  */
218   cp = abs_filename;
219   if (dirlist_len > 0)
220     {
221       memcpy (cp, dirlist, dirlist_len);
222       __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
223       cp += dirlist_len;
224       cp[-1] = '/';
225     }
226 
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   lastp = l10nfile_list;
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 	lastp = &retval->next;
289       }
290 
291   if (retval != NULL || do_allocate == 0)
292     {
293       free (abs_filename);
294       return retval;
295     }
296 
297   dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
298 
299   /* Allocate a new loaded_l10nfile.  */
300   retval =
301     (struct loaded_l10nfile *)
302     malloc (sizeof (*retval)
303 	    + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
304 	       * sizeof (struct loaded_l10nfile *)));
305   if (retval == NULL)
306     return NULL;
307 
308   retval->filename = abs_filename;
309 
310   /* We set retval->data to NULL here; it is filled in later.
311      Setting retval->decided to 1 here means that retval does not
312      correspond to a real file (dirlist_count > 1) or is not worth
313      looking up (if an unnormalized codeset was specified).  */
314   retval->decided = (dirlist_count > 1
315 		     || ((mask & XPG_CODESET) != 0
316 			 && (mask & XPG_NORM_CODESET) != 0));
317   retval->data = NULL;
318 
319   retval->next = *lastp;
320   *lastp = retval;
321 
322   entries = 0;
323   /* Recurse to fill the inheritance list of RETVAL.
324      If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
325      entry does not correspond to a real file; retval->filename contains
326      colons.  In this case we loop across all elements of DIRLIST and
327      across all bit patterns dominated by MASK.
328      If the DIRLIST is a single directory or entirely redundant (i.e.
329      DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
330      MASK, excluding MASK itself.
331      In either case, we loop down from MASK to 0.  This has the effect
332      that the extra bits in the locale name are dropped in this order:
333      first the modifier, then the territory, then the codeset, then the
334      normalized_codeset.  */
335   for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
336     if ((cnt & ~mask) == 0
337 	&& ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
338 	&& ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
339       {
340 	if (dirlist_count > 1)
341 	  {
342 	    /* Iterate over all elements of the DIRLIST.  */
343 	    char *dir = NULL;
344 
345 	    while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
346 		   != NULL)
347 	      retval->successor[entries++]
348 		= _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
349 				      cnt, language, territory, codeset,
350 				      normalized_codeset, modifier, special,
351 				      sponsor, revision, filename, 1);
352 	  }
353 	else
354 	  retval->successor[entries++]
355 	    = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
356 				  cnt, language, territory, codeset,
357 				  normalized_codeset, modifier, special,
358 				  sponsor, revision, filename, 1);
359       }
360   retval->successor[entries] = NULL;
361 
362   return retval;
363 }
364 
365 /* Normalize codeset name.  There is no standard for the codeset
366    names.  Normalization allows the user to use any of the common
367    names.  The return value is dynamically allocated and has to be
368    freed by the caller.  */
369 const char *
_nl_normalize_codeset(const char * codeset,size_t name_len)370 _nl_normalize_codeset (const char *codeset, size_t name_len)
371 {
372   int len = 0;
373   int only_digit = 1;
374   char *retval;
375   char *wp;
376   size_t cnt;
377 
378   for (cnt = 0; cnt < name_len; ++cnt)
379     if (isalnum ((unsigned char) codeset[cnt]))
380       {
381 	++len;
382 
383 	if (isalpha ((unsigned char) codeset[cnt]))
384 	  only_digit = 0;
385       }
386 
387   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
388 
389   if (retval != NULL)
390     {
391       if (only_digit)
392 	wp = stpcpy (retval, "iso");
393       else
394 	wp = retval;
395 
396       for (cnt = 0; cnt < name_len; ++cnt)
397 	if (isalpha ((unsigned char) codeset[cnt]))
398 	  *wp++ = tolower ((unsigned char) codeset[cnt]);
399 	else if (isdigit ((unsigned char) codeset[cnt]))
400 	  *wp++ = codeset[cnt];
401 
402       *wp = '\0';
403     }
404 
405   return (const char *) retval;
406 }
407 
408 
409 /* @@ begin of epilog @@ */
410 
411 /* We don't want libintl.a to depend on any other library.  So we
412    avoid the non-standard function stpcpy.  In GNU C Library this
413    function is available, though.  Also allow the symbol HAVE_STPCPY
414    to be defined.  */
415 #if !_LIBC && !HAVE_STPCPY
416 static char *
stpcpy(char * dest,const char * src)417 stpcpy (char *dest, const char *src)
418 {
419   while ((*dest++ = *src++) != '\0')
420     /* Do nothing. */ ;
421   return dest - 1;
422 }
423 #endif
424