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