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. */
21 # define _GNU_SOURCE 1
30 #if defined _LIBC || defined HAVE_ARGZ_H
34 #include <sys/types.h>
39 /* On some strange systems still no definition of NULL is found. Sigh! */
41 # if defined __STDC__ && __STDC__
42 # define NULL ((void *) 0)
48 /* @@ end of prolog @@ */
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. */
55 # define stpcpy(dest, src) __stpcpy(dest, src)
59 static char *stpcpy (char *dest
, const char *src
);
63 /* Define function which are usually not available. */
65 #if defined HAVE_ARGZ_COUNT
67 # define __argz_count argz_count
69 /* Returns the number of strings in ARGZ. */
71 argz_count__ (const char *argz
, size_t len
)
76 size_t part_len
= strlen (argz
);
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
91 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
92 except the last into the character SEP. */
94 argz_stringify__ (char *argz
, size_t len
, int sep
)
98 size_t part_len
= strlen (argz
);
105 # undef __argz_stringify
106 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
107 #endif /* !_LIBC && !HAVE_ARGZ_STRINGIFY */
110 #elif defined HAVE_ARGZ_NEXT
112 # define __argz_next argz_next
115 argz_next__ (char *argz
, size_t argz_len
, const char *entry
)
119 if (entry
< argz
+ argz_len
)
120 entry
= strchr (entry
, '\0') + 1;
122 return entry
>= argz
+ argz_len
? NULL
: (char *) entry
;
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. */
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;
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
)
159 struct loaded_l10nfile
*last
= NULL
;
160 struct loaded_l10nfile
*retval
;
165 /* Allocate room for the full file name. */
166 abs_filename
= (char *) malloc (dirlist_len
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
)
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);
189 cp
= stpcpy (cp
, language
);
191 if ((mask
& XPG_TERRITORY
) != 0)
194 cp
= stpcpy (cp
, territory
);
196 if ((mask
& XPG_CODESET
) != 0)
199 cp
= stpcpy (cp
, codeset
);
201 if ((mask
& XPG_NORM_CODESET
) != 0)
204 cp
= stpcpy (cp
, normalized_codeset
);
206 if ((mask
& XPG_MODIFIER
) != 0)
209 cp
= stpcpy (cp
, modifier
);
213 stpcpy (cp
, filename
);
215 /* Look in list of already loaded domains whether it is already
218 for (retval
= *l10nfile_list
; retval
!= NULL
; retval
= retval
->next
)
219 if (retval
->filename
!= NULL
)
221 int compare
= strcmp (retval
->filename
, abs_filename
);
227 /* It's not in the list. */
235 if (retval
!= NULL
|| do_allocate
== 0)
241 retval
= (struct loaded_l10nfile
*)
242 malloc (sizeof (*retval
) + (__argz_count (dirlist
, dirlist_len
)
244 * sizeof (struct loaded_l10nfile
*)));
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,
255 retval
->decided
= (__argz_count (dirlist
, dirlist_len
) != 1
256 || ((mask
& XPG_CODESET
) != 0
257 && (mask
& XPG_NORM_CODESET
) != 0));
262 retval
->next
= *l10nfile_list
;
263 *l10nfile_list
= retval
;
267 retval
->next
= last
->next
;
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. */
282 while ((dir
= __argz_next ((char *) dirlist
, dirlist_len
, dir
))
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
;
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. */
299 _nl_normalize_codeset (const char *codeset
, size_t name_len
)
307 locale_t locale
= newlocale (0, "C", NULL
);
309 # define locale _nl_C_locobj_ptr
312 for (cnt
= 0; cnt
< name_len
; ++cnt
)
313 if (__isalnum_l ((unsigned char) codeset
[cnt
], locale
))
317 if (! __isdigit_l ((unsigned char) codeset
[cnt
], locale
))
321 retval
= (char *) malloc ((only_digit
? 3 : 0) + len
+ 1);
326 wp
= stpcpy (retval
, "iso");
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
];
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
349 #if !_LIBC && !HAVE_STPCPY
351 stpcpy (char *dest
, const char *src
)
353 while ((*dest
++ = *src
++) != '\0')