about summary refs log tree commit diff
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)
downloadglibc-a891c7b0e014df5df027249f59cb444b87afdf30.tar.gz
glibc-a891c7b0e014df5df027249f59cb444b87afdf30.tar.xz
glibc-a891c7b0e014df5df027249f59cb444b87afdf30.zip
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)
 {