x86-64: Require BMI2 for AVX2 strcmp implementation
[glibc.git] / intl / l10nflist.c
blob078a450dfec21faf2d26dc5d0cb02158c1f23229
1 /* Copyright (C) 1995-2022 Free Software Foundation, Inc.
2 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
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.
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.
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/>. */
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
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
28 #include <string.h>
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>
37 #include "loadinfo.h"
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
48 /* @@ end of prolog @@ */
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
63 /* Define function which are usually not available. */
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
71 argz_count__ (const char *argz, size_t len)
73 size_t count = 0;
74 while (len > 0)
76 size_t part_len = strlen (argz);
77 argz += part_len + 1;
78 len -= part_len + 1;
79 count++;
81 return count;
83 # undef __argz_count
84 # define __argz_count(argz, len) argz_count__ (argz, len)
85 #endif /* !_LIBC && !HAVE_ARGZ_COUNT */
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
94 argz_stringify__ (char *argz, size_t len, int sep)
96 while (len > 0)
98 size_t part_len = strlen (argz);
99 argz += part_len;
100 len -= part_len + 1;
101 if (len > 0)
102 *argz++ = sep;
105 # undef __argz_stringify
106 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
107 #endif /* !_LIBC && !HAVE_ARGZ_STRINGIFY */
109 #ifdef _LIBC
110 #elif defined HAVE_ARGZ_NEXT
111 # undef __argz_next
112 # define __argz_next argz_next
113 #else
114 static char *
115 argz_next__ (char *argz, size_t argz_len, const char *entry)
117 if (entry)
119 if (entry < argz + argz_len)
120 entry = strchr (entry, '\0') + 1;
122 return entry >= argz + argz_len ? NULL : (char *) entry;
124 else
125 if (argz_len > 0)
126 return argz;
127 else
128 return 0;
130 # undef __argz_next
131 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
132 #endif /* !_LIBC && !HAVE_ARGZ_NEXT */
134 /* Return number of bits set in X. */
135 #ifndef ARCH_POP
136 static inline int
137 pop (int x)
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;
145 return x;
147 #endif
150 struct loaded_l10nfile *
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)
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;
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);
178 if (abs_filename == NULL)
179 return NULL;
181 retval = NULL;
182 last = NULL;
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);
191 if ((mask & XPG_TERRITORY) != 0)
193 *cp++ = '_';
194 cp = stpcpy (cp, territory);
196 if ((mask & XPG_CODESET) != 0)
198 *cp++ = '.';
199 cp = stpcpy (cp, codeset);
201 if ((mask & XPG_NORM_CODESET) != 0)
203 *cp++ = '.';
204 cp = stpcpy (cp, normalized_codeset);
206 if ((mask & XPG_MODIFIER) != 0)
208 *cp++ = '@';
209 cp = stpcpy (cp, modifier);
212 *cp++ = '/';
213 stpcpy (cp, filename);
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)
221 int compare = strcmp (retval->filename, abs_filename);
222 if (compare == 0)
223 /* We found it! */
224 break;
225 if (compare < 0)
227 /* It's not in the list. */
228 retval = NULL;
229 break;
232 last = retval;
235 if (retval != NULL || do_allocate == 0)
237 free (abs_filename);
238 return retval;
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)
247 free (abs_filename);
248 return NULL;
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;
260 if (last == NULL)
262 retval->next = *l10nfile_list;
263 *l10nfile_list = retval;
265 else
267 retval->next = last->next;
268 last->next = retval;
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)
279 /* Iterate over all elements of the DIRLIST. */
280 char *dir = NULL;
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);
289 retval->successor[entries] = NULL;
291 return retval;
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 *
299 _nl_normalize_codeset (const char *codeset, size_t name_len)
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
312 for (cnt = 0; cnt < name_len; ++cnt)
313 if (__isalnum_l ((unsigned char) codeset[cnt], locale))
315 ++len;
317 if (! __isdigit_l ((unsigned char) codeset[cnt], locale))
318 only_digit = 0;
321 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
323 if (retval != NULL)
325 if (only_digit)
326 wp = stpcpy (retval, "iso");
327 else
328 wp = retval;
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];
336 *wp = '\0';
339 return (const char *) retval;
343 /* @@ begin of epilog @@ */
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 *
351 stpcpy (char *dest, const char *src)
353 while ((*dest++ = *src++) != '\0')
354 /* Do nothing. */ ;
355 return dest - 1;
357 #endif