1 /* Copyright (C) 1995-2022 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 || 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 /* Define function which are usually not available.  */
64 
65 #if defined HAVE_ARGZ_COUNT
66 # undef __argz_count
67 # define __argz_count argz_count
68 #else
69 /* Returns the number of strings in ARGZ.  */
70 static size_t
argz_count__(const char * argz,size_t len)71 argz_count__ (const char *argz, size_t len)
72 {
73   size_t count = 0;
74   while (len > 0)
75     {
76       size_t part_len = strlen (argz);
77       argz += part_len + 1;
78       len -= part_len + 1;
79       count++;
80     }
81   return count;
82 }
83 # undef __argz_count
84 # define __argz_count(argz, len) argz_count__ (argz, len)
85 #endif	/* !_LIBC && !HAVE_ARGZ_COUNT */
86 
87 #if defined HAVE_ARGZ_STRINGIFY
88 # undef __argz_stringify
89 # define __argz_stringify argz_stringify
90 #else
91 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
92    except the last into the character SEP.  */
93 static void
argz_stringify__(char * argz,size_t len,int sep)94 argz_stringify__ (char *argz, size_t len, int sep)
95 {
96   while (len > 0)
97     {
98       size_t part_len = strlen (argz);
99       argz += part_len;
100       len -= part_len + 1;
101       if (len > 0)
102 	*argz++ = sep;
103     }
104 }
105 # undef __argz_stringify
106 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
107 #endif	/* !_LIBC && !HAVE_ARGZ_STRINGIFY */
108 
109 #ifdef _LIBC
110 #elif defined HAVE_ARGZ_NEXT
111 # undef __argz_next
112 # define __argz_next argz_next
113 #else
114 static char *
argz_next__(char * argz,size_t argz_len,const char * entry)115 argz_next__ (char *argz, size_t argz_len, const char *entry)
116 {
117   if (entry)
118     {
119       if (entry < argz + argz_len)
120         entry = strchr (entry, '\0') + 1;
121 
122       return entry >= argz + argz_len ? NULL : (char *) entry;
123     }
124   else
125     if (argz_len > 0)
126       return argz;
127     else
128       return 0;
129 }
130 # undef __argz_next
131 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
132 #endif	/* !_LIBC && !HAVE_ARGZ_NEXT */
133 
134 /* Return number of bits set in X.  */
135 #ifndef ARCH_POP
136 static inline int
pop(int x)137 pop (int x)
138 {
139   /* We assume that no more than 16 bits are used.  */
140   x = ((x & ~0x5555) >> 1) + (x & 0x5555);
141   x = ((x & ~0x3333) >> 2) + (x & 0x3333);
142   x = ((x >> 4) + x) & 0x0f0f;
143   x = ((x >> 8) + x) & 0xff;
144 
145   return x;
146 }
147 #endif
148 
149 
150 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)151 _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
152 		    const char *dirlist, size_t dirlist_len,
153 		    int mask, const char *language, const char *territory,
154 		    const char *codeset, const char *normalized_codeset,
155 		    const char *modifier,
156 		    const char *filename, int do_allocate)
157 {
158   char *abs_filename;
159   struct loaded_l10nfile *last = NULL;
160   struct loaded_l10nfile *retval;
161   char *cp;
162   size_t entries;
163   int cnt;
164 
165   /* Allocate room for the full file name.  */
166   abs_filename = (char *) malloc (dirlist_len
167 				  + strlen (language)
168 				  + ((mask & XPG_TERRITORY) != 0
169 				     ? strlen (territory) + 1 : 0)
170 				  + ((mask & XPG_CODESET) != 0
171 				     ? strlen (codeset) + 1 : 0)
172 				  + ((mask & XPG_NORM_CODESET) != 0
173 				     ? strlen (normalized_codeset) + 1 : 0)
174 				  + ((mask & XPG_MODIFIER) != 0
175 				     ? strlen (modifier) + 1 : 0)
176 				  + 1 + strlen (filename) + 1);
177 
178   if (abs_filename == NULL)
179     return NULL;
180 
181   retval = NULL;
182   last = NULL;
183 
184   /* Construct file name.  */
185   memcpy (abs_filename, dirlist, dirlist_len);
186   __argz_stringify (abs_filename, dirlist_len, ':');
187   cp = abs_filename + (dirlist_len - 1);
188   *cp++ = '/';
189   cp = stpcpy (cp, language);
190 
191   if ((mask & XPG_TERRITORY) != 0)
192     {
193       *cp++ = '_';
194       cp = stpcpy (cp, territory);
195     }
196   if ((mask & XPG_CODESET) != 0)
197     {
198       *cp++ = '.';
199       cp = stpcpy (cp, codeset);
200     }
201   if ((mask & XPG_NORM_CODESET) != 0)
202     {
203       *cp++ = '.';
204       cp = stpcpy (cp, normalized_codeset);
205     }
206   if ((mask & XPG_MODIFIER) != 0)
207     {
208       *cp++ = '@';
209       cp = stpcpy (cp, modifier);
210     }
211 
212   *cp++ = '/';
213   stpcpy (cp, filename);
214 
215   /* Look in list of already loaded domains whether it is already
216      available.  */
217   last = NULL;
218   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
219     if (retval->filename != NULL)
220       {
221 	int compare = strcmp (retval->filename, abs_filename);
222 	if (compare == 0)
223 	  /* We found it!  */
224 	  break;
225 	if (compare < 0)
226 	  {
227 	    /* It's not in the list.  */
228 	    retval = NULL;
229 	    break;
230 	  }
231 
232 	last = retval;
233       }
234 
235   if (retval != NULL || do_allocate == 0)
236     {
237       free (abs_filename);
238       return retval;
239     }
240 
241   retval = (struct loaded_l10nfile *)
242     malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
243 				* (1 << pop (mask))
244 				* sizeof (struct loaded_l10nfile *)));
245   if (retval == NULL)
246     {
247       free (abs_filename);
248       return NULL;
249     }
250 
251   retval->filename = abs_filename;
252   /* If more than one directory is in the list this is a pseudo-entry
253      which just references others.  We do not try to load data for it,
254      ever.  */
255   retval->decided = (__argz_count (dirlist, dirlist_len) != 1
256 		     || ((mask & XPG_CODESET) != 0
257 			 && (mask & XPG_NORM_CODESET) != 0));
258   retval->data = NULL;
259 
260   if (last == NULL)
261     {
262       retval->next = *l10nfile_list;
263       *l10nfile_list = retval;
264     }
265   else
266     {
267       retval->next = last->next;
268       last->next = retval;
269     }
270 
271   entries = 0;
272   /* If the DIRLIST is a real list the RETVAL entry corresponds not to
273      a real file.  So we have to use the DIRLIST separation mechanism
274      of the inner loop.  */
275   cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
276   for (; cnt >= 0; --cnt)
277     if ((cnt & ~mask) == 0)
278       {
279 	/* Iterate over all elements of the DIRLIST.  */
280 	char *dir = NULL;
281 
282 	while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
283 	       != NULL)
284 	  retval->successor[entries++]
285 	    = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
286 				  language, territory, codeset,
287 				  normalized_codeset, modifier, filename, 1);
288       }
289   retval->successor[entries] = NULL;
290 
291   return retval;
292 }
293 
294 /* Normalize codeset name.  There is no standard for the codeset
295    names.  Normalization allows the user to use any of the common
296    names.  The return value is dynamically allocated and has to be
297    freed by the caller.  */
298 const char *
_nl_normalize_codeset(const char * codeset,size_t name_len)299 _nl_normalize_codeset (const char *codeset, size_t name_len)
300 {
301   size_t len = 0;
302   int only_digit = 1;
303   char *retval;
304   char *wp;
305   size_t cnt;
306 #if !IS_IN (libc)
307   locale_t locale = newlocale (0, "C", NULL);
308 #else
309 # define locale _nl_C_locobj_ptr
310 #endif
311 
312   for (cnt = 0; cnt < name_len; ++cnt)
313     if (__isalnum_l ((unsigned char) codeset[cnt], locale))
314       {
315 	++len;
316 
317 	if (! __isdigit_l ((unsigned char) codeset[cnt], locale))
318 	  only_digit = 0;
319       }
320 
321   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
322 
323   if (retval != NULL)
324     {
325       if (only_digit)
326 	wp = stpcpy (retval, "iso");
327       else
328 	wp = retval;
329 
330       for (cnt = 0; cnt < name_len; ++cnt)
331 	if (__isalpha_l ((unsigned char) codeset[cnt], locale))
332 	  *wp++ = __tolower_l ((unsigned char) codeset[cnt], locale);
333 	else if (__isdigit_l ((unsigned char) codeset[cnt], locale))
334 	  *wp++ = codeset[cnt];
335 
336       *wp = '\0';
337     }
338 
339   return (const char *) retval;
340 }
341 
342 
343 /* @@ begin of epilog @@ */
344 
345 /* We don't want libintl.a to depend on any other library.  So we
346    avoid the non-standard function stpcpy.  In GNU C Library this
347    function is available, though.  Also allow the symbol HAVE_STPCPY
348    to be defined.  */
349 #if !_LIBC && !HAVE_STPCPY
350 static char *
stpcpy(char * dest,const char * src)351 stpcpy (char *dest, const char *src)
352 {
353   while ((*dest++ = *src++) != '\0')
354     /* Do nothing. */ ;
355   return dest - 1;
356 }
357 #endif
358