diff --git a/ChangeLog b/ChangeLog index 15af2f314b..9464fc0610 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,91 @@ +2002-08-09 Roland McGrath + + * 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 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 + + * 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. + 2002-08-08 Ulrich Drepper * sysdeps/unix/sysv/linux/sigwait.c: New file. diff --git a/inet/rexec.c b/inet/rexec.c index e8ecf79c54..8f6190a834 100644 --- a/inet/rexec.c +++ b/inet/rexec.c @@ -177,6 +177,7 @@ bad: freeaddrinfo(res0); return (-1); } +libc_hidden_def (rexec_af) int rexec(ahost, rport, name, pass, cmd, fd2p) 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 +# 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 #include #include +#include #include #include #include @@ -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))); + 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. */ - namehashent->hashval = hval; - - ++head->namehash_used; + 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) { diff --git a/misc/err.c b/misc/err.c index 05406276d0..f04b2dc7a3 100644 --- a/misc/err.c +++ b/misc/err.c @@ -108,7 +108,7 @@ vwarnx (const char *format, __gnuc_va_list ap) } funlockfile (stderr); } -libc_hidden_def (warnx) +libc_hidden_def (vwarnx) void vwarn (const char *format, __gnuc_va_list ap) @@ -142,7 +142,7 @@ vwarn (const char *format, __gnuc_va_list ap) } funlockfile (stderr); } -libc_hidden_def (warnx) +libc_hidden_def (vwarnx) void diff --git a/signal/allocrtsig.c b/signal/allocrtsig.c index e8b09e4596..1d5c1f3824 100644 --- a/signal/allocrtsig.c +++ b/signal/allocrtsig.c @@ -1,5 +1,5 @@ /* Handle real-time signal allocation. - Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc. + Copyright (C) 1997,98,99,2002 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 1997. @@ -62,6 +62,7 @@ __libc_current_sigrtmin (void) #endif return current_rtmin; } +libc_hidden_def (__libc_current_sigrtmin) /* Return number of available real-time signal with lowest priority. */ int @@ -73,6 +74,7 @@ __libc_current_sigrtmax (void) #endif return current_rtmax; } +libc_hidden_def (__libc_current_sigrtmax) /* Allocate real-time signal with highest/lowest available priority. Please note that we don't use a lock since we assume diff --git a/sysdeps/generic/morecore.c b/sysdeps/generic/morecore.c index 53969f59bf..11a12e6d2b 100644 --- a/sysdeps/generic/morecore.c +++ b/sysdeps/generic/morecore.c @@ -51,3 +51,4 @@ __default_morecore (increment) return NULL; return result; } +libc_hidden_def (__default_morecore)