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/>. */
26 #ifdef _POSIX_MAPPED_FILES
27 # include <sys/mman.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
65 /* Fill in LOCDATA->private for the LC_CTYPE category. */
67 _nl_intern_locale_data_fill_cache_ctype (struct __locale_data
*locdata
)
69 struct lc_ctype_data
*data
= locdata
->private;
71 /* Default to no translation. Assumes zero initialization of *data. */
72 memset (data
->outdigit_bytes
, 1, sizeof (data
->outdigit_bytes
));
74 for (int i
= 0; i
<= 9; ++i
)
77 = locdata
->values
[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_MB
+ i
)].string
;
79 if (digit
[0] != '0' + i
|| digit
[1] != '\0')
81 data
->outdigit_translation_needed
= true;
82 len
= strlen (locdata
->values
[_NL_ITEM_INDEX
83 (_NL_CTYPE_OUTDIGIT0_MB
+ i
)].string
);
87 data
->outdigit_bytes
[i
] = len
;
89 data
->outdigit_bytes_all_equal
= len
;
90 else if (data
->outdigit_bytes_all_equal
!= len
)
91 data
->outdigit_bytes_all_equal
= 0;
95 /* Updates data in LOCDATA->private for CATEGORY. */
97 _nl_intern_locale_data_fill_cache (int category
, struct __locale_data
*locdata
)
102 _nl_intern_locale_data_fill_cache_ctype (locdata
);
107 /* Returns the number of bytes allocated of struct __locale_data for
110 _nl_intern_locale_data_extra_size (int category
)
115 return sizeof (struct lc_ctype_data
);
121 struct __locale_data
*
122 _nl_intern_locale_data (int category
, const void *data
, size_t datasize
)
127 unsigned int nstrings
;
128 unsigned int strindex
[0];
129 } *const filedata
= data
;
130 struct __locale_data
*newdata
;
133 if (__builtin_expect (datasize
< sizeof *filedata
, 0)
134 || __builtin_expect (filedata
->magic
!= LIMAGIC (category
), 0))
137 __set_errno (EINVAL
);
141 if (__builtin_expect (filedata
->nstrings
< _nl_category_num_items
[category
],
143 || (__builtin_expect (sizeof *filedata
144 + filedata
->nstrings
* sizeof (unsigned int)
147 /* Insufficient data. */
148 __set_errno (EINVAL
);
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
);
156 newdata
= malloc (base_size
+ extra_size
);
160 newdata
->filedata
= (void *) filedata
;
161 newdata
->filesize
= datasize
;
163 newdata
->private = NULL
;
166 newdata
->private = (char *) newdata
+ base_size
;
167 memset (newdata
->private, 0, extra_size
);
169 newdata
->usage_count
= 0;
170 newdata
->use_translit
= 0;
171 newdata
->nstrings
= filedata
->nstrings
;
172 for (cnt
= 0; cnt
< newdata
->nstrings
; ++cnt
)
174 size_t idx
= filedata
->strindex
[cnt
];
175 if (__glibc_unlikely (idx
> (size_t) newdata
->filesize
))
179 __set_errno (EINVAL
);
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. */
188 #define CATTEST(cat) \
190 if (cnt >= (sizeof (_nl_value_type_LC_##cat) \
191 / sizeof (_nl_value_type_LC_##cat[0]))) \
203 CATTEST (MEASUREMENT
);
204 CATTEST (IDENTIFICATION
);
206 assert (category
== LC_CTYPE
);
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
;
217 if (!LOCFILE_ALIGNED_P (idx
))
219 newdata
->values
[cnt
].word
=
220 *((const uint32_t *) (newdata
->filedata
+ idx
));
225 _nl_intern_locale_data_fill_cache (category
, newdata
);
231 _nl_load_locale (struct loaded_l10nfile
*file
, int category
)
235 struct __stat64_t64 st
;
236 struct __locale_data
*newdata
;
238 int alloc
= ld_mapped
;
243 fd
= __open_nocancel (file
->filename
, O_RDONLY
| O_CLOEXEC
);
244 if (__builtin_expect (fd
, 0) < 0)
245 /* Cannot open the file. */
248 if (__glibc_unlikely (__fstat64_time64 (fd
, &st
) < 0))
251 __close_nocancel_nostatus (fd
);
254 if (__glibc_unlikely (S_ISDIR (st
.st_mode
)))
256 /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
261 __close_nocancel_nostatus (fd
);
263 filenamelen
= strlen (file
->filename
);
264 newp
= (char *) alloca (filenamelen
265 + 5 + _nl_category_name_sizes
[category
] + 1);
266 __mempcpy (__mempcpy (__mempcpy (newp
, file
->filename
, filenamelen
),
267 "/SYS_", 5), _nl_category_names_get (category
),
268 _nl_category_name_sizes
[category
] + 1);
270 fd
= __open_nocancel (newp
, O_RDONLY
| O_CLOEXEC
);
271 if (__builtin_expect (fd
, 0) < 0)
274 if (__glibc_unlikely (__fstat64_time64 (fd
, &st
) < 0))
278 /* Map in the file's data. */
280 #ifdef _POSIX_MAPPED_FILES
282 /* Linux seems to lack read-only copy-on-write. */
283 # define MAP_COPY MAP_PRIVATE
286 /* Some systems do not have this flag; it is superfluous. */
289 filedata
= __mmap ((caddr_t
) 0, st
.st_size
,
290 PROT_READ
, MAP_FILE
|MAP_COPY
, fd
, 0);
291 if (__glibc_unlikely (filedata
== MAP_FAILED
))
294 if (__builtin_expect (errno
, ENOSYS
) == ENOSYS
)
296 #endif /* _POSIX_MAPPED_FILES */
297 /* No mmap; allocate a buffer and read from the file. */
299 filedata
= malloc (st
.st_size
);
300 if (filedata
!= NULL
)
302 off_t to_read
= st
.st_size
;
304 char *p
= (char *) filedata
;
307 nread
= __read_nocancel (fd
, p
, to_read
);
308 if (__builtin_expect (nread
, 1) <= 0)
312 __set_errno (EINVAL
); /* Bizarreness going on. */
318 __set_errno (save_err
);
320 #ifdef _POSIX_MAPPED_FILES
323 #endif /* _POSIX_MAPPED_FILES */
325 /* We have mapped the data, so we no longer need the descriptor. */
326 __close_nocancel_nostatus (fd
);
328 if (__glibc_unlikely (filedata
== NULL
))
329 /* We failed to map or read the data. */
332 newdata
= _nl_intern_locale_data (category
, filedata
, st
.st_size
);
333 if (__glibc_unlikely (newdata
== NULL
))
336 #ifdef _POSIX_MAPPED_FILES
337 if (alloc
== ld_mapped
)
338 __munmap ((caddr_t
) filedata
, st
.st_size
);
343 /* _nl_intern_locale_data leaves us these fields to initialize. */
344 newdata
->name
= NULL
; /* This will be filled if necessary in findlocale.c. */
345 newdata
->alloc
= alloc
;
347 file
->data
= newdata
;
351 _nl_unload_locale (int category
, struct __locale_data
*locale
)
353 /* Deallocate locale->private. */
357 _nl_cleanup_ctype (locale
);
360 _nl_cleanup_time (locale
);
364 switch (__builtin_expect (locale
->alloc
, ld_mapped
))
367 free ((void *) locale
->filedata
);
370 #ifdef _POSIX_MAPPED_FILES
371 __munmap ((caddr_t
) locale
->filedata
, locale
->filesize
);
374 case ld_archive
: /* Nothing to do. */
378 if (__builtin_expect (locale
->alloc
, ld_mapped
) != ld_archive
)
379 free ((char *) locale
->name
);