summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2007-04-16 22:55:50 +0000
committerJakub Jelinek <jakub@redhat.com>2007-04-16 22:55:50 +0000
commita891c7b0e014df5df027249f59cb444b87afdf30 (patch)
treea383ef12ee847c1c220f4ee41a4431b5d596dd0f
parenteae077f1c3de8572b3616b29cd53e72ca054b280 (diff)
New approach to including locale data in the rpm.
-rw-r--r--ChangeLog4
-rw-r--r--fedora/build-locale-archive.c509
-rw-r--r--fedora/glibc.spec.in35
-rw-r--r--locale/programs/locarchive.c4
4 files changed, 519 insertions, 33 deletions
diff --git a/ChangeLog b/ChangeLog
index 0eff0121e8..b54e3bc79c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2007-04-16 Jakub Jelinek <jakub@redhat.com>
+
+ * locale/programs/locarchive.c (add_alias, insert_name): Remove static.
+
2007-03-23 Jakub Jelinek <jakub@redhat.com>
* scripts/check-local-headers.sh: Filter out sys/capability.h.
diff --git a/fedora/build-locale-archive.c b/fedora/build-locale-archive.c
index a35171736e..ef0ac91ba7 100644
--- a/fedora/build-locale-archive.c
+++ b/fedora/build-locale-archive.c
@@ -1,23 +1,39 @@
#define _GNU_SOURCE
-#include <stdlib.h>
-#include <unistd.h>
+#include <assert.h>
#include <dirent.h>
#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdarg.h>
#include <stdbool.h>
-#include <sys/stat.h>
#include <stdio.h>
-#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "../locale/hashval.h"
+#define __LC_LAST 13
+#include "../locale/locarchive.h"
+#include "../crypt/md5.h"
const char *alias_file = DATADIR "/locale/locale.alias";
const char *locar_file = PREFIX "/lib/locale/locale-archive";
+const char *tmpl_file = PREFIX "/lib/locale/locale-archive.tmpl";
const char *loc_path = PREFIX "/lib/locale/";
int be_quiet = 1;
int verbose = 0;
int max_locarchive_open_retry = 10;
const char *output_prefix;
+static const char *locnames[] =
+ {
+#define DEFINE_CATEGORY(category, category_name, items, a) \
+ [category] = category_name,
+#include "../locale/categories.def"
+#undef DEFINE_CATEGORY
+ };
+
static int
is_prime (unsigned long candidate)
{
@@ -51,6 +67,7 @@ next_prime (unsigned long seed)
void *
xmalloc (size_t size)
{
+ (void) size;
exit (255);
}
@@ -72,7 +89,446 @@ error (int status, int errnum, const char *message, ...)
exit (errnum == EROFS ? 0 : status);
}
-extern int add_locales_to_archive (size_t nlist, char *list[], bool replace);
+static void
+open_tmpl_archive (struct locarhandle *ah)
+{
+ struct stat64 st;
+ int fd;
+ struct locarhead head;
+ const char *archivefname = tmpl_file;
+
+ /* Open the archive. We must have exclusive write access. */
+ fd = open64 (archivefname, O_RDONLY);
+ if (fd == -1)
+ error (EXIT_FAILURE, errno, "cannot open locale archive template file \"%s\"",
+ archivefname);
+
+ if (fstat64 (fd, &st) < 0)
+ error (EXIT_FAILURE, errno, "cannot stat locale archive template file \"%s\"",
+ archivefname);
+
+ /* Read the header. */
+ if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
+ error (EXIT_FAILURE, errno, "cannot read archive header");
+
+ ah->fd = fd;
+ ah->len = (head.sumhash_offset
+ + head.sumhash_size * sizeof (struct sumhashent));
+ if (ah->len > st.st_size)
+ error (EXIT_FAILURE, 0, "locale archite template file truncated");
+ ah->len = st.st_size;
+
+ /* Now we know how large the administrative information part is.
+ Map all of it. */
+ ah->addr = mmap64 (NULL, ah->len, PROT_READ, MAP_SHARED, fd, 0);
+ if (ah->addr == MAP_FAILED)
+ error (EXIT_FAILURE, errno, "cannot map archive header");
+}
+
+/* Open the locale archive. */
+extern void open_archive (struct locarhandle *ah, bool readonly);
+
+/* Close the locale archive. */
+extern void close_archive (struct locarhandle *ah);
+
+/* Add given locale data to the archive. */
+extern int add_locale_to_archive (struct locarhandle *ah, const char *name,
+ locale_data_t data, bool replace);
+
+extern void add_alias (struct locarhandle *ah, const char *alias,
+ bool replace, const char *oldname,
+ uint32_t *locrec_offset_p);
+
+extern struct namehashent *
+insert_name (struct locarhandle *ah,
+ const char *name, size_t name_len, bool replace);
+
+struct nameent
+{
+ char *name;
+ struct locrecent *locrec;
+};
+
+struct dataent
+{
+ const unsigned char *sum;
+ uint32_t file_offset;
+};
+
+static int
+nameentcmp (const void *a, const void *b)
+{
+ struct locrecent *la = ((const struct nameent *) a)->locrec;
+ struct locrecent *lb = ((const struct nameent *) b)->locrec;
+ uint32_t start_a = -1, end_a = 0;
+ uint32_t start_b = -1, end_b = 0;
+ int cnt;
+
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (cnt != LC_ALL)
+ {
+ if (la->record[cnt].offset < start_a)
+ start_a = la->record[cnt].offset;
+ if (la->record[cnt].offset + la->record[cnt].len > end_a)
+ end_a = la->record[cnt].offset + la->record[cnt].len;
+ }
+ assert (start_a != (uint32_t)-1);
+ assert (end_a != 0);
+
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (cnt != LC_ALL)
+ {
+ if (lb->record[cnt].offset < start_b)
+ start_b = lb->record[cnt].offset;
+ if (lb->record[cnt].offset + lb->record[cnt].len > end_b)
+ end_b = lb->record[cnt].offset + lb->record[cnt].len;
+ }
+ assert (start_b != (uint32_t)-1);
+ assert (end_b != 0);
+
+ if (start_a != start_b)
+ return (int)start_a - (int)start_b;
+ return (int)end_a - (int)end_b;
+}
+
+static int
+dataentcmp (const void *a, const void *b)
+{
+ if (((const struct dataent *) a)->file_offset
+ < ((const struct dataent *) b)->file_offset)
+ return -1;
+
+ if (((const struct dataent *) a)->file_offset
+ > ((const struct dataent *) b)->file_offset)
+ return 1;
+
+ return 0;
+}
+
+static int
+sumsearchfn (const void *key, const void *ent)
+{
+ uint32_t keyn = *(uint32_t *)key;
+ uint32_t entn = ((struct dataent *)ent)->file_offset;
+
+ if (keyn < entn)
+ return -1;
+ if (keyn > entn)
+ return 1;
+ return 0;
+}
+
+static void
+compute_data (struct locarhandle *ah, struct nameent *name, size_t sumused,
+ struct dataent *files, locale_data_t data)
+{
+ int cnt;
+ struct locrecent *locrec = name->locrec;
+ struct dataent *file;
+ data[LC_ALL].addr = ((char *) ah->addr) + locrec->record[LC_ALL].offset;
+ data[LC_ALL].size = locrec->record[LC_ALL].len;
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (cnt != LC_ALL)
+ {
+ data[cnt].addr = ((char *) ah->addr) + locrec->record[cnt].offset;
+ data[cnt].size = locrec->record[cnt].len;
+ if (data[cnt].addr >= data[LC_ALL].addr
+ && data[cnt].addr + data[cnt].size
+ <= data[LC_ALL].addr + data[LC_ALL].size)
+ __md5_buffer (data[cnt].addr, data[cnt].size, data[cnt].sum);
+ else
+ {
+ file = bsearch (&locrec->record[cnt].offset, files, sumused,
+ sizeof (*files), sumsearchfn);
+ if (file == NULL)
+ error (EXIT_FAILURE, 0, "inconsistent template file");
+ memcpy (data[cnt].sum, file->sum, sizeof (data[cnt].sum));
+ }
+ }
+}
+
+static int
+fill_archive (struct locarhandle *tmpl_ah, size_t nlist, char *list[],
+ const char *primary)
+{
+ struct locarhandle ah;
+ struct locarhead *head;
+ int result = 0;
+ struct nameent *names;
+ struct namehashent *namehashtab;
+ size_t cnt, used;
+ struct dataent *files;
+ struct sumhashent *sumhashtab;
+ size_t sumused;
+ struct locrecent *primary_locrec = NULL;
+ struct nameent *primary_nameent = NULL;
+
+ head = tmpl_ah->addr;
+ names = (struct nameent *) malloc (head->namehash_used
+ * sizeof (struct nameent));
+ files = (struct dataent *) malloc (head->sumhash_used
+ * sizeof (struct dataent));
+ if (names == NULL || files == NULL)
+ error (EXIT_FAILURE, errno, "could not allocate tables");
+
+ namehashtab = (struct namehashent *) ((char *) tmpl_ah->addr
+ + head->namehash_offset);
+ sumhashtab = (struct sumhashent *) ((char *) tmpl_ah->addr
+ + head->sumhash_offset);
+
+ for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
+ if (namehashtab[cnt].locrec_offset != 0)
+ {
+ assert (used < head->namehash_used);
+ names[used].name = tmpl_ah->addr + namehashtab[cnt].name_offset;
+ names[used++].locrec
+ = (struct locrecent *) ((char *) tmpl_ah->addr +
+ namehashtab[cnt].locrec_offset);
+ }
+
+ /* Sort the names. */
+ qsort (names, used, sizeof (struct nameent), nameentcmp);
+
+ for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
+ if (sumhashtab[cnt].file_offset != 0)
+ {
+ assert (sumused < head->sumhash_used);
+ files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
+ files[sumused++].file_offset = sumhashtab[cnt].file_offset;
+ }
+
+ /* Sort by file locations. */
+ qsort (files, sumused, sizeof (struct dataent), dataentcmp);
+
+ /* Open the archive. This call never returns if we cannot
+ successfully open the archive. */
+ open_archive (&ah, false);
+
+ if (primary != NULL)
+ {
+ for (cnt = 0; cnt < used; ++cnt)
+ if (strcmp (names[cnt].name, primary) == 0)
+ break;
+ if (cnt < used)
+ {
+ locale_data_t data;
+
+ compute_data (tmpl_ah, &names[cnt], sumused, files, data);
+ result |= add_locale_to_archive (&ah, primary, data, 0);
+ primary_locrec = names[cnt].locrec;
+ primary_nameent = &names[cnt];
+ }
+ }
+
+ for (cnt = 0; cnt < used; ++cnt)
+ if (&names[cnt] == primary_nameent)
+ continue;
+ else if ((cnt > 0 && names[cnt - 1].locrec == names[cnt].locrec)
+ || names[cnt].locrec == primary_locrec)
+ {
+ const char *oldname;
+ struct namehashent *namehashent;
+ uint32_t locrec_offset;
+
+ if (names[cnt].locrec == primary_locrec)
+ oldname = primary;
+ else
+ oldname = names[cnt - 1].name;
+ namehashent = insert_name (&ah, oldname, strlen (oldname), true);
+ assert (namehashent->name_offset != 0);
+ assert (namehashent->locrec_offset != 0);
+ locrec_offset = namehashent->locrec_offset;
+ add_alias (&ah, names[cnt].name, 0, oldname, &locrec_offset);
+ }
+ else
+ {
+ locale_data_t data;
+
+ compute_data (tmpl_ah, &names[cnt], sumused, files, data);
+ result |= add_locale_to_archive (&ah, names[cnt].name, data, 0);
+ }
+
+ while (nlist-- > 0)
+ {
+ const char *fname = *list++;
+ size_t fnamelen = strlen (fname);
+ struct stat64 st;
+ DIR *dirp;
+ struct dirent64 *d;
+ int seen;
+ locale_data_t data;
+ int cnt;
+
+ /* First see whether this really is a directory and whether it
+ contains all the require locale category files. */
+ if (stat64 (fname, &st) < 0)
+ {
+ error (0, 0, "stat of \"%s\" failed: %s: ignored", fname,
+ strerror (errno));
+ continue;
+ }
+ if (!S_ISDIR (st.st_mode))
+ {
+ error (0, 0, "\"%s\" is no directory; ignored", fname);
+ continue;
+ }
+
+ dirp = opendir (fname);
+ if (dirp == NULL)
+ {
+ error (0, 0, "cannot open directory \"%s\": %s: ignored",
+ fname, strerror (errno));
+ continue;
+ }
+
+ seen = 0;
+ while ((d = readdir64 (dirp)) != NULL)
+ {
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (cnt != LC_ALL)
+ if (strcmp (d->d_name, locnames[cnt]) == 0)
+ {
+ unsigned char d_type;
+
+ /* We have an object of the required name. If it's
+ a directory we have to look at a file with the
+ prefix "SYS_". Otherwise we have found what we
+ are looking for. */
+#ifdef _DIRENT_HAVE_D_TYPE
+ d_type = d->d_type;
+
+ if (d_type != DT_REG)
+#endif
+ {
+ char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
+
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d_type == DT_UNKNOWN)
+#endif
+ {
+ strcpy (stpcpy (stpcpy (fullname, fname), "/"),
+ d->d_name);
+
+ if (stat64 (fullname, &st) == -1)
+ /* We cannot stat the file, ignore it. */
+ break;
+
+ d_type = IFTODT (st.st_mode);
+ }
+
+ if (d_type == DT_DIR)
+ {
+ /* We have to do more tests. The file is a
+ directory and it therefore must contain a
+ regular file with the same name except a
+ "SYS_" prefix. */
+ char *t = stpcpy (stpcpy (fullname, fname), "/");
+ strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
+ d->d_name);
+
+ if (stat64 (fullname, &st) == -1)
+ /* There is no SYS_* file or we cannot
+ access it. */
+ break;
+
+ d_type = IFTODT (st.st_mode);
+ }
+ }
+
+ /* If we found a regular file (eventually after
+ following a symlink) we are successful. */
+ if (d_type == DT_REG)
+ ++seen;
+ break;
+ }
+ }
+
+ closedir (dirp);
+
+ if (seen != __LC_LAST - 1)
+ {
+ /* We don't have all locale category files. Ignore the name. */
+ error (0, 0, "incomplete set of locale files in \"%s\"",
+ fname);
+ continue;
+ }
+
+ /* Add the files to the archive. To do this we first compute
+ sizes and the MD5 sums of all the files. */
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (cnt != LC_ALL)
+ {
+ char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
+ int fd;
+
+ strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
+ fd = open64 (fullname, O_RDONLY);
+ if (fd == -1 || fstat64 (fd, &st) == -1)
+ {
+ /* Cannot read the file. */
+ if (fd != -1)
+ close (fd);
+ break;
+ }
+
+ if (S_ISDIR (st.st_mode))
+ {
+ char *t;
+ close (fd);
+ t = stpcpy (stpcpy (fullname, fname), "/");
+ strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
+ locnames[cnt]);
+
+ fd = open64 (fullname, O_RDONLY);
+ if (fd == -1 || fstat64 (fd, &st) == -1
+ || !S_ISREG (st.st_mode))
+ {
+ if (fd != -1)
+ close (fd);
+ break;
+ }
+ }
+
+ /* Map the file. */
+ data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
+ fd, 0);
+ if (data[cnt].addr == MAP_FAILED)
+ {
+ /* Cannot map it. */
+ close (fd);
+ break;
+ }
+
+ data[cnt].size = st.st_size;
+ __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
+
+ /* We don't need the file descriptor anymore. */
+ close (fd);
+ }
+
+ if (cnt != __LC_LAST)
+ {
+ while (cnt-- > 0)
+ if (cnt != LC_ALL)
+ munmap (data[cnt].addr, data[cnt].size);
+
+ error (0, 0, "cannot read all files in \"%s\": ignored", fname);
+
+ continue;
+ }
+
+ result |= add_locale_to_archive (&ah, basename (fname), data, 0);
+
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (cnt != LC_ALL)
+ munmap (data[cnt].addr, data[cnt].size);
+ }
+
+ /* We are done. */
+ close_archive (&ah);
+
+ return result;
+}
int main ()
{
@@ -82,12 +538,16 @@ int main ()
struct stat64 st;
char *list[16384], *primary;
unsigned int cnt = 0;
+ struct locarhandle tmpl_ah;
+ size_t loc_path_len = strlen (loc_path);
- unlink (locar_file);
dirp = opendir (loc_path);
if (dirp == NULL)
error (EXIT_FAILURE, errno, "cannot open directory \"%s\"", loc_path);
+ open_tmpl_archive (&tmpl_ah);
+
+ unlink (locar_file);
primary = getenv ("LC_ALL");
if (primary == NULL)
primary = getenv ("LANG");
@@ -113,30 +573,25 @@ int main ()
primary = ptr;
}
else
- primary = ".....";
- }
- strcpy (stpcpy (path, loc_path), primary);
- if (stat64 (path, &st) >= 0 && S_ISDIR (st.st_mode))
- {
- list[cnt] = strdup (path);
- if (list[cnt] == NULL)
- error (0, errno, "cannot add file to list \"%s\"", path);
- else
- cnt++;
+ primary = NULL;
}
- if (cnt == 0)
- primary = NULL;
}
+ memcpy (path, loc_path, loc_path_len);
+
while ((d = readdir64 (dirp)) != NULL)
{
if (strcmp (d->d_name, ".") == 0 || strcmp (d->d_name, "..") == 0)
continue;
- if (primary && strcmp (d->d_name, primary) == 0)
- continue;
+ size_t d_name_len = strlen (d->d_name);
+ if (loc_path_len + d_name_len + 1 > sizeof (path))
+ {
+ error (0, 0, "too long filename \"%s\"", d->d_name);
+ continue;
+ }
- strcpy (stpcpy (path, loc_path), d->d_name);
+ memcpy (path + loc_path_len, d->d_name, d_name_len + 1);
if (stat64 (path, &st) < 0)
{
error (0, errno, "cannot stat \"%s\"", path);
@@ -152,10 +607,18 @@ int main ()
error (0, errno, "cannot add file to list \"%s\"", path);
continue;
}
+ if (primary != NULL && cnt > 0 && strcmp (primary, d->d_name) == 0)
+ {
+ char *p = list[0];
+ list[0] = list[cnt];
+ list[cnt] = p;
+ }
cnt++;
}
closedir (dirp);
- add_locales_to_archive (cnt, list, 0);
+ fill_archive (&tmpl_ah, cnt, list, primary);
+ close_archive (&tmpl_ah);
+ unlink (tmpl_file);
char *argv[] = { "/usr/sbin/tzdata-update", NULL };
execve (argv[0], (char *const *)argv, (char *const *)&argv[1]);
exit (0);
diff --git a/fedora/glibc.spec.in b/fedora/glibc.spec.in
index 779c17e370..a8b70f8a0b 100644
--- a/fedora/glibc.spec.in
+++ b/fedora/glibc.spec.in
@@ -1,4 +1,4 @@
-%define glibcrelease 20
+%define glibcrelease 21
%define auxarches i586 i686 athlon sparcv9 alphaev6
%define xenarches i686 athlon
%ifarch %{xenarches}
@@ -969,11 +969,6 @@ popd
cd ..
%endif
-# compatibility hack: this locale has vanished from glibc, but some other
-# programs are still using it. Normally we would handle it in the %pre
-# section but with glibc that is simply not an option
-mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/locale/ru_RU/LC_MESSAGES
-
# Remove the files we don't want to distribute
rm -f $RPM_BUILD_ROOT%{_prefix}/%{_lib}/libNoVersion*
rm -f $RPM_BUILD_ROOT/%{_lib}/libNoVersion*
@@ -1051,7 +1046,19 @@ rm -f $RPM_BUILD_ROOT%{_prefix}/include/rpcsvc/rquota.[hx]
# Hardlink identical locale files together
%ifnarch %{auxarches}
gcc -O2 -o build-%{nptl_target_cpu}-linuxnptl/hardlink fedora/hardlink.c
-build-%{nptl_target_cpu}-linuxnptl/hardlink -vc $RPM_BUILD_ROOT%{_prefix}/lib/locale
+rm ${RPM_BUILD_ROOT}${_prefix}/lib/locale/locale-archive || :
+olddir=`pwd`
+pushd ${RPM_BUILD_ROOT}${_prefix}/lib/locale
+# Intentionally we do not pass --alias-file=, aliases will be added
+# by build-locale-archive.
+$olddir/build-%{nptl_target_cpu}-linuxnptl/elf/ld.so \
+ --library-path $olddir/build-%{nptl_target_cpu}-linuxnptl/ \
+ $olddir/build-%{nptl_target_cpu}-linuxnptl/locale/localedef \
+ --prefix ${RPM_BUILD_ROOT} --add-to-archive \
+ *_*
+rm -rf *_*
+popd
+#build-%{nptl_target_cpu}-linuxnptl/hardlink -vc $RPM_BUILD_ROOT%{_prefix}/lib/locale
%endif
rm -f ${RPM_BUILD_ROOT}/%{_lib}/libnss1-*
@@ -1125,7 +1132,7 @@ grep -v '%{_prefix}/%{_lib}/lib.*\.a' < rpm.filelist.full |
grep -v 'nscd' > rpm.filelist
grep '%{_prefix}/bin' < rpm.filelist >> common.filelist
-grep '%{_prefix}/lib/locale' < rpm.filelist >> common.filelist
+grep '%{_prefix}/lib/locale' < rpm.filelist | grep -v /locale-archive.tmpl >> common.filelist
grep '%{_prefix}/libexec/pt_chown' < rpm.filelist >> common.filelist
grep '%{_prefix}/sbin/[^gi]' < rpm.filelist >> common.filelist
grep '%{_prefix}/share' < rpm.filelist \
@@ -1495,6 +1502,7 @@ rm -f *.filelist*
%ifnarch %{auxarches}
%files -f common.filelist common
%defattr(-,root,root)
+%attr(0644,root,root) %config(missingok) %{_prefix}/lib/locale/locale-archive.tmpl
%attr(0644,root,root) %verify(not md5 size mtime mode) %ghost %config(missingok,noreplace) %{_prefix}/lib/locale/locale-archive
%dir %attr(755,root,root) /etc/default
%verify(not md5 size mtime) %config(noreplace) /etc/default/nss
@@ -1549,10 +1557,21 @@ rm -f *.filelist*
%endif
%changelog
+* Mon Apr 16 2007 Jakub Jelinek <jakub@redhat.com> 2.5.90-21
+- don't include individual locale files in glibc-common,
+ rather include prepared locale-archive template and let
+ build-locale-archive create locale-archive from the template
+ and any user supplied /usr/lib/locale/*_* directories,
+ then unlink the locale-archive template - this should save
+ > 80MB of glibc-common occupied disk space
+
* Sat Mar 31 2007 Jakub Jelinek <jakub@redhat.com> 2.5.90-20
- assorted NIS+ speedups (#223467)
- fix HAVE_LIBCAP configure detection (#178934)
- remove %{_prefix}/sbin/rpcinfo from glibc-common (#228894)
+- nexttoward*/nextafter* fixes (BZ#3306)
+- feholdexcept/feupdateenv fixes (BZ#3427)
+- speed up fnmatch with two or more * in the pattern
* Sat Mar 17 2007 Jakub Jelinek <jakub@redhat.com> 2.5.90-19
- fix power6 libm compat symbols on ppc32 (#232633)
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c
index 5c0d5f18ce..201700dcb0 100644
--- a/locale/programs/locarchive.c
+++ b/locale/programs/locarchive.c
@@ -521,7 +521,7 @@ close_archive (struct locarhandle *ah)
#include "../../intl/explodename.c"
#include "../../intl/l10nflist.c"
-static struct namehashent *
+struct namehashent *
insert_name (struct locarhandle *ah,
const char *name, size_t name_len, bool replace)
{
@@ -579,7 +579,7 @@ insert_name (struct locarhandle *ah,
return &namehashtab[idx];
}
-static void
+void
add_alias (struct locarhandle *ah, const char *alias, bool replace,
const char *oldname, uint32_t *locrec_offset_p)
{