aboutsummaryrefslogtreecommitdiff
path: root/locale
diff options
context:
space:
mode:
Diffstat (limited to 'locale')
-rw-r--r--locale/C-address.c1
-rw-r--r--locale/C-collate.c1
-rw-r--r--locale/C-ctype.c1
-rw-r--r--locale/C-identification.c1
-rw-r--r--locale/C-measurement.c1
-rw-r--r--locale/C-messages.c1
-rw-r--r--locale/C-monetary.c1
-rw-r--r--locale/C-name.c1
-rw-r--r--locale/C-numeric.c1
-rw-r--r--locale/C-paper.c1
-rw-r--r--locale/C-telephone.c1
-rw-r--r--locale/C-time.c1
-rw-r--r--locale/Makefile4
-rw-r--r--locale/findlocale.c21
-rw-r--r--locale/hashval.h3
-rw-r--r--locale/loadarchive.c447
-rw-r--r--locale/loadlocale.c188
-rw-r--r--locale/localeinfo.h52
-rw-r--r--locale/newlocale.c17
-rw-r--r--locale/programs/localedef.c8
-rw-r--r--locale/programs/localedef.h1
-rw-r--r--locale/programs/locarchive.c416
-rw-r--r--locale/setlocale.c16
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)
{