Update translations.
[glibc.git] / locale / loadlocale.c
blobb8cd1aa4414ae46c103ab2c90bf8c61460f930e4
1 /* Functions to read locale data files.
2 Copyright (C) 1996-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
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.
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
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #include <assert.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <locale.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #ifdef _POSIX_MAPPED_FILES
27 # include <sys/mman.h>
28 #endif
29 #include <sys/stat.h>
31 #include <not-cancel.h>
32 #include "localeinfo.h"
35 static const size_t _nl_category_num_items[] =
37 #define DEFINE_CATEGORY(category, category_name, items, a) \
38 [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
39 #include "categories.def"
40 #undef DEFINE_CATEGORY
44 #define NO_PAREN(arg, rest...) arg, ##rest
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. */
49 #define DEFINE_CATEGORY(category, category_name, items, a) \
50 static const enum value_type _nl_value_type_##category \
51 [_NL_ITEM_INDEX (_NL_NUM_##category)] = { NO_PAREN items };
52 #define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
53 [_NL_ITEM_INDEX (element)] = type,
54 #include "categories.def"
55 #undef DEFINE_CATEGORY
57 static const enum value_type *const _nl_value_types[] =
59 #define DEFINE_CATEGORY(category, category_name, items, a) \
60 [category] = _nl_value_type_##category,
61 #include "categories.def"
62 #undef DEFINE_CATEGORY
66 struct __locale_data *
67 _nl_intern_locale_data (int category, const void *data, size_t datasize)
69 const struct
71 unsigned int magic;
72 unsigned int nstrings;
73 unsigned int strindex[0];
74 } *const filedata = data;
75 struct __locale_data *newdata;
76 size_t cnt;
78 if (__builtin_expect (datasize < sizeof *filedata, 0)
79 || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
81 /* Bad data file. */
82 __set_errno (EINVAL);
83 return NULL;
86 if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
88 || (__builtin_expect (sizeof *filedata
89 + filedata->nstrings * sizeof (unsigned int)
90 >= datasize, 0)))
92 /* Insufficient data. */
93 __set_errno (EINVAL);
94 return NULL;
97 newdata = malloc (sizeof *newdata
98 + filedata->nstrings * sizeof (union locale_data_value));
99 if (newdata == NULL)
100 return NULL;
102 newdata->filedata = (void *) filedata;
103 newdata->filesize = datasize;
104 newdata->private.data = NULL;
105 newdata->private.cleanup = NULL;
106 newdata->usage_count = 0;
107 newdata->use_translit = 0;
108 newdata->nstrings = filedata->nstrings;
109 for (cnt = 0; cnt < newdata->nstrings; ++cnt)
111 size_t idx = filedata->strindex[cnt];
112 if (__glibc_unlikely (idx > (size_t) newdata->filesize))
114 puntdata:
115 free (newdata);
116 __set_errno (EINVAL);
117 return NULL;
120 /* Determine the type. There is one special case: the LC_CTYPE
121 category can have more elements than there are in the
122 _nl_value_type_LC_XYZ array. There are all pointers. */
123 switch (category)
125 #define CATTEST(cat) \
126 case LC_##cat: \
127 if (cnt >= (sizeof (_nl_value_type_LC_##cat) \
128 / sizeof (_nl_value_type_LC_##cat[0]))) \
129 goto puntdata; \
130 break
131 CATTEST (NUMERIC);
132 CATTEST (TIME);
133 CATTEST (COLLATE);
134 CATTEST (MONETARY);
135 CATTEST (MESSAGES);
136 CATTEST (PAPER);
137 CATTEST (NAME);
138 CATTEST (ADDRESS);
139 CATTEST (TELEPHONE);
140 CATTEST (MEASUREMENT);
141 CATTEST (IDENTIFICATION);
142 default:
143 assert (category == LC_CTYPE);
144 break;
147 if ((category == LC_CTYPE
148 && cnt >= (sizeof (_nl_value_type_LC_CTYPE)
149 / sizeof (_nl_value_type_LC_CTYPE[0])))
150 || __builtin_expect (_nl_value_types[category][cnt] != word, 1))
151 newdata->values[cnt].string = newdata->filedata + idx;
152 else
154 if (!LOCFILE_ALIGNED_P (idx))
155 goto puntdata;
156 newdata->values[cnt].word =
157 *((const uint32_t *) (newdata->filedata + idx));
161 return newdata;
164 void
165 _nl_load_locale (struct loaded_l10nfile *file, int category)
167 int fd;
168 void *filedata;
169 struct __stat64_t64 st;
170 struct __locale_data *newdata;
171 int save_err;
172 int alloc = ld_mapped;
174 file->decided = 1;
175 file->data = NULL;
177 fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC);
178 if (__builtin_expect (fd, 0) < 0)
179 /* Cannot open the file. */
180 return;
182 if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
184 puntfd:
185 __close_nocancel_nostatus (fd);
186 return;
188 if (__glibc_unlikely (S_ISDIR (st.st_mode)))
190 /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
191 instead. */
192 char *newp;
193 size_t filenamelen;
195 __close_nocancel_nostatus (fd);
197 filenamelen = strlen (file->filename);
198 newp = (char *) alloca (filenamelen
199 + 5 + _nl_category_name_sizes[category] + 1);
200 __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
201 "/SYS_", 5), _nl_category_names_get (category),
202 _nl_category_name_sizes[category] + 1);
204 fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
205 if (__builtin_expect (fd, 0) < 0)
206 return;
208 if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
209 goto puntfd;
212 /* Map in the file's data. */
213 save_err = errno;
214 #ifdef _POSIX_MAPPED_FILES
215 # ifndef MAP_COPY
216 /* Linux seems to lack read-only copy-on-write. */
217 # define MAP_COPY MAP_PRIVATE
218 # endif
219 # ifndef MAP_FILE
220 /* Some systems do not have this flag; it is superfluous. */
221 # define MAP_FILE 0
222 # endif
223 filedata = __mmap ((caddr_t) 0, st.st_size,
224 PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
225 if (__glibc_unlikely (filedata == MAP_FAILED))
227 filedata = NULL;
228 if (__builtin_expect (errno, ENOSYS) == ENOSYS)
230 #endif /* _POSIX_MAPPED_FILES */
231 /* No mmap; allocate a buffer and read from the file. */
232 alloc = ld_malloced;
233 filedata = malloc (st.st_size);
234 if (filedata != NULL)
236 off_t to_read = st.st_size;
237 ssize_t nread;
238 char *p = (char *) filedata;
239 while (to_read > 0)
241 nread = __read_nocancel (fd, p, to_read);
242 if (__builtin_expect (nread, 1) <= 0)
244 free (filedata);
245 if (nread == 0)
246 __set_errno (EINVAL); /* Bizarreness going on. */
247 goto puntfd;
249 p += nread;
250 to_read -= nread;
252 __set_errno (save_err);
254 #ifdef _POSIX_MAPPED_FILES
257 #endif /* _POSIX_MAPPED_FILES */
259 /* We have mapped the data, so we no longer need the descriptor. */
260 __close_nocancel_nostatus (fd);
262 if (__glibc_unlikely (filedata == NULL))
263 /* We failed to map or read the data. */
264 return;
266 newdata = _nl_intern_locale_data (category, filedata, st.st_size);
267 if (__glibc_unlikely (newdata == NULL))
268 /* Bad data. */
270 #ifdef _POSIX_MAPPED_FILES
271 if (alloc == ld_mapped)
272 __munmap ((caddr_t) filedata, st.st_size);
273 #endif
274 return;
277 /* _nl_intern_locale_data leaves us these fields to initialize. */
278 newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
279 newdata->alloc = alloc;
281 file->data = newdata;
284 void
285 _nl_unload_locale (struct __locale_data *locale)
287 if (locale->private.cleanup)
288 (*locale->private.cleanup) (locale);
290 switch (__builtin_expect (locale->alloc, ld_mapped))
292 case ld_malloced:
293 free ((void *) locale->filedata);
294 break;
295 case ld_mapped:
296 #ifdef _POSIX_MAPPED_FILES
297 __munmap ((caddr_t) locale->filedata, locale->filesize);
298 break;
299 #endif
300 case ld_archive: /* Nothing to do. */
301 break;
304 if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
305 free ((char *) locale->name);
307 free (locale);