diff options
author | Roland McGrath <roland@gnu.org> | 2002-08-10 06:22:37 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 2002-08-10 06:22:37 +0000 |
commit | cb09a2cda2e83d6002a41a5d5e5f37a925f52d51 (patch) | |
tree | 8ed11334f3c54c99d6c4399839936d1d75af5301 /locale | |
parent | 946860b1cb6372260430fb0f446083d66f35f434 (diff) | |
download | glibc-cb09a2cda2e83d6002a41a5d5e5f37a925f52d51.tar glibc-cb09a2cda2e83d6002a41a5d5e5f37a925f52d51.tar.gz glibc-cb09a2cda2e83d6002a41a5d5e5f37a925f52d51.tar.bz2 glibc-cb09a2cda2e83d6002a41a5d5e5f37a925f52d51.zip |
* locale/loadarchive.c (_nl_load_locale_from_archive): Parse locale
name to find codeset name (if any) and normalize it. If the
normalized name differs, look up only that in the archive.
* locale/programs/locarchive.c (add_locale_to_archive): If the name
contains a codeset, normalize the codeset store only the normalized
name in the archive. If not, add an alias containing the locale's
normalized codeset name. Apply codeset name normalization when
matching entries in the alias file.
* locale/programs/locarchive.c (delete_locales_from_archive): Don't
decrement HEAD->namehash_used here.
(add_locale): Only need to insert name string when name_offset != 0.
* locale/programs/localedef.c (options): Add -A/--alias-file.
(alias_file): New variable.
(parse_opt): Grok -A, set that.
* locale/programs/localedef.h: Declare it.
* locale/programs/locarchive.c (insert_name): New function, broken out
of ...
(add_locale_to_archive): ... here. Call that.
(add_alias): New function.
(add_locale): New static function, add_locale_to_archive renamed.
(add_locale_to_archive): Call that and use add_alias to add an alias
for the name with codeset if the given name lacks it.
(enlarge_archive): Call add_locale instead of add_locale_to_archive.
* locale/Makefile (routines): Add loadarchive.
* locale/loadarchive.c: New file, started from code by Ulrich Drepper.
(_nl_load_locale_from_archive): New function.
* locale/localeinfo.h: Declare it.
* locale/findlocale.c (_nl_find_locale): If using default locale path,
try _nl_load_locale_from_archive first.
* locale/loadlocale.c (_nl_intern_locale_data): New function,
broken out of _nl_load_locale.
(_nl_load_locale): Call that.
* locale/localeinfo.h: Declare it.
(struct locale_data): Replace member `mmaped' with `alloc', an enum.
(struct locale_data): Remove unused member `options'.
* locale/findlocale.c (_nl_remove_locale): Update uses.
* locale/loadlocale.c (_nl_load_locale, _nl_unload_locale): Likewise.
* locale/C-collate.c: Update initializer.
* locale/C-identification.c: Likewise.
* locale/C-measurement.c: Likewise.
* locale/C-telephone.c: Likewise.
* locale/C-address.c: Likewise.
* locale/C-name.c: Likewise.
* locale/C-paper.c: Likewise.
* locale/C-time.c: Likewise.
* locale/C-numeric.c: Likewise.
* locale/C-monetary.c: Likewise.
* locale/C-messages.c : Likewise.
* locale/C-ctype.c: Likewise.
* locale/hashval.h [! LONGBITS]: Include <limits.h> here and
use CHAR_BIT instead of BITSPERBYTE.
* locale/localeinfo.h (_nl_find_locale, _nl_load_locale,
_nl_unload_locale): Add `internal_function attribute_hidden' to decls.
* locale/findlocale.c (_nl_find_locale): Add internal_function to defn.
(_nl_remove_locale): Likewise.
* locale/loadlocale.c (_nl_load_locale, _nl_unload_locale): Likewise.
* locale/findlocale.c (_nl_default_locale_path): New variable.
(_nl_find_locale): If LOCALE_PATH is null, default to that.
* locale/localeinfo.h: Declare it.
* locale/setlocale.c (setlocale): Use _nl_default_locale_path
in place of LOCALEDIR. If no LOCPATH, pass null to _nl_find_locale.
* locale/newlocale.c (__newlocale): Likewise.
* misc/err.c (vwarnx, vwarn): Fix typos in libc_hidden_def uses.
* inet/rexec.c (rexec_af): Add libc_hidden_def.
* sysdeps/generic/morecore.c: Likewise.
* signal/allocrtsig.c (__libc_current_sigrtmin): Likewise.
(__libc_current_sigrtmax): Likewise.
2002-08-08 Roland McGrath <roland@redhat.com>
* locale/loadlocale.c (_nl_load_locale): Don't use MAP_INHERIT.
* catgets/open_catalog.c (__open_catalog): Likewise.
* locale/programs/locarchive.c (INITIAL_NUM_NAMES): Renamed
from typo INITIAL_NUM_NANES.
(create_archive): Update use.
Diffstat (limited to 'locale')
-rw-r--r-- | locale/C-address.c | 1 | ||||
-rw-r--r-- | locale/C-collate.c | 1 | ||||
-rw-r--r-- | locale/C-ctype.c | 1 | ||||
-rw-r--r-- | locale/C-identification.c | 1 | ||||
-rw-r--r-- | locale/C-measurement.c | 1 | ||||
-rw-r--r-- | locale/C-messages.c | 1 | ||||
-rw-r--r-- | locale/C-monetary.c | 1 | ||||
-rw-r--r-- | locale/C-name.c | 1 | ||||
-rw-r--r-- | locale/C-numeric.c | 1 | ||||
-rw-r--r-- | locale/C-paper.c | 1 | ||||
-rw-r--r-- | locale/C-telephone.c | 1 | ||||
-rw-r--r-- | locale/C-time.c | 1 | ||||
-rw-r--r-- | locale/Makefile | 4 | ||||
-rw-r--r-- | locale/findlocale.c | 21 | ||||
-rw-r--r-- | locale/hashval.h | 3 | ||||
-rw-r--r-- | locale/loadarchive.c | 447 | ||||
-rw-r--r-- | locale/loadlocale.c | 188 | ||||
-rw-r--r-- | locale/localeinfo.h | 52 | ||||
-rw-r--r-- | locale/newlocale.c | 17 | ||||
-rw-r--r-- | locale/programs/localedef.c | 8 | ||||
-rw-r--r-- | locale/programs/localedef.h | 1 | ||||
-rw-r--r-- | locale/programs/locarchive.c | 416 | ||||
-rw-r--r-- | locale/setlocale.c | 16 |
23 files changed, 990 insertions, 195 deletions
diff --git a/locale/C-address.c b/locale/C-address.c index 12c9dde8d7..78b5a28468 100644 --- a/locale/C-address.c +++ b/locale/C-address.c @@ -30,7 +30,6 @@ const struct locale_data _nl_C_LC_ADDRESS attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 13, { { string: "%a%N%f%N%d%N%b%N%s %h %e %r%N%C-%z %T%N%c%N" }, diff --git a/locale/C-collate.c b/locale/C-collate.c index 0f31b08d10..f6db63feb7 100644 --- a/locale/C-collate.c +++ b/locale/C-collate.c @@ -104,7 +104,6 @@ const struct locale_data _nl_C_LC_COLLATE attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 19, { /* _NL_COLLATE_NRULES */ diff --git a/locale/C-ctype.c b/locale/C-ctype.c index 3dcd5fe121..57234844ce 100644 --- a/locale/C-ctype.c +++ b/locale/C-ctype.c @@ -544,7 +544,6 @@ const struct locale_data _nl_C_LC_CTYPE attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 1, /* Enable transliteration by default. */ - NULL, NR_FIXED + NR_CLASSES + NR_MAPS, { /* _NL_CTYPE_CLASS */ diff --git a/locale/C-identification.c b/locale/C-identification.c index c2fd478289..542b33ccaf 100644 --- a/locale/C-identification.c +++ b/locale/C-identification.c @@ -30,7 +30,6 @@ const struct locale_data _nl_C_LC_IDENTIFICATION attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 16, { { string: "ISO/IEC 14652 i18n FDCC-set" }, diff --git a/locale/C-measurement.c b/locale/C-measurement.c index 590432146f..0a9c9831af 100644 --- a/locale/C-measurement.c +++ b/locale/C-measurement.c @@ -30,7 +30,6 @@ const struct locale_data _nl_C_LC_MEASUREMENT attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 2, { { string: "\1" }, diff --git a/locale/C-messages.c b/locale/C-messages.c index a118398394..f446e3dda6 100644 --- a/locale/C-messages.c +++ b/locale/C-messages.c @@ -30,7 +30,6 @@ const struct locale_data _nl_C_LC_MESSAGES attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 5, { { string: "^[yY]" }, diff --git a/locale/C-monetary.c b/locale/C-monetary.c index 0c91145f5c..643a1c4198 100644 --- a/locale/C-monetary.c +++ b/locale/C-monetary.c @@ -34,7 +34,6 @@ const struct locale_data _nl_C_LC_MONETARY attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 46, { { string: "" }, diff --git a/locale/C-name.c b/locale/C-name.c index 8526ec076f..7e714b3a37 100644 --- a/locale/C-name.c +++ b/locale/C-name.c @@ -30,7 +30,6 @@ const struct locale_data _nl_C_LC_NAME attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 7, { { string: "%p%t%g%t%m%t%f" }, diff --git a/locale/C-numeric.c b/locale/C-numeric.c index 3c096c6479..1171a4b973 100644 --- a/locale/C-numeric.c +++ b/locale/C-numeric.c @@ -27,7 +27,6 @@ const struct locale_data _nl_C_LC_NUMERIC attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 6, { { string: "." }, diff --git a/locale/C-paper.c b/locale/C-paper.c index fb3e619ad3..62d1a7dec7 100644 --- a/locale/C-paper.c +++ b/locale/C-paper.c @@ -30,7 +30,6 @@ const struct locale_data _nl_C_LC_PAPER attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 3, { { word: 297 }, diff --git a/locale/C-telephone.c b/locale/C-telephone.c index 795de3c1f3..abd667ea8a 100644 --- a/locale/C-telephone.c +++ b/locale/C-telephone.c @@ -30,7 +30,6 @@ const struct locale_data _nl_C_LC_TELEPHONE attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 5, { { string: "+%c %a %l" }, diff --git a/locale/C-time.c b/locale/C-time.c index 9ce133a6ce..b50fea57b9 100644 --- a/locale/C-time.c +++ b/locale/C-time.c @@ -29,7 +29,6 @@ const struct locale_data _nl_C_LC_TIME attribute_hidden = NULL, 0, 0, /* no file mapped */ UNDELETABLE, 0, - NULL, 111, { { string: "Sun" }, diff --git a/locale/Makefile b/locale/Makefile index d3c62adc07..9509c1f7f7 100644 --- a/locale/Makefile +++ b/locale/Makefile @@ -35,8 +35,8 @@ distribute = localeinfo.h categories.def iso-639.def iso-3166.def \ locfile-kw.gperf locfile-kw.h linereader.h \ locfile.h charmap.h repertoire.h localedef.h \ 3level.h charmap-dir.h locarchive.c) -routines = setlocale findlocale loadlocale localeconv nl_langinfo \ - nl_langinfo_l mb_cur_max \ +routines = setlocale findlocale loadlocale loadarchive \ + localeconv nl_langinfo nl_langinfo_l mb_cur_max \ newlocale duplocale freelocale uselocale tests = tst-C-locale categories = ctype messages monetary numeric time paper name \ diff --git a/locale/findlocale.c b/locale/findlocale.c index 75784bab9f..80480113c0 100644 --- a/locale/findlocale.c +++ b/locale/findlocale.c @@ -42,8 +42,11 @@ extern struct locale_data *const _nl_C[] attribute_hidden; which are somehow addressed. */ struct loaded_l10nfile *_nl_locale_file_list[__LC_LAST]; +const char _nl_default_locale_path[] attribute_hidden = LOCALEDIR; + struct locale_data * +internal_function _nl_find_locale (const char *locale_path, size_t locale_path_len, int category, const char **name) { @@ -82,6 +85,19 @@ _nl_find_locale (const char *locale_path, size_t locale_path_len, return _nl_C[category]; } + /* We really have to load some data. First we try the archive, + but only if there was no LOCPATH environment variable specified. */ + if (__builtin_expect (locale_path == NULL, 1)) + { + struct locale_data *data = _nl_load_locale_from_archive (category, name); + if (__builtin_expect (data != NULL, 1)) + return data; + + /* Nothing in the archive. Set the default path to search below. */ + locale_path = _nl_default_locale_path; + locale_path_len = sizeof _nl_default_locale_path; + } + /* We really have to load some data. First see whether the name is an alias. Please note that this makes it impossible to have "C" or "POSIX" as aliases. */ @@ -99,7 +115,7 @@ _nl_find_locale (const char *locale_path, size_t locale_path_len, Beside the first all of them are allowed to be missing. If the full specified locale is not found, the less specific one are - looked for. The various part will be stripped of according to + looked for. The various part will be stripped off according to the following order: (1) codeset (2) normalized codeset @@ -236,6 +252,7 @@ _nl_find_locale (const char *locale_path, size_t locale_path_len, /* Calling this function assumes the lock for handling global locale data is acquired. */ void +internal_function _nl_remove_locale (int locale, struct locale_data *data) { if (--data->usage_count == 0) @@ -258,7 +275,7 @@ _nl_remove_locale (int locale, struct locale_data *data) #ifdef _POSIX_MAPPED_FILES /* Really delete the data. First delete the real data. */ - if (__builtin_expect (data->mmaped, 1)) + if (__builtin_expect (data->alloc == ld_mapped, 1)) { /* Try to unmap the area. If this fails we mark the area as permanent. */ diff --git a/locale/hashval.h b/locale/hashval.h index e35957dde3..f846cdfb27 100644 --- a/locale/hashval.h +++ b/locale/hashval.h @@ -19,7 +19,8 @@ 02111-1307 USA. */ #ifndef LONGBITS -# define LONGBITS (sizeof (long int) * BITSPERBYTE) +# include <limits.h> +# define LONGBITS (sizeof (long int) * CHAR_BIT) #endif unsigned long int compute_hashval (const void *key, size_t keylen); diff --git a/locale/loadarchive.c b/locale/loadarchive.c new file mode 100644 index 0000000000..08e5f94905 --- /dev/null +++ b/locale/loadarchive.c @@ -0,0 +1,447 @@ +/* Code to load locale data from the locale archive file. + Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <locale.h> +#include <stddef.h> +#include <stdbool.h> +#include <errno.h> +#include <assert.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/param.h> + +#include "localeinfo.h" +#include "locarchive.h" + +/* Define the hash function. We define the function as static inline. */ +#define compute_hashval static inline compute_hashval +#include "hashval.h" +#undef compute_hashval + +#undef LOCALEDIR +#define LOCALEDIR "/spare/roland/tmp/usr/lib/locale/" + +/* Name of the locale archive file. */ +static const char archfname[] = LOCALEDIR "locale-archive"; + + +/* Record of contiguous pages already mapped from the locale archive. */ +struct archmapped +{ + void *ptr; + uint32_t from; + uint32_t len; + struct archmapped *next; +}; +static struct archmapped *archmapped; + +/* This describes the mapping at the beginning of the file that contains + the header data. There could be data in the following partial page, + so this is searched like any other. Once the archive has been used, + ARCHMAPPED points to this; if mapping the archive header failed, + then headmap.ptr is null. */ +static struct archmapped headmap; +static struct stat64 archive_stat; /* stat of archive when header mapped. */ + +/* Record of locales that we have already loaded from the archive. */ +struct locale_in_archive +{ + struct locale_in_archive *next; + const char *name; + struct locale_data *data[__LC_LAST]; +}; +static struct locale_in_archive *archloaded; + + +/* Local structure and subroutine of _nl_load_archive, see below. */ +struct range +{ + uint32_t from; + uint32_t len; + int category; + void *result; +}; + +static int +rangecmp (const void *p1, const void *p2) +{ + return ((struct range *) p1)->from - ((struct range *) p2)->from; +} + + +/* Calculate the amount of space needed for all the tables described + by the given header. Note we do not include the empty table space + that has been preallocated in the file, so our mapping may not be + large enough if localedef adds data to the file in place. However, + doing that would permute the header fields while we are accessing + them and thus not be safe anyway, so we don't allow for that. */ +static inline off_t +calculate_head_size (const struct locarhead *h) +{ + off_t namehash_end = (h->namehash_offset + + h->namehash_size * sizeof (struct namehashent)); + off_t string_end = h->string_offset + h->string_used; + off_t locrectab_end = (h->locrectab_offset + + h->locrectab_used * sizeof (struct locrecent)); + return MAX (namehash_end, MAX (string_end, locrectab_end)); +} + + +/* Find the locale *NAMEP in the locale archive, and return the + internalized data structure for its CATEGORY data. If this locale has + already been loaded from the archive, just returns the existing data + structure. If successful, sets *NAMEP to point directly into the mapped + archive string table; that way, the next call can short-circuit strcmp. */ +struct locale_data * +internal_function +_nl_load_locale_from_archive (int category, const char **namep) +{ + const char *name = *namep; + struct + { + void *addr; + size_t len; + } results[__LC_LAST]; + struct locale_in_archive *lia; + struct locarhead *head; + struct namehashent *namehashtab; + struct locrecent *locrec; + struct archmapped *mapped; + struct archmapped *last; + unsigned long int hval; + size_t idx; + size_t incr; + struct range ranges[__LC_LAST - 1]; + int nranges; + int cnt; + size_t ps = __sysconf (_SC_PAGE_SIZE); + int fd = -1; + + /* Check if we have already loaded this locale from the archive. + If we previously loaded the locale but found bogons in the data, + then we will have stored a null pointer to return here. */ + for (lia = archloaded; lia != NULL; lia = lia->next) + if (name == lia->name || !strcmp (name, lia->name)) + { + *namep = lia->name; + return lia->data[category]; + } + + { + /* If the name contains a codeset, then we normalize the name before + doing the lookup. */ + const char *p = strchr (name, '.'); + if (p != NULL && p[1] != '@' && p[1] != '\0') + { + const char *rest = __strchrnul (++p, '@'); + const char *normalized_codeset = _nl_normalize_codeset (p, rest - p); + if (normalized_codeset == NULL) /* malloc failure */ + return NULL; + if (strncmp (normalized_codeset, p, rest - p) != 0 + || normalized_codeset[rest - p] != '\0') + { + /* There is a normalized codeset name that is different from + what was specified; reconstruct a new locale name using it. */ + size_t normlen = strlen (normalized_codeset); + size_t restlen = strlen (rest) + 1; + char *newname = alloca (p - name + normlen + restlen); + memcpy (__mempcpy (__mempcpy (newname, name, p - name), + normalized_codeset, normlen), + rest, restlen); + free ((char *) normalized_codeset); + name = newname; + } + } + } + + /* Make sure the archive is loaded. */ + if (archmapped == NULL) + { + /* We do this early as a sign that we have tried to open the archive. + If headmap.ptr remains null, that's an indication that we tried + and failed, so we won't try again. */ + archmapped = &headmap; + + /* The archive has never been opened. */ + fd = __open64 (archfname, O_RDONLY); + if (fd < 0) + /* Cannot open the archive, for whatever reason. */ + return NULL; + + if (__fxstat64 (_STAT_VER, fd, &archive_stat) == -1) + { + /* stat failed, very strange. */ + close_and_out: + __close (fd); + return NULL; + } + + if (sizeof (void *) > 4) + { + /* We will just map the whole file, what the hell. */ + void *result = __mmap64 (NULL, archive_stat.st_size, + PROT_READ, MAP_SHARED, fd, 0); + if (result == MAP_FAILED) + goto close_and_out; + /* Check whether the file is large enough for the sizes given in the + header. */ + if (calculate_head_size ((const struct locarhead *) result) + > archive_stat.st_size) + { + (void) munmap (result, archive_stat.st_size); + goto close_and_out; + } + __close (fd); + fd = -1; + + headmap.ptr = result; + /* headmap.from already initialized to zero. */ + headmap.len = archive_stat.st_size; + } + else + { + struct locarhead head; + off_t head_size; + void *result; + + if (TEMP_FAILURE_RETRY (__read (fd, &head, sizeof (head))) + != sizeof (head)) + goto close_and_out; + head_size = calculate_head_size (&head); + if (head_size > archive_stat.st_size) + goto close_and_out; + result = __mmap64 (NULL, head_size, PROT_READ, MAP_SHARED, fd, 0); + if (result == MAP_FAILED) + goto close_and_out; + + /* Record that we have mapped the initial pages of the file. */ + headmap.ptr = result; + headmap.len = MIN ((head_size + ps - 1) & ~(ps - 1), + archive_stat.st_size); + } + } + + /* If there is no archive or it cannot be loaded for some reason fail. */ + if (__builtin_expect (headmap.ptr == NULL, 0)) + return NULL; + + /* We have the archive available. To find the name we first have to + determine its hash value. */ + hval = compute_hashval (name, strlen (name)); + + head = headmap.ptr; + namehashtab = (struct namehashent *) ((char *) head + + head->namehash_offset); + + idx = hval % head->namehash_size; + incr = 1 + hval % (head->namehash_size - 2); + + /* If the name_offset field is zero this means this is a + deleted entry and therefore no entry can be found. */ + while (1) + { + if (namehashtab[idx].name_offset == 0) + /* Not found. */ + return NULL; + + if (namehashtab[idx].hashval == hval + && strcmp (name, headmap.ptr + namehashtab[idx].name_offset) == 0) + /* Found the entry. */ + break; + + idx += incr; + if (idx >= head->namehash_size) + idx -= head->namehash_size; + } + + /* We found an entry. It might be a placeholder for a removed one. */ + if (namehashtab[idx].locrec_offset == 0) + return NULL; + + locrec = (struct locrecent *) (headmap.ptr + namehashtab[idx].locrec_offset); + + if (sizeof (void *) > 4 /* || headmap.len == archive_stat.st_size */) + { + /* We already have the whole locale archive mapped in. */ + assert (headmap.len == archive_stat.st_size); + for (cnt = 0; cnt < __LC_LAST; ++cnt) + if (cnt != LC_ALL) + { + if (locrec->record[cnt].offset + locrec->record[cnt].len + > headmap.len) + /* The archive locrectab contains bogus offsets. */ + return NULL; + results[cnt].addr = headmap.ptr + locrec->record[cnt].offset; + results[cnt].len = locrec->record[cnt].len; + } + } + else + { + /* Get the offsets of the data files and sort them. */ + for (cnt = nranges = 0; cnt < __LC_LAST; ++cnt) + if (cnt != LC_ALL) + { + ranges[nranges].from = locrec->record[cnt].offset; + ranges[nranges].len = locrec->record[cnt].len; + ranges[nranges].category = cnt; + ranges[nranges].result = NULL; + + ++nranges; + } + + qsort (ranges, nranges, sizeof (ranges[0]), rangecmp); + + /* The information about mmap'd blocks is kept in a list. + Skip over the blocks which are before the data we need. */ + last = mapped = archmapped; + for (cnt = 0; cnt < nranges; ++cnt) + { + int upper; + size_t from; + size_t to; + void *addr; + struct archmapped *newp; + + /* Determine whether the appropriate page is already mapped. */ + while (mapped != NULL + && mapped->from + mapped->len <= ranges[cnt].from) + { + last = mapped; + mapped = mapped->next; + } + + /* Do we have a match? */ + if (mapped != NULL + && mapped->from <= ranges[cnt].from + && ((char *) ranges[cnt].from + ranges[cnt].len + <= (char *) mapped->from + mapped->len)) + { + /* Yep, already loaded. */ + results[ranges[cnt].category].addr = ((char *) mapped->ptr + + ranges[cnt].from + - mapped->from); + results[ranges[cnt].category].len = ranges[cnt].len; + continue; + } + + /* Map the range with the locale data from the file. We will + try to cover as much of the locale as possible. I.e., if the + next category (next as in "next offset") is on the current or + immediately following page we use it as well. */ + assert (powerof2 (ps)); + from = ranges[cnt].from & ~(ps - 1); + upper = cnt; + do + { + to = ((ranges[upper].from + ranges[upper].len + ps - 1) + & ~(ps - 1)); + ++upper; + } + /* Loop while still in contiguous pages. */ + while (upper < nranges && ranges[upper].from < to + ps); + + if (to > archive_stat.st_size) + /* The archive locrectab contains bogus offsets. */ + return NULL; + + /* Open the file if it hasn't happened yet. */ + if (fd == -1) + { + struct stat64 st; + fd = __open64 (archfname, O_RDONLY); + if (fd == -1) + /* Cannot open the archive, for whatever reason. */ + return NULL; + /* Now verify we think this is really the same archive file + we opened before. If it has been changed we cannot trust + the header we read previously. */ + if (__fxstat64 (_STAT_VER, fd, &st) < 0 + || st.st_size != archive_stat.st_size + || st.st_mtime != archive_stat.st_mtime + || st.st_dev != archive_stat.st_dev + || st.st_ino != archive_stat.st_ino) + return NULL; + } + + /* Map the range from the archive. */ + addr = __mmap64 (NULL, to - from, PROT_READ, MAP_SHARED, fd, from); + if (addr == MAP_FAILED) + return NULL; + + /* Allocate a record for this mapping. */ + newp = (struct archmapped *) malloc (sizeof (struct archmapped)); + if (newp == NULL) + { + (void) munmap (addr, to - from); + return NULL; + } + + /* And queue it. */ + newp->ptr = addr; + newp->from = from; + newp->len = to - from; + assert (last->next == mapped); + newp->next = mapped; + last->next = newp; + last = newp; + + /* Determine the load addresses for the category data. */ + do + { + assert (ranges[cnt].from >= from); + results[ranges[cnt].category].addr = ((char *) addr + + ranges[cnt].from - from); + results[ranges[cnt].category].len = ranges[cnt].len; + } + while (++cnt < upper); + --cnt; /* The 'for' will increase 'cnt' again. */ + } + } + + /* We succeeded in mapping all the necessary regions of the archive. + Now we need the expected data structures to point into the data. */ + + lia = malloc (sizeof *lia); + if (__builtin_expect (lia == NULL, 0)) + return NULL; + + lia->name = headmap.ptr + namehashtab[idx].name_offset; + lia->next = archloaded; + archloaded = lia; + + for (cnt = 0; cnt < __LC_LAST; ++cnt) + if (cnt != LC_ALL) + { + lia->data[cnt] = _nl_intern_locale_data (cnt, + results[cnt].addr, + results[cnt].len); + if (__builtin_expect (lia->data[cnt] != NULL, 1)) + { + /* _nl_intern_locale_data leaves us these fields to initialize. */ + lia->data[cnt]->alloc = ld_archive; + lia->data[cnt]->name = lia->name; + } + } + + *namep = lia->name; + return lia->data[category]; +} diff --git a/locale/loadlocale.c b/locale/loadlocale.c index 53962babac..14f3773c47 100644 --- a/locale/loadlocale.c +++ b/locale/loadlocale.c @@ -60,21 +60,82 @@ static const enum value_type *_nl_value_types[] = }; -void -_nl_load_locale (struct loaded_l10nfile *file, int category) +struct locale_data * +internal_function +_nl_intern_locale_data (int category, const void *data, size_t datasize) { - int fd; - struct + const struct { unsigned int magic; unsigned int nstrings; unsigned int strindex[0]; - } *filedata; + } *const filedata = data; + struct locale_data *newdata; + size_t cnt; + + if (__builtin_expect (datasize < sizeof *filedata, 0) + || __builtin_expect (filedata->magic != LIMAGIC (category), 0)) + { + /* Bad data file. */ + __set_errno (EINVAL); + return NULL; + } + + if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category], + 0) + || (__builtin_expect (sizeof *filedata + + filedata->nstrings * sizeof (unsigned int) + >= datasize, 0))) + { + /* Insufficient data. */ + __set_errno (EINVAL); + return NULL; + } + + newdata = malloc (sizeof *newdata + + filedata->nstrings * sizeof (union locale_data_value)); + if (newdata == NULL) + return NULL; + + newdata->filedata = (void *) filedata; + newdata->filesize = datasize; + newdata->usage_count = 0; + newdata->use_translit = 0; + newdata->nstrings = filedata->nstrings; + for (cnt = 0; cnt < newdata->nstrings; ++cnt) + { + size_t idx = filedata->strindex[cnt]; + if (__builtin_expect (idx > newdata->filesize, 0)) + { + puntdata: + free (newdata); + __set_errno (EINVAL); + return NULL; + } + if (__builtin_expect (_nl_value_types[category][cnt] == word, 0)) + { + if (idx % __alignof__ (u_int32_t) != 0) + goto puntdata; + newdata->values[cnt].word = + *((const u_int32_t *) (newdata->filedata + idx)); + } + else + newdata->values[cnt].string = newdata->filedata + idx; + } + + return newdata; +} + +void +internal_function +_nl_load_locale (struct loaded_l10nfile *file, int category) +{ + int fd; + void *filedata; struct stat64 st; struct locale_data *newdata; int save_err; - int mmaped = 1; - size_t cnt; + int alloc = ld_mapped; file->decided = 1; file->data = NULL; @@ -85,7 +146,11 @@ _nl_load_locale (struct loaded_l10nfile *file, int category) return; if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0) - goto puntfd; + { + puntfd: + __close (fd); + return; + } if (__builtin_expect (S_ISDIR (st.st_mode), 0)) { /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo @@ -122,25 +187,15 @@ _nl_load_locale (struct loaded_l10nfile *file, int category) /* Some systems do not have this flag; it is superfluous. */ # define MAP_FILE 0 # endif -# ifndef MAP_INHERIT - /* Some systems might lack this; they lose. */ -# define MAP_INHERIT 0 -# endif - filedata = (void *) __mmap ((caddr_t) 0, st.st_size, PROT_READ, - MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0); - if (__builtin_expect ((void *) filedata != MAP_FAILED, 1)) - { - if (__builtin_expect (st.st_size < sizeof (*filedata), 0)) - /* This cannot be a locale data file since it's too small. */ - goto puntfd; - } - else + filedata = __mmap ((caddr_t) 0, st.st_size, + PROT_READ, MAP_FILE|MAP_COPY, fd, 0); + if (__builtin_expect (filedata == MAP_FAILED, 0)) { if (__builtin_expect (errno, ENOSYS) == ENOSYS) { #endif /* _POSIX_MAPPED_FILES */ /* No mmap; allocate a buffer and read from the file. */ - mmaped = 0; + alloc = ld_malloced; filedata = malloc (st.st_size); if (filedata != NULL) { @@ -160,87 +215,58 @@ _nl_load_locale (struct loaded_l10nfile *file, int category) p += nread; to_read -= nread; } + __set_errno (save_err); } - else - goto puntfd; - __set_errno (save_err); #ifdef _POSIX_MAPPED_FILES } - else - goto puntfd; } #endif /* _POSIX_MAPPED_FILES */ - if (__builtin_expect (filedata->magic != LIMAGIC (category), 0)) - /* Bad data file in either byte order. */ + /* We have mapped the data, so we no longer need the descriptor. */ + __close (fd); + + if (__builtin_expect (filedata == NULL, 0)) + /* We failed to map or read the data. */ + return; + + newdata = _nl_intern_locale_data (category, filedata, st.st_size); + if (__builtin_expect (newdata == NULL, 0)) + /* Bad data. */ { - puntmap: #ifdef _POSIX_MAPPED_FILES - __munmap ((caddr_t) filedata, st.st_size); + if (alloc == ld_mapped) + __munmap ((caddr_t) filedata, st.st_size); #endif - puntfd: - __close (fd); return; } - if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category], - 0) - || (__builtin_expect (sizeof *filedata - + filedata->nstrings * sizeof (unsigned int) - >= (size_t) st.st_size, 0))) - { - /* Insufficient data. */ - __set_errno (EINVAL); - goto puntmap; - } - - newdata = malloc (sizeof *newdata - + filedata->nstrings * sizeof (union locale_data_value)); - if (newdata == NULL) - goto puntmap; - + /* _nl_intern_locale_data leaves us these fields to initialize. */ newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */ - newdata->filedata = (void *) filedata; - newdata->filesize = st.st_size; - newdata->mmaped = mmaped; - newdata->usage_count = 0; - newdata->use_translit = 0; - newdata->options = NULL; - newdata->nstrings = filedata->nstrings; - for (cnt = 0; cnt < newdata->nstrings; ++cnt) - { - off_t idx = filedata->strindex[cnt]; - if (__builtin_expect (idx > newdata->filesize, 0)) - { - free (newdata); - __set_errno (EINVAL); - goto puntmap; - } - if (__builtin_expect (_nl_value_types[category][cnt] == word, 0)) - { - assert (idx % __alignof__ (u_int32_t) == 0); - newdata->values[cnt].word = - *((u_int32_t *) (newdata->filedata + idx)); - } - else - newdata->values[cnt].string = newdata->filedata + idx; - } + newdata->alloc = alloc; - __close (fd); file->data = newdata; } void +internal_function _nl_unload_locale (struct locale_data *locale) { + switch (__builtin_expect (locale->alloc, ld_mapped)) + { + case ld_malloced: + free ((void *) locale->filedata); + break; + case ld_mapped: #ifdef _POSIX_MAPPED_FILES - if (__builtin_expect (locale->mmaped, 1)) - __munmap ((caddr_t) locale->filedata, locale->filesize); - else + __munmap ((caddr_t) locale->filedata, locale->filesize); + break; #endif - free ((void *) locale->filedata); + case ld_archive: /* Nothing to do. */ + break; + } + + if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive) + free ((char *) locale->name); - free ((char *) locale->options); - free ((char *) locale->name); free (locale); } diff --git a/locale/localeinfo.h b/locale/localeinfo.h index 97471e9be5..31de4d0974 100644 --- a/locale/localeinfo.h +++ b/locale/localeinfo.h @@ -47,14 +47,17 @@ struct locale_data const char *name; const char *filedata; /* Region mapping the file data. */ off_t filesize; /* Size of the file (and the region). */ - int mmaped; /* If nonzero the data is mmaped. */ + enum /* Flavor of storage used for those. */ + { + ld_malloced, /* Both are malloc'd. */ + ld_mapped, /* name is malloc'd, filedata mmap'd */ + ld_archive /* Both point into mmap'd archive regions. */ + } alloc; unsigned int usage_count; /* Counter for users. */ int use_translit; /* Nonzero if the mb*towv*() and wc*tomb() functions should use transliteration. */ - const char *options; /* Extra options from the locale name, - not used in the path to the locale data. */ unsigned int nstrings; /* Number of strings below. */ union locale_data_value @@ -152,6 +155,7 @@ extern const char _nl_C_codeset[] attribute_hidden; Each is malloc'd unless it is _nl_C_name. */ extern const char *_nl_current_names[] attribute_hidden; + #ifndef SHARED /* For each category declare the variable for the current locale data. */ @@ -222,22 +226,50 @@ extern struct __locale_struct _nl_global_locale attribute_hidden; #endif +/* Default search path if no LOCPATH environment variable. */ +extern const char _nl_default_locale_path[] attribute_hidden; + /* Load the locale data for CATEGORY from the file specified by *NAME. - If *NAME is "", use environment variables as specified by POSIX, - and fill in *NAME with the actual name used. The directories - listed in LOCALE_PATH are searched for the locale files. */ + If *NAME is "", use environment variables as specified by POSIX, and + fill in *NAME with the actual name used. If LOCALE_PATH is not null, + those directories are searched for the locale files. If it's null, + the locale archive is checked first and then _nl_default_locale_path + is searched for locale files. */ extern struct locale_data *_nl_find_locale (const char *locale_path, size_t locale_path_len, - int category, const char **name); + int category, const char **name) + internal_function attribute_hidden; /* Try to load the file described by FILE. */ -extern void _nl_load_locale (struct loaded_l10nfile *file, int category); +extern void _nl_load_locale (struct loaded_l10nfile *file, int category) + internal_function attribute_hidden; /* Free all resource. */ -extern void _nl_unload_locale (struct locale_data *locale); +extern void _nl_unload_locale (struct locale_data *locale) + internal_function attribute_hidden; /* Free the locale and give back all memory if the usage count is one. */ -extern void _nl_remove_locale (int locale, struct locale_data *data); +extern void _nl_remove_locale (int locale, struct locale_data *data) + internal_function attribute_hidden; + +/* Find the locale *NAMEP in the locale archive, and return the + internalized data structure for its CATEGORY data. If this locale has + already been loaded from the archive, just returns the existing data + structure. If successful, sets *NAMEP to point directly into the mapped + archive string table; that way, the next call can short-circuit strcmp. */ +extern struct locale_data *_nl_load_locale_from_archive (int category, + const char **namep) + internal_function attribute_hidden; + +/* Validate the contents of a locale file and set up the in-core + data structure to point into the data. This leaves the `alloc' + and `name' fields uninitialized, for the caller to fill in. + If any bogons are detected in the data, this will refuse to + intern it, and return a null pointer instead. */ +extern struct locale_data *_nl_intern_locale_data (int category, + const void *data, + size_t datasize) + internal_function attribute_hidden; /* Return `era' entry which corresponds to TP. Used in strftime. */ diff --git a/locale/newlocale.c b/locale/newlocale.c index 6bab98e219..14c116beb1 100644 --- a/locale/newlocale.c +++ b/locale/newlocale.c @@ -80,19 +80,22 @@ __newlocale (int category_mask, const char *locale, __locale_t base) /* We perhaps really have to load some data. So we determine the path in which to look for the data now. The environment variable `LOCPATH' must only be used when the binary has no SUID or SGID - bit set. */ + bit set. If using the default path, we tell _nl_find_locale + by passing null and it can check the canonical locale archive. */ locale_path = NULL; locale_path_len = 0; locpath_var = getenv ("LOCPATH"); if (locpath_var != NULL && locpath_var[0] != '\0') - if (__argz_create_sep (locpath_var, ':', - &locale_path, &locale_path_len) != 0) - return NULL; + { + if (__argz_create_sep (locpath_var, ':', + &locale_path, &locale_path_len) != 0) + return NULL; - if (__argz_append (&locale_path, &locale_path_len, - LOCALEDIR, sizeof (LOCALEDIR)) != 0) - return NULL; + if (__argz_add_sep (&locale_path, &locale_path_len, + _nl_default_locale_path, ':') != 0) + return NULL; + } /* Get the names for the locales we are interested in. We either allow a composite name or a single name. */ diff --git a/locale/programs/localedef.c b/locale/programs/localedef.c index c1d347b06a..5bbf0bf7e3 100644 --- a/locale/programs/localedef.c +++ b/locale/programs/localedef.c @@ -76,6 +76,9 @@ static const char *input_file; /* Name of the repertoire map file. */ const char *repertoire_global; +/* Name of the locale.alias file. */ +const char *alias_file; + /* List of all locales. */ static struct localedef_t *locales; @@ -140,6 +143,8 @@ static const struct argp_option options[] = { "delete-from-archive", OPT_DELETE_FROM_ARCHIVE, NULL, 0, N_("Remove locales named by parameters from archive") }, { "list-archive", OPT_LIST_ARCHIVE, NULL, 0, N_("List content of archive") }, + { "alias-file", 'A', "FILE", 0, + N_("locale.alias file to consult when making archive")}, { NULL, 0, NULL, 0, NULL } }; @@ -331,6 +336,9 @@ parse_opt (int key, char *arg, struct argp_state *state) case 'f': charmap_file = arg; break; + case 'A': + alias_file = arg; + break; case 'i': input_file = arg; break; diff --git a/locale/programs/localedef.h b/locale/programs/localedef.h index 3217338ee7..41f72860d4 100644 --- a/locale/programs/localedef.h +++ b/locale/programs/localedef.h @@ -118,6 +118,7 @@ extern int oldstyle_tables; extern const char *repertoire_global; extern int max_locarchive_open_retry; extern bool no_archive; +extern const char *alias_file; /* Prototypes for a few program-wide used functions. */ diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c index de026b2a74..267d7baaa4 100644 --- a/locale/programs/locarchive.c +++ b/locale/programs/locarchive.c @@ -31,6 +31,7 @@ #include <locale.h> #include <stdbool.h> #include <stdio.h> +#include <stdio_ext.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -59,7 +60,7 @@ static const char *locnames[] = /* Size of the initial archive header. */ -#define INITIAL_NUM_NANES 450 +#define INITIAL_NUM_NAMES 450 #define INITIAL_SIZE_STRINGS 3500 #define INITIAL_NUM_LOCREC 350 #define INITIAL_NUM_SUMS 2000 @@ -85,7 +86,7 @@ create_archive (const char *archivefname, struct locarhandle *ah) head.magic = AR_MAGIC; head.namehash_offset = sizeof (struct locarhead); head.namehash_used = 0; - head.namehash_size = next_prime (INITIAL_NUM_NANES); + head.namehash_size = next_prime (INITIAL_NUM_NAMES); head.string_offset = (head.namehash_offset + head.namehash_size * sizeof (struct namehashent)); @@ -166,6 +167,9 @@ create_archive (const char *archivefname, struct locarhandle *ah) ah->len = total; } +/* forward decl for below */ +static uint32_t add_locale (struct locarhandle *ah, const char *name, + locale_data_t data, bool replace); static void enlarge_archive (struct locarhandle *ah, const struct locarhead *head) @@ -300,10 +304,9 @@ enlarge_archive (struct locarhandle *ah, const struct locarhead *head) old_data[idx].sum); } - if (add_locale_to_archive (&new_ah, - ((char *) ah->addr - + oldnamehashtab[cnt].name_offset), - old_data, 0) != 0) + if (add_locale (&new_ah, + ((char *) ah->addr + oldnamehashtab[cnt].name_offset), + old_data, 0) == 0) error (EXIT_FAILURE, 0, _("cannot extend locale archive file")); } @@ -446,16 +449,123 @@ close_archive (struct locarhandle *ah) } } +#include "../../intl/explodename.c" +#include "../../intl/l10nflist.c" + +static struct namehashent * +insert_name (struct locarhandle *ah, + const char *name, size_t name_len, bool replace) +{ + const struct locarhead *const head = ah->addr; + struct namehashent *namehashtab + = (struct namehashent *) ((char *) ah->addr + head->namehash_offset); + unsigned int insert_idx, idx, incr; + + /* Hash value of the locale name. */ + uint32_t hval = compute_hashval (name, name_len); + + insert_idx = -1; + idx = hval % head->namehash_size; + incr = 1 + hval % (head->namehash_size - 2); + + /* If the name_offset field is zero this means this is a + deleted entry and therefore no entry can be found. */ + while (namehashtab[idx].name_offset != 0) + { + if (namehashtab[idx].hashval == hval + && strcmp (name, + (char *) ah->addr + namehashtab[idx].name_offset) == 0) + { + /* Found the entry. */ + if (namehashtab[idx].locrec_offset != 0 && ! replace) + { + if (! be_quiet) + error (0, 0, _("locale '%s' already exists"), name); + return NULL; + } + + break; + } + + /* Remember the first place we can insert the new entry. */ + if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1) + insert_idx = idx; + + idx += incr; + if (idx >= head->namehash_size) + idx -= head->namehash_size; + } + + /* Add as early as possible. */ + if (insert_idx != -1) + idx = insert_idx; + + namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */ + return &namehashtab[idx]; +} + +static void +add_alias (struct locarhandle *ah, const char *alias, bool replace, + const char *oldname, uint32_t locrec_offset) +{ + struct locarhead *head = ah->addr; + const size_t name_len = strlen (alias); + struct namehashent *namehashent = insert_name (ah, alias, strlen (alias), + replace); + if (namehashent == NULL && ! replace) + return; + + if (namehashent->name_offset == 0) + { + /* We are adding a new hash entry for this alias. + Determine whether we have to resize the file. */ + if (head->string_used + name_len + 1 > head->string_size + || 100 * head->namehash_used > 75 * head->namehash_size) + { + /* The current archive is not large enough. */ + enlarge_archive (ah, head); + + /* The locrecent might have moved, so we have to look up + the old name afresh. */ + namehashent = insert_name (ah, oldname, strlen (oldname), true); + assert (namehashent->name_offset != 0); + assert (namehashent->locrec_offset != 0); + locrec_offset = namehashent->locrec_offset; + + /* Tail call to try the whole thing again. */ + add_alias (ah, alias, replace, oldname, locrec_offset); + return; + } + + /* Add the name string. */ + memcpy (ah->addr + head->string_offset + head->string_used, + alias, name_len + 1); + namehashent->name_offset = head->string_offset + head->string_used; + head->string_used += name_len + 1; + + ++head->namehash_used; + } + + if (namehashent->locrec_offset != 0) + { + /* Replacing an existing entry. + Mark that we are no longer using the old locrecent. */ + struct locrecent *locrecent + = (struct locrecent *) ((char *) ah->addr + + namehashent->locrec_offset); + --locrecent->refs; + } + + /* Point this entry at the locrecent installed for the main name. */ + namehashent->locrec_offset = locrec_offset; +} + /* Check the content of the archive for duplicates. Add the content - of the files if necessary. Add all the names, possibly overwriting - old files. */ -int -add_locale_to_archive (ah, name, data, replace) - struct locarhandle *ah; - const char *name; - locale_data_t data; - bool replace; + of the files if necessary. Returns the locrec_offset. */ +static uint32_t +add_locale (struct locarhandle *ah, + const char *name, locale_data_t data, bool replace) { /* First look for the name. If it already exists and we are not supposed to replace it don't do anything. If it does not exist @@ -467,9 +577,7 @@ add_locale_to_archive (ah, name, data, replace) uint32_t hval; unsigned int cnt; unsigned int idx; - unsigned int insert_idx; struct locarhead *head; - struct namehashent *namehashtab; struct namehashent *namehashent; unsigned int incr; struct locrecent *locrecent; @@ -477,8 +585,6 @@ add_locale_to_archive (ah, name, data, replace) head = ah->addr; sumhashtab = (struct sumhashent *) ((char *) ah->addr + head->sumhash_offset); - namehashtab = (struct namehashent *) ((char *) ah->addr - + head->namehash_offset); /* For each locale category data set determine whether the same data @@ -514,47 +620,10 @@ add_locale_to_archive (ah, name, data, replace) } } - - /* Hash value of the locale name. */ - hval = compute_hashval (name, name_len); - - insert_idx = -1; - idx = hval % head->namehash_size; - incr = 1 + hval % (head->namehash_size - 2); - - /* If the name_offset field is zero this means this is no - deleted entry and therefore no entry can be found. */ - while (namehashtab[idx].name_offset != 0) - { - if (namehashtab[idx].hashval == hval - && strcmp (name, - (char *) ah->addr + namehashtab[idx].name_offset) == 0) - { - /* Found the entry. */ - if (namehashtab[idx].locrec_offset != 0 && ! replace) - { - if (! be_quiet) - error (0, 0, _("locale '%s' already exists"), name); - return 1; - } - - break; - } - - /* Remember the first place we can insert the new entry. */ - if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1) - insert_idx = idx; - - idx += incr; - if (idx >= head->namehash_size) - idx -= head->namehash_size; - } - - /* Add as early as possible. */ - if (insert_idx != -1) - idx = insert_idx; - - namehashent = &namehashtab[idx]; + /* Find a slot for the locale name in the hash table. */ + namehashent = insert_name (ah, name, name_len, replace); + if (namehashent == NULL) /* Already exists and !REPLACE. */ + return 0; /* Determine whether we have to resize the file. */ if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size @@ -565,7 +634,7 @@ add_locale_to_archive (ah, name, data, replace) { /* The current archive is not large enough. */ enlarge_archive (ah, head); - return add_locale_to_archive (ah, name, data, replace); + return add_locale (ah, name, data, replace); } /* Add the locale data which is not yet in the archive. */ @@ -620,29 +689,46 @@ add_locale_to_archive (ah, name, data, replace) ++head->sumhash_used; } - - if (namehashent->locrec_offset == 0) + if (namehashent->name_offset == 0) { /* Add the name string. */ memcpy ((char *) ah->addr + head->string_offset + head->string_used, name, name_len + 1); namehashent->name_offset = head->string_offset + head->string_used; head->string_used += name_len + 1; + ++head->namehash_used; + } + if (namehashent->locrec_offset == 0) + { /* Allocate a name location record. */ namehashent->locrec_offset = (head->locrectab_offset + (head->locrectab_used++ * sizeof (struct locrecent))); - - namehashent->hashval = hval; - - ++head->namehash_used; + locrecent = (struct locrecent *) ((char *) ah->addr + + namehashent->locrec_offset); + locrecent->refs = 1; } + else + { + /* If there are other aliases pointing to this locrecent, + we still need a new one. If not, reuse the old one. */ + locrecent = (struct locrecent *) ((char *) ah->addr + + namehashent->locrec_offset); + if (locrecent->refs > 1) + { + --locrecent->refs; + namehashent->locrec_offset = (head->locrectab_offset + + (head->locrectab_used++ + * sizeof (struct locrecent))); + locrecent = (struct locrecent *) ((char *) ah->addr + + namehashent->locrec_offset); + locrecent->refs = 1; + } + } /* Fill in the table with the locations of the locale data. */ - locrecent = (struct locrecent *) ((char *) ah->addr - + namehashent->locrec_offset); for (cnt = 0; cnt < __LC_LAST; ++cnt) if (cnt != LC_ALL) { @@ -650,13 +736,196 @@ add_locale_to_archive (ah, name, data, replace) locrecent->record[cnt].len = data[cnt].size; } + return namehashent->locrec_offset; +} - /* Read the locale.alias file to see whether any matching record is - found. If an entry is available check whether it is already in - the archive. If this is the case check whether the new locale's - name is more specific than the one currently referred to by the - alias. */ +/* Check the content of the archive for duplicates. Add the content + of the files if necessary. Add all the names, possibly overwriting + old files. */ +int +add_locale_to_archive (ah, name, data, replace) + struct locarhandle *ah; + const char *name; + locale_data_t data; + bool replace; +{ + char *normalized_name = NULL; + uint32_t locrec_offset; + + /* First analyze the name to decide how to archive it. */ + const char *language; + const char *modifier; + const char *territory; + const char *codeset; + const char *normalized_codeset; + int mask = _nl_explode_name (strdupa (name), + &language, &modifier, &territory, + &codeset, &normalized_codeset); + + if (mask & XPG_NORM_CODESET) + /* This name contains a codeset in unnormalized form. + We will store it in the archive with a normalized name. */ + asprintf (&normalized_name, "%s%s%s.%s%s%s", + language, territory == NULL ? "" : "_", territory ?: "", + (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset, + modifier == NULL ? "" : "@", modifier ?: ""); + + /* This call does the main work. */ + locrec_offset = add_locale (ah, normalized_name ?: name, data, replace); + free (normalized_name); + if (locrec_offset == 0) + { + if (mask & XPG_NORM_CODESET) + free ((char *) normalized_codeset); + return -1; + } + + if ((mask & XPG_CODESET) == 0) + { + /* This name lacks a codeset, so determine the locale's codeset and + add an alias for its name with normalized codeset appended. */ + + const struct + { + unsigned int magic; + unsigned int nstrings; + unsigned int strindex[0]; + } *filedata = data[LC_CTYPE].addr; + codeset = (char *) filedata + + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]; + + normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset)); + mask |= XPG_NORM_CODESET; + + asprintf (&normalized_name, "%s%s%s.%s%s%s", + language, territory == NULL ? "" : "_", territory ?: "", + normalized_codeset, + modifier == NULL ? "" : "@", modifier ?: ""); + + add_alias (ah, normalized_name, replace, name, locrec_offset); + free (normalized_name); + } + + /* Now read the locale.alias files looking for lines whose + right hand side matches our name after normalization. */ + if (alias_file != NULL) + { + FILE *fp; + fp = fopen (alias_file, "r"); + if (fp == NULL) + error (1, errno, _("locale alias file `%s' not found"), + alias_file); + + /* No threads present. */ + __fsetlocking (fp, FSETLOCKING_BYCALLER); + + while (! feof_unlocked (fp)) + { + /* It is a reasonable approach to use a fix buffer here + because + a) we are only interested in the first two fields + b) these fields must be usable as file names and so must + not be that long */ + char buf[BUFSIZ]; + char *alias; + char *value; + char *cp; + + if (fgets_unlocked (buf, BUFSIZ, fp) == NULL) + /* EOF reached. */ + break; + + cp = buf; + /* Ignore leading white space. */ + while (isspace (cp[0]) && cp[0] != '\n') + ++cp; + + /* A leading '#' signals a comment line. */ + if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n') + { + alias = cp++; + while (cp[0] != '\0' && !isspace (cp[0])) + ++cp; + /* Terminate alias name. */ + if (cp[0] != '\0') + *cp++ = '\0'; + + /* Now look for the beginning of the value. */ + while (isspace (cp[0])) + ++cp; + + if (cp[0] != '\0') + { + value = cp++; + while (cp[0] != '\0' && !isspace (cp[0])) + ++cp; + /* Terminate value. */ + if (cp[0] == '\n') + { + /* This has to be done to make the following + test for the end of line possible. We are + looking for the terminating '\n' which do not + overwrite here. */ + *cp++ = '\0'; + *cp = '\n'; + } + else if (cp[0] != '\0') + *cp++ = '\0'; + + /* Does this alias refer to our locale? We will + normalize the right hand side and compare the + elements of the normalized form. */ + { + const char *rhs_language; + const char *rhs_modifier; + const char *rhs_territory; + const char *rhs_codeset; + const char *rhs_normalized_codeset; + int rhs_mask = _nl_explode_name (value, + &rhs_language, + &rhs_modifier, + &rhs_territory, + &rhs_codeset, + &rhs_normalized_codeset); + if (!strcmp (language, rhs_language) + && ((rhs_mask & XPG_CODESET) + /* He has a codeset, it must match normalized. */ + ? !strcmp ((mask & XPG_NORM_CODESET) + ? normalized_codeset : codeset, + (rhs_mask & XPG_NORM_CODESET) + ? rhs_normalized_codeset : rhs_codeset) + /* He has no codeset, we must also have none. */ + : (mask & XPG_CODESET) == 0) + /* Codeset (or lack thereof) matches. */ + && !strcmp (territory ?: "", rhs_territory ?: "") + && !strcmp (modifier ?: "", rhs_modifier ?: "")) + /* We have a winner. */ + add_alias (ah, alias, replace, + normalized_name ?: name, locrec_offset); + if (rhs_mask & XPG_NORM_CODESET) + free ((char *) rhs_normalized_codeset); + } + } + } + + /* Possibly not the whole line fits into the buffer. + Ignore the rest of the line. */ + while (strchr (cp, '\n') == NULL) + { + cp = buf; + if (fgets_unlocked (buf, BUFSIZ, fp) == NULL) + /* Make sure the inner loop will be left. The outer + loop will exit at the `feof' test. */ + *cp = '\n'; + } + } + + fclose (fp); + } + + if (mask & XPG_NORM_CODESET) + free ((char *) normalized_codeset); return 0; } @@ -903,7 +1172,6 @@ delete_locales_from_archive (nlist, list) /* Found the entry. Now mark it as removed by zero-ing the reference to the locale record. */ namehashtab[idx].locrec_offset = 0; - --head->namehash_used; break; } diff --git a/locale/setlocale.c b/locale/setlocale.c index 50963d152c..46af339eae 100644 --- a/locale/setlocale.c +++ b/locale/setlocale.c @@ -247,18 +247,22 @@ setlocale (int category, const char *locale) /* We perhaps really have to load some data. So we determine the path in which to look for the data now. The environment variable `LOCPATH' must only be used when the binary has no SUID or SGID - bit set. */ + bit set. If using the default path, we tell _nl_find_locale + by passing null and it can check the canonical locale archive. */ locale_path = NULL; locale_path_len = 0; locpath_var = getenv ("LOCPATH"); if (locpath_var != NULL && locpath_var[0] != '\0') - if (__argz_create_sep (locpath_var, ':', - &locale_path, &locale_path_len) != 0) - return NULL; + { + if (__argz_create_sep (locpath_var, ':', + &locale_path, &locale_path_len) != 0) + return NULL; - if (__argz_add_sep (&locale_path, &locale_path_len, LOCALEDIR, ':') != 0) - return NULL; + if (__argz_add_sep (&locale_path, &locale_path_len, + _nl_default_locale_path, ':') != 0) + return NULL; + } if (category == LC_ALL) { |