Update install.texi, and regenerate INSTALL.
[glibc.git] / locale / loadlocale.c
blobf4e6cc9fc2d5c06fae75397e254a6c2bab888416
1 /* Functions to read locale data files.
2 Copyright (C) 1996-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <locale.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #ifdef _POSIX_MAPPED_FILES
28 # include <sys/mman.h>
29 #endif
30 #include <sys/stat.h>
32 #include <not-cancel.h>
33 #include "localeinfo.h"
36 static const size_t _nl_category_num_items[] =
38 #define DEFINE_CATEGORY(category, category_name, items, a) \
39 [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
40 #include "categories.def"
41 #undef DEFINE_CATEGORY
45 #define NO_PAREN(arg, rest...) arg, ##rest
47 /* The size of the array must be specified explicitly because some of
48 the 'items' may be subarrays, which will cause the compiler to deduce
49 an incorrect size from the initializer. */
50 #define DEFINE_CATEGORY(category, category_name, items, a) \
51 static const enum value_type _nl_value_type_##category \
52 [_NL_ITEM_INDEX (_NL_NUM_##category)] = { NO_PAREN items };
53 #define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
54 [_NL_ITEM_INDEX (element)] = type,
55 #include "categories.def"
56 #undef DEFINE_CATEGORY
58 static const enum value_type *const _nl_value_types[] =
60 #define DEFINE_CATEGORY(category, category_name, items, a) \
61 [category] = _nl_value_type_##category,
62 #include "categories.def"
63 #undef DEFINE_CATEGORY
67 struct __locale_data *
68 _nl_intern_locale_data (int category, const void *data, size_t datasize)
70 const struct
72 unsigned int magic;
73 unsigned int nstrings;
74 unsigned int strindex[0];
75 } *const filedata = data;
76 struct __locale_data *newdata;
77 size_t cnt;
79 if (__builtin_expect (datasize < sizeof *filedata, 0)
80 || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
82 /* Bad data file. */
83 __set_errno (EINVAL);
84 return NULL;
87 if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
89 || (__builtin_expect (sizeof *filedata
90 + filedata->nstrings * sizeof (unsigned int)
91 >= datasize, 0)))
93 /* Insufficient data. */
94 __set_errno (EINVAL);
95 return NULL;
98 newdata = malloc (sizeof *newdata
99 + filedata->nstrings * sizeof (union locale_data_value));
100 if (newdata == NULL)
101 return NULL;
103 newdata->filedata = (void *) filedata;
104 newdata->filesize = datasize;
105 newdata->private.data = NULL;
106 newdata->private.cleanup = NULL;
107 newdata->usage_count = 0;
108 newdata->use_translit = 0;
109 newdata->nstrings = filedata->nstrings;
110 for (cnt = 0; cnt < newdata->nstrings; ++cnt)
112 size_t idx = filedata->strindex[cnt];
113 if (__glibc_unlikely (idx > (size_t) newdata->filesize))
115 puntdata:
116 free (newdata);
117 __set_errno (EINVAL);
118 return NULL;
121 /* Determine the type. There is one special case: the LC_CTYPE
122 category can have more elements than there are in the
123 _nl_value_type_LC_XYZ array. There are all pointers. */
124 switch (category)
126 #define CATTEST(cat) \
127 case LC_##cat: \
128 if (cnt >= (sizeof (_nl_value_type_LC_##cat) \
129 / sizeof (_nl_value_type_LC_##cat[0]))) \
130 goto puntdata; \
131 break
132 CATTEST (NUMERIC);
133 CATTEST (TIME);
134 CATTEST (COLLATE);
135 CATTEST (MONETARY);
136 CATTEST (MESSAGES);
137 CATTEST (PAPER);
138 CATTEST (NAME);
139 CATTEST (ADDRESS);
140 CATTEST (TELEPHONE);
141 CATTEST (MEASUREMENT);
142 CATTEST (IDENTIFICATION);
143 default:
144 assert (category == LC_CTYPE);
145 break;
148 if ((category == LC_CTYPE
149 && cnt >= (sizeof (_nl_value_type_LC_CTYPE)
150 / sizeof (_nl_value_type_LC_CTYPE[0])))
151 || __builtin_expect (_nl_value_types[category][cnt] != word, 1))
152 newdata->values[cnt].string = newdata->filedata + idx;
153 else
155 if (!LOCFILE_ALIGNED_P (idx))
156 goto puntdata;
157 newdata->values[cnt].word =
158 *((const uint32_t *) (newdata->filedata + idx));
162 return newdata;
165 void
166 _nl_load_locale (struct loaded_l10nfile *file, int category)
168 int fd;
169 void *filedata;
170 struct __stat64_t64 st;
171 struct __locale_data *newdata;
172 int save_err;
173 int alloc = ld_mapped;
175 file->decided = 1;
176 file->data = NULL;
178 fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC);
179 if (__builtin_expect (fd, 0) < 0)
180 /* Cannot open the file. */
181 return;
183 if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
185 puntfd:
186 __close_nocancel_nostatus (fd);
187 return;
189 if (__glibc_unlikely (S_ISDIR (st.st_mode)))
191 /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
192 instead. */
193 char *newp;
194 size_t filenamelen;
196 __close_nocancel_nostatus (fd);
198 filenamelen = strlen (file->filename);
199 newp = (char *) alloca (filenamelen
200 + 5 + _nl_category_name_sizes[category] + 1);
201 __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
202 "/SYS_", 5), _nl_category_names_get (category),
203 _nl_category_name_sizes[category] + 1);
205 fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
206 if (__builtin_expect (fd, 0) < 0)
207 return;
209 if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
210 goto puntfd;
213 /* Map in the file's data. */
214 save_err = errno;
215 #ifdef _POSIX_MAPPED_FILES
216 # ifndef MAP_COPY
217 /* Linux seems to lack read-only copy-on-write. */
218 # define MAP_COPY MAP_PRIVATE
219 # endif
220 # ifndef MAP_FILE
221 /* Some systems do not have this flag; it is superfluous. */
222 # define MAP_FILE 0
223 # endif
224 filedata = __mmap ((caddr_t) 0, st.st_size,
225 PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
226 if (__glibc_unlikely (filedata == MAP_FAILED))
228 filedata = NULL;
229 if (__builtin_expect (errno, ENOSYS) == ENOSYS)
231 #endif /* _POSIX_MAPPED_FILES */
232 /* No mmap; allocate a buffer and read from the file. */
233 alloc = ld_malloced;
234 filedata = malloc (st.st_size);
235 if (filedata != NULL)
237 off_t to_read = st.st_size;
238 ssize_t nread;
239 char *p = (char *) filedata;
240 while (to_read > 0)
242 nread = __read_nocancel (fd, p, to_read);
243 if (__builtin_expect (nread, 1) <= 0)
245 free (filedata);
246 if (nread == 0)
247 __set_errno (EINVAL); /* Bizarreness going on. */
248 goto puntfd;
250 p += nread;
251 to_read -= nread;
253 __set_errno (save_err);
255 #ifdef _POSIX_MAPPED_FILES
258 #endif /* _POSIX_MAPPED_FILES */
260 /* We have mapped the data, so we no longer need the descriptor. */
261 __close_nocancel_nostatus (fd);
263 if (__glibc_unlikely (filedata == NULL))
264 /* We failed to map or read the data. */
265 return;
267 newdata = _nl_intern_locale_data (category, filedata, st.st_size);
268 if (__glibc_unlikely (newdata == NULL))
269 /* Bad data. */
271 #ifdef _POSIX_MAPPED_FILES
272 if (alloc == ld_mapped)
273 __munmap ((caddr_t) filedata, st.st_size);
274 #endif
275 return;
278 /* _nl_intern_locale_data leaves us these fields to initialize. */
279 newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
280 newdata->alloc = alloc;
282 file->data = newdata;
285 void
286 _nl_unload_locale (struct __locale_data *locale)
288 if (locale->private.cleanup)
289 (*locale->private.cleanup) (locale);
291 switch (__builtin_expect (locale->alloc, ld_mapped))
293 case ld_malloced:
294 free ((void *) locale->filedata);
295 break;
296 case ld_mapped:
297 #ifdef _POSIX_MAPPED_FILES
298 __munmap ((caddr_t) locale->filedata, locale->filesize);
299 break;
300 #endif
301 case ld_archive: /* Nothing to do. */
302 break;
305 if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
306 free ((char *) locale->name);
308 free (locale);