]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/loadlocale.c
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / locale / loadlocale.c
CommitLineData
ce4d8b66 1/* Functions to read locale data files.
6d7e8eda 2 Copyright (C) 1996-2023 Free Software Foundation, Inc.
c84142e8 3 This file is part of the GNU C Library.
933e73fa 4
c84142e8 5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
933e73fa 9
c84142e8
UD
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 13 Lesser General Public License for more details.
933e73fa 14
41bdb6e2 15 You should have received a copy of the GNU Lesser General Public
59ba27a6 16 License along with the GNU C Library; if not, see
5a82c748 17 <https://www.gnu.org/licenses/>. */
933e73fa 18
e43e0dd6 19#include <assert.h>
933e73fa
RM
20#include <errno.h>
21#include <fcntl.h>
3e154a6f 22#include <locale.h>
933e73fa
RM
23#include <stdlib.h>
24#include <string.h>
7a12c6bb 25#include <unistd.h>
b6aa34eb
UD
26#ifdef _POSIX_MAPPED_FILES
27# include <sys/mman.h>
28#endif
7a12c6bb
RM
29#include <sys/stat.h>
30
72ef277e 31#include <not-cancel.h>
933e73fa
RM
32#include "localeinfo.h"
33
7a12c6bb
RM
34
35static const size_t _nl_category_num_items[] =
19bc17a9 36{
4b10dd6c 37#define DEFINE_CATEGORY(category, category_name, items, a) \
19bc17a9 38 [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
933e73fa
RM
39#include "categories.def"
40#undef DEFINE_CATEGORY
19bc17a9
RM
41};
42
43
44#define NO_PAREN(arg, rest...) arg, ##rest
45
f301e533
RL
46/* The size of the array must be specified explicitly because some of
47 the 'items' may be subarrays, which will cause the compiler to deduce
48 an incorrect size from the initializer. */
4b10dd6c 49#define DEFINE_CATEGORY(category, category_name, items, a) \
f301e533
RL
50static const enum value_type _nl_value_type_##category \
51 [_NL_ITEM_INDEX (_NL_NUM_##category)] = { NO_PAREN items };
19bc17a9
RM
52#define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
53 [_NL_ITEM_INDEX (element)] = type,
54#include "categories.def"
55#undef DEFINE_CATEGORY
56
aa87e915 57static const enum value_type *const _nl_value_types[] =
19bc17a9 58{
4b10dd6c 59#define DEFINE_CATEGORY(category, category_name, items, a) \
19bc17a9
RM
60 [category] = _nl_value_type_##category,
61#include "categories.def"
62#undef DEFINE_CATEGORY
63};
64
93ec1cf0
FW
65/* Fill in LOCDATA->private for the LC_CTYPE category. */
66static void
67_nl_intern_locale_data_fill_cache_ctype (struct __locale_data *locdata)
68{
69 struct lc_ctype_data *data = locdata->private;
70
71 /* Default to no translation. Assumes zero initialization of *data. */
72 memset (data->outdigit_bytes, 1, sizeof (data->outdigit_bytes));
73
74 for (int i = 0; i <= 9; ++i)
75 {
76 const char *digit
77 = locdata->values[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_MB + i)].string;
78 unsigned char len;
79 if (digit[0] != '0' + i || digit[1] != '\0')
80 {
81 data->outdigit_translation_needed = true;
82 len = strlen (locdata->values[_NL_ITEM_INDEX
83 (_NL_CTYPE_OUTDIGIT0_MB + i)].string);
84 }
85 else
86 len = 1;
87 data->outdigit_bytes[i] = len;
88 if (i == 0)
89 data->outdigit_bytes_all_equal = len;
90 else if (data->outdigit_bytes_all_equal != len)
91 data->outdigit_bytes_all_equal = 0;
92 }
93}
94
95/* Updates data in LOCDATA->private for CATEGORY. */
96static void
97_nl_intern_locale_data_fill_cache (int category, struct __locale_data *locdata)
98{
99 switch (category)
100 {
101 case LC_CTYPE:
102 _nl_intern_locale_data_fill_cache_ctype (locdata);
103 break;
104 }
105}
106
107/* Returns the number of bytes allocated of struct __locale_data for
108 CATEGORY. */
109static size_t
110_nl_intern_locale_data_extra_size (int category)
111{
112 switch (category)
113 {
114 case LC_CTYPE:
115 return sizeof (struct lc_ctype_data);
116 default:
117 return 0;
118 }
119}
933e73fa 120
f095bb72 121struct __locale_data *
cb09a2cd 122_nl_intern_locale_data (int category, const void *data, size_t datasize)
933e73fa 123{
cb09a2cd 124 const struct
933e73fa
RM
125 {
126 unsigned int magic;
127 unsigned int nstrings;
128 unsigned int strindex[0];
cb09a2cd 129 } *const filedata = data;
f095bb72 130 struct __locale_data *newdata;
cb09a2cd
RM
131 size_t cnt;
132
133 if (__builtin_expect (datasize < sizeof *filedata, 0)
134 || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
135 {
136 /* Bad data file. */
137 __set_errno (EINVAL);
138 return NULL;
139 }
140
141 if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
142 0)
143 || (__builtin_expect (sizeof *filedata
144 + filedata->nstrings * sizeof (unsigned int)
145 >= datasize, 0)))
146 {
147 /* Insufficient data. */
148 __set_errno (EINVAL);
149 return NULL;
150 }
151
93ec1cf0
FW
152 size_t base_size = (sizeof *newdata
153 + filedata->nstrings * sizeof (union locale_data_value));
154 size_t extra_size = _nl_intern_locale_data_extra_size (category);
155
156 newdata = malloc (base_size + extra_size);
cb09a2cd
RM
157 if (newdata == NULL)
158 return NULL;
159
160 newdata->filedata = (void *) filedata;
161 newdata->filesize = datasize;
93ec1cf0
FW
162 if (extra_size == 0)
163 newdata->private = NULL;
164 else
165 {
166 newdata->private = (char *) newdata + base_size;
167 memset (newdata->private, 0, extra_size);
168 }
cb09a2cd
RM
169 newdata->usage_count = 0;
170 newdata->use_translit = 0;
171 newdata->nstrings = filedata->nstrings;
172 for (cnt = 0; cnt < newdata->nstrings; ++cnt)
173 {
174 size_t idx = filedata->strindex[cnt];
a1ffb40e 175 if (__glibc_unlikely (idx > (size_t) newdata->filesize))
cb09a2cd
RM
176 {
177 puntdata:
178 free (newdata);
179 __set_errno (EINVAL);
180 return NULL;
181 }
fe6ce170
UD
182
183 /* Determine the type. There is one special case: the LC_CTYPE
184 category can have more elements than there are in the
185 _nl_value_type_LC_XYZ array. There are all pointers. */
186 switch (category)
187 {
188#define CATTEST(cat) \
0062ace2
LC
189 case LC_##cat: \
190 if (cnt >= (sizeof (_nl_value_type_LC_##cat) \
191 / sizeof (_nl_value_type_LC_##cat[0]))) \
192 goto puntdata; \
fe6ce170
UD
193 break
194 CATTEST (NUMERIC);
195 CATTEST (TIME);
196 CATTEST (COLLATE);
197 CATTEST (MONETARY);
198 CATTEST (MESSAGES);
199 CATTEST (PAPER);
200 CATTEST (NAME);
201 CATTEST (ADDRESS);
202 CATTEST (TELEPHONE);
203 CATTEST (MEASUREMENT);
204 CATTEST (IDENTIFICATION);
205 default:
206 assert (category == LC_CTYPE);
207 break;
208 }
209
210 if ((category == LC_CTYPE
211 && cnt >= (sizeof (_nl_value_type_LC_CTYPE)
212 / sizeof (_nl_value_type_LC_CTYPE[0])))
213 || __builtin_expect (_nl_value_types[category][cnt] != word, 1))
214 newdata->values[cnt].string = newdata->filedata + idx;
215 else
cb09a2cd 216 {
7602d070 217 if (!LOCFILE_ALIGNED_P (idx))
cb09a2cd
RM
218 goto puntdata;
219 newdata->values[cnt].word =
d9fee042 220 *((const uint32_t *) (newdata->filedata + idx));
cb09a2cd 221 }
cb09a2cd
RM
222 }
223
93ec1cf0
FW
224 if (extra_size > 0)
225 _nl_intern_locale_data_fill_cache (category, newdata);
226
cb09a2cd
RM
227 return newdata;
228}
229
230void
cb09a2cd
RM
231_nl_load_locale (struct loaded_l10nfile *file, int category)
232{
233 int fd;
234 void *filedata;
52a5fe70 235 struct __stat64_t64 st;
f095bb72 236 struct __locale_data *newdata;
7a12c6bb 237 int save_err;
cb09a2cd 238 int alloc = ld_mapped;
933e73fa 239
7a12c6bb
RM
240 file->decided = 1;
241 file->data = NULL;
242
c2284574 243 fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC);
3a31f6f4 244 if (__builtin_expect (fd, 0) < 0)
7a12c6bb
RM
245 /* Cannot open the file. */
246 return;
247
52a5fe70 248 if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
cb09a2cd
RM
249 {
250 puntfd:
c181840c 251 __close_nocancel_nostatus (fd);
cb09a2cd
RM
252 return;
253 }
a1ffb40e 254 if (__glibc_unlikely (S_ISDIR (st.st_mode)))
933e73fa 255 {
7a12c6bb 256 /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
f095bb72 257 instead. */
7a12c6bb 258 char *newp;
b6aa34eb 259 size_t filenamelen;
036cc82f 260
c181840c 261 __close_nocancel_nostatus (fd);
7a12c6bb 262
b6aa34eb
UD
263 filenamelen = strlen (file->filename);
264 newp = (char *) alloca (filenamelen
7a12c6bb 265 + 5 + _nl_category_name_sizes[category] + 1);
b6aa34eb 266 __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
de18a706 267 "/SYS_", 5), _nl_category_names_get (category),
b6aa34eb 268 _nl_category_name_sizes[category] + 1);
7a12c6bb 269
c2284574 270 fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
3a31f6f4 271 if (__builtin_expect (fd, 0) < 0)
7a12c6bb
RM
272 return;
273
52a5fe70 274 if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
7a12c6bb 275 goto puntfd;
933e73fa
RM
276 }
277
7a12c6bb
RM
278 /* Map in the file's data. */
279 save_err = errno;
b6aa34eb
UD
280#ifdef _POSIX_MAPPED_FILES
281# ifndef MAP_COPY
7a12c6bb 282 /* Linux seems to lack read-only copy-on-write. */
b6aa34eb
UD
283# define MAP_COPY MAP_PRIVATE
284# endif
285# ifndef MAP_FILE
7a12c6bb 286 /* Some systems do not have this flag; it is superfluous. */
b6aa34eb
UD
287# define MAP_FILE 0
288# endif
cb09a2cd
RM
289 filedata = __mmap ((caddr_t) 0, st.st_size,
290 PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
a1ffb40e 291 if (__glibc_unlikely (filedata == MAP_FAILED))
7a12c6bb 292 {
8b2f25c2 293 filedata = NULL;
3a31f6f4 294 if (__builtin_expect (errno, ENOSYS) == ENOSYS)
7a12c6bb 295 {
b6aa34eb 296#endif /* _POSIX_MAPPED_FILES */
7a12c6bb 297 /* No mmap; allocate a buffer and read from the file. */
cb09a2cd 298 alloc = ld_malloced;
7a12c6bb
RM
299 filedata = malloc (st.st_size);
300 if (filedata != NULL)
301 {
302 off_t to_read = st.st_size;
303 ssize_t nread;
304 char *p = (char *) filedata;
305 while (to_read > 0)
306 {
a748eb31 307 nread = __read_nocancel (fd, p, to_read);
3a31f6f4 308 if (__builtin_expect (nread, 1) <= 0)
7a12c6bb
RM
309 {
310 free (filedata);
311 if (nread == 0)
c4029823 312 __set_errno (EINVAL); /* Bizarreness going on. */
7a12c6bb
RM
313 goto puntfd;
314 }
315 p += nread;
316 to_read -= nread;
317 }
cb09a2cd 318 __set_errno (save_err);
7a12c6bb 319 }
b6aa34eb 320#ifdef _POSIX_MAPPED_FILES
7a12c6bb 321 }
7a12c6bb 322 }
b6aa34eb 323#endif /* _POSIX_MAPPED_FILES */
933e73fa 324
cb09a2cd 325 /* We have mapped the data, so we no longer need the descriptor. */
c181840c 326 __close_nocancel_nostatus (fd);
cb09a2cd 327
a1ffb40e 328 if (__glibc_unlikely (filedata == NULL))
cb09a2cd
RM
329 /* We failed to map or read the data. */
330 return;
331
332 newdata = _nl_intern_locale_data (category, filedata, st.st_size);
a1ffb40e 333 if (__glibc_unlikely (newdata == NULL))
cb09a2cd 334 /* Bad data. */
933e73fa 335 {
b6aa34eb 336#ifdef _POSIX_MAPPED_FILES
cb09a2cd
RM
337 if (alloc == ld_mapped)
338 __munmap ((caddr_t) filedata, st.st_size);
b6aa34eb 339#endif
4a33c2f5 340 return;
933e73fa
RM
341 }
342
cb09a2cd 343 /* _nl_intern_locale_data leaves us these fields to initialize. */
7a12c6bb 344 newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
cb09a2cd 345 newdata->alloc = alloc;
933e73fa 346
7a12c6bb 347 file->data = newdata;
933e73fa 348}
a5a0310d
UD
349
350void
bbebe83a 351_nl_unload_locale (int category, struct __locale_data *locale)
a5a0310d 352{
bbebe83a
FW
353 /* Deallocate locale->private. */
354 switch (category)
355 {
356 case LC_CTYPE:
357 _nl_cleanup_ctype (locale);
358 break;
359 case LC_TIME:
360 _nl_cleanup_time (locale);
361 break;
362 }
df9f41c9 363
cb09a2cd
RM
364 switch (__builtin_expect (locale->alloc, ld_mapped))
365 {
366 case ld_malloced:
367 free ((void *) locale->filedata);
368 break;
369 case ld_mapped:
b6aa34eb 370#ifdef _POSIX_MAPPED_FILES
cb09a2cd
RM
371 __munmap ((caddr_t) locale->filedata, locale->filesize);
372 break;
b6aa34eb 373#endif
cb09a2cd
RM
374 case ld_archive: /* Nothing to do. */
375 break;
376 }
377
378 if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
379 free ((char *) locale->name);
a5a0310d
UD
380
381 free (locale);
382}