about summary refs log tree commit diff
path: root/locale
diff options
context:
space:
mode:
Diffstat (limited to 'locale')
-rw-r--r--locale/Makefile9
-rw-r--r--locale/hashval.h41
-rw-r--r--locale/locarchive.h97
-rw-r--r--locale/programs/ld-address.c2
-rw-r--r--locale/programs/ld-collate.c4
-rw-r--r--locale/programs/ld-ctype.c3
-rw-r--r--locale/programs/ld-identification.c3
-rw-r--r--locale/programs/ld-measurement.c2
-rw-r--r--locale/programs/ld-messages.c2
-rw-r--r--locale/programs/ld-monetary.c2
-rw-r--r--locale/programs/ld-name.c2
-rw-r--r--locale/programs/ld-numeric.c2
-rw-r--r--locale/programs/ld-paper.c2
-rw-r--r--locale/programs/ld-telephone.c2
-rw-r--r--locale/programs/ld-time.c2
-rw-r--r--locale/programs/localedef.c95
-rw-r--r--locale/programs/localedef.h28
-rw-r--r--locale/programs/locarchive.c931
-rw-r--r--locale/programs/locfile.c61
-rw-r--r--locale/programs/locfile.h8
-rw-r--r--locale/programs/simple-hash.c30
-rw-r--r--locale/programs/simple-hash.h4
22 files changed, 1268 insertions, 64 deletions
diff --git a/locale/Makefile b/locale/Makefile
index 03ed1b66fa..7a773f104f 100644
--- a/locale/Makefile
+++ b/locale/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1991, 92, 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+# Copyright (C) 1991,92,1995-1999,2000,2001,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
@@ -26,6 +26,7 @@ distribute	= localeinfo.h categories.def iso-639.def iso-3166.def \
 		  iso-4217.def weight.h weightwc.h strlen-hash.h elem-hash.h \
 		  indigits.h indigitswc.h outdigits.h outdigitswc.h \
 		  coll-lookup.h C-translit.h.in C-translit.h gen-translit.pl \
+		  locarchive.h \
 		  $(addprefix programs/, \
 			      locale.c localedef.c \
 			      $(localedef-modules:=.c) $(locale-modules:=.c) \
@@ -33,7 +34,7 @@ distribute	= localeinfo.h categories.def iso-639.def iso-3166.def \
 			      charmap-kw.gperf charmap-kw.h locfile-token.h \
 			      locfile-kw.gperf locfile-kw.h linereader.h \
 			      locfile.h charmap.h repertoire.h localedef.h \
-			      3level.h charmap-dir.h)
+			      3level.h charmap-dir.h locarchive.c)
 routines	= setlocale findlocale loadlocale localeconv nl_langinfo \
 		  nl_langinfo_l mb_cur_max \
 		  newlocale duplocale freelocale
@@ -54,12 +55,12 @@ extra-libs-others = $(extra-libs)
 libBrokenLocale-routines = broken_cur_max
 
 subdir-dirs	= programs
-vpath %.c programs
+vpath %.c programs ../crypt
 vpath %.h programs
 vpath %.gperf programs
 
 localedef-modules	:= $(categories:%=ld-%) charmap linereader locfile \
-			   repertoire
+			   repertoire md5 locarchive
 locale-modules		:= locale-spec
 lib-modules		:= charmap-dir simple-hash xmalloc xstrdup
 
diff --git a/locale/hashval.h b/locale/hashval.h
new file mode 100644
index 0000000000..15ec1244cf
--- /dev/null
+++ b/locale/hashval.h
@@ -0,0 +1,41 @@
+/* Implement simple hashing table with string based keys.
+   Copyright (C) 1994-1997, 2000, 2001, 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, October 1994.
+
+   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.  */
+
+unsigned long
+compute_hashval (key, keylen)
+     const void *key;
+     size_t keylen;
+{
+  size_t cnt;
+  unsigned long int hval;
+
+  /* Compute the hash value for the given string.  The algorithm
+     is taken from [Aho,Sethi,Ullman], modified to reduce the number of
+     collisions for short strings with very varied bit patterns.
+     See http://www.clisp.org/haible/hashfunc.html.  */
+  cnt = 0;
+  hval = keylen;
+  while (cnt < keylen)
+    {
+      hval = (hval << 9) | (hval >> (LONGBITS - 9));
+      hval += (unsigned long int) *(((char *) key) + cnt++);
+    }
+  return hval != 0 ? hval : ~((unsigned long) 0);
+}
diff --git a/locale/locarchive.h b/locale/locarchive.h
new file mode 100644
index 0000000000..2005989598
--- /dev/null
+++ b/locale/locarchive.h
@@ -0,0 +1,97 @@
+/* Definitions for locale archive handling.
+   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.  */
+
+#ifndef _LOCARCHIVE_H
+#define _LOCARCHIVE_H 1
+
+#include <stdint.h>
+
+
+#define AR_MAGIC 0xde020109
+
+struct locarhead
+{
+  uint32_t magic;
+  /* Serial number.  */
+  uint32_t serial;
+  /* Name hash table.  */
+  uint32_t namehash_offset;
+  uint32_t namehash_used;
+  uint32_t namehash_size;
+  /* String table.  */
+  uint32_t string_offset;
+  uint32_t string_used;
+  uint32_t string_size;
+  /* Table with locale records.  */
+  uint32_t locrectab_offset;
+  uint32_t locrectab_used;
+  uint32_t locrectab_size;
+  /* MD5 sum hash table.  */
+  uint32_t sumhash_offset;
+  uint32_t sumhash_used;
+  uint32_t sumhash_size;
+};
+
+
+struct namehashent
+{
+  /* Hash value of the name.  */
+  uint32_t hashval;
+  /* Offset of the name in the string table.  */
+  uint32_t name_offset;
+  /* Offset of the locale record.  */
+  uint32_t locrec_offset;
+};
+
+
+struct sumhashent
+{
+  /* MD5 sum.  */
+  char sum[16];
+  /* Offset of the file in the archive.  */
+  uint32_t file_offset;
+};
+
+struct locrecent
+{
+  struct
+  {
+    uint32_t offset;
+    uint32_t len;
+  } record[__LC_LAST];
+};
+
+
+struct locarhandle
+{
+  int fd;
+  void *addr;
+  size_t len;
+};
+
+
+/* In memory data for the locales with their checksums.  */
+typedef struct
+{
+  off64_t size;
+  void *addr;
+  char sum[16];
+} locale_data_t[__LC_LAST];
+
+#endif	/* locarchive.h */
diff --git a/locale/programs/ld-address.c b/locale/programs/ld-address.c
index 52b91cfc28..5c8efaa282 100644
--- a/locale/programs/ld-address.c
+++ b/locale/programs/ld-address.c
@@ -422,7 +422,7 @@ address_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   assert (cnt == 3 + _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS));
 
-  write_locale_data (output_path, "LC_ADDRESS",
+  write_locale_data (output_path, LC_ADDRESS, "LC_ADDRESS",
 		     3 + _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS), iov);
 }
 
diff --git a/locale/programs/ld-collate.c b/locale/programs/ld-collate.c
index 191194799d..ca58266839 100644
--- a/locale/programs/ld-collate.c
+++ b/locale/programs/ld-collate.c
@@ -1987,7 +1987,7 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
       assert (cnt == _NL_ITEM_INDEX (_NL_NUM_LC_COLLATE));
 
-      write_locale_data (output_path, "LC_COLLATE", 2 + cnt, iov);
+      write_locale_data (output_path, LC_COLLATE, "LC_COLLATE", 2 + cnt, iov);
 
       return;
     }
@@ -2571,7 +2571,7 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   assert (cnt == _NL_ITEM_INDEX (_NL_NUM_LC_COLLATE));
 
-  write_locale_data (output_path, "LC_COLLATE", 2 + cnt, iov);
+  write_locale_data (output_path, LC_COLLATE, "LC_COLLATE", 2 + cnt, iov);
 
   obstack_free (&weightpool, NULL);
   obstack_free (&extrapool, NULL);
diff --git a/locale/programs/ld-ctype.c b/locale/programs/ld-ctype.c
index 1026133dc2..eed2e94732 100644
--- a/locale/programs/ld-ctype.c
+++ b/locale/programs/ld-ctype.c
@@ -1214,7 +1214,8 @@ ctype_output (struct localedef_t *locale, const struct charmap_t *charmap,
   assert (2 + elem + offset == (nelems + 2 * ctype->nr_charclass
 				+ ctype->map_collection_nr + 4 + 2));
 
-  write_locale_data (output_path, "LC_CTYPE", 2 + elem + offset, iov);
+  write_locale_data (output_path, LC_CTYPE, "LC_CTYPE", 2 + elem + offset,
+		     iov);
 }
 
 
diff --git a/locale/programs/ld-identification.c b/locale/programs/ld-identification.c
index 481e4e79c4..ae5ea6fb16 100644
--- a/locale/programs/ld-identification.c
+++ b/locale/programs/ld-identification.c
@@ -291,7 +291,8 @@ identification_output (struct localedef_t *locale,
   assert (cnt == (2 + _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION)
 		  + (__LC_LAST - 2)));
 
-  write_locale_data (output_path, "LC_IDENTIFICATION", cnt, iov);
+  write_locale_data (output_path, LC_IDENTIFICATION, "LC_IDENTIFICATION", cnt,
+		     iov);
 }
 
 
diff --git a/locale/programs/ld-measurement.c b/locale/programs/ld-measurement.c
index 75219bdf9f..2aa75b47dc 100644
--- a/locale/programs/ld-measurement.c
+++ b/locale/programs/ld-measurement.c
@@ -150,7 +150,7 @@ measurement_output (struct localedef_t *locale,
 
   assert (cnt == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MEASUREMENT));
 
-  write_locale_data (output_path, "LC_MEASUREMENT",
+  write_locale_data (output_path, LC_MEASUREMENT, "LC_MEASUREMENT",
 		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_MEASUREMENT), iov);
 }
 
diff --git a/locale/programs/ld-messages.c b/locale/programs/ld-messages.c
index fadf82757f..d5cc393e4b 100644
--- a/locale/programs/ld-messages.c
+++ b/locale/programs/ld-messages.c
@@ -226,7 +226,7 @@ messages_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   assert (cnt + 1 == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES));
 
-  write_locale_data (output_path, "LC_MESSAGES",
+  write_locale_data (output_path, LC_MESSAGES, "LC_MESSAGES",
 		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES), iov);
 }
 
diff --git a/locale/programs/ld-monetary.c b/locale/programs/ld-monetary.c
index 4bb7c0bba4..4c36c505ba 100644
--- a/locale/programs/ld-monetary.c
+++ b/locale/programs/ld-monetary.c
@@ -612,7 +612,7 @@ monetary_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   assert (cnt == 3 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY));
 
-  write_locale_data (output_path, "LC_MONETARY",
+  write_locale_data (output_path, LC_MONETARY, "LC_MONETARY",
 		     3 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY), iov);
 }
 
diff --git a/locale/programs/ld-name.c b/locale/programs/ld-name.c
index 11e0ace7d6..80b42f7048 100644
--- a/locale/programs/ld-name.c
+++ b/locale/programs/ld-name.c
@@ -210,7 +210,7 @@ name_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   assert (cnt == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_NAME));
 
-  write_locale_data (output_path, "LC_NAME",
+  write_locale_data (output_path, LC_NAME, "LC_NAME",
 		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_NAME), iov);
 }
 
diff --git a/locale/programs/ld-numeric.c b/locale/programs/ld-numeric.c
index 6e385f2be6..594c0c8c1b 100644
--- a/locale/programs/ld-numeric.c
+++ b/locale/programs/ld-numeric.c
@@ -187,7 +187,7 @@ numeric_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   assert (cnt + 1 == 3 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC));
 
-  write_locale_data (output_path, "LC_NUMERIC",
+  write_locale_data (output_path, LC_NUMERIC, "LC_NUMERIC",
 		     3 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC), iov);
 }
 
diff --git a/locale/programs/ld-paper.c b/locale/programs/ld-paper.c
index 2dce9ca8da..2910954f04 100644
--- a/locale/programs/ld-paper.c
+++ b/locale/programs/ld-paper.c
@@ -154,7 +154,7 @@ paper_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   assert (cnt == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_PAPER));
 
-  write_locale_data (output_path, "LC_PAPER",
+  write_locale_data (output_path, LC_PAPER, "LC_PAPER",
 		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_PAPER), iov);
 }
 
diff --git a/locale/programs/ld-telephone.c b/locale/programs/ld-telephone.c
index 01dcf36008..5a60c016b0 100644
--- a/locale/programs/ld-telephone.c
+++ b/locale/programs/ld-telephone.c
@@ -218,7 +218,7 @@ telephone_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   assert (cnt == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_TELEPHONE));
 
-  write_locale_data (output_path, "LC_TELEPHONE",
+  write_locale_data (output_path, LC_TELEPHONE, "LC_TELEPHONE",
 		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_TELEPHONE), iov);
 }
 
diff --git a/locale/programs/ld-time.c b/locale/programs/ld-time.c
index 32d9dad971..2ff56b0034 100644
--- a/locale/programs/ld-time.c
+++ b/locale/programs/ld-time.c
@@ -906,7 +906,7 @@ time_output (struct localedef_t *locale, const struct charmap_t *charmap,
 		  + 2 + time->num_era * 10 - 1));
   assert (last_idx  == _NL_ITEM_INDEX (_NL_NUM_LC_TIME));
 
-  write_locale_data (output_path, "LC_TIME", 2 + cnt, iov);
+  write_locale_data (output_path, LC_TIME, "LC_TIME", 2 + cnt, iov);
 }
 
 
diff --git a/locale/programs/localedef.c b/locale/programs/localedef.c
index 3c159560e9..526f2025e7 100644
--- a/locale/programs/localedef.c
+++ b/locale/programs/localedef.c
@@ -27,6 +27,7 @@
 #include <libintl.h>
 #include <locale.h>
 #include <mcheck.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -56,7 +57,8 @@ int verbose;
 /* If not zero suppress warnings and information messages.  */
 int be_quiet;
 
-/* If not zero, produce old-style hash table instead of 3-level access tables.  */
+/* If not zero, produce old-style hash table instead of 3-level access
+   tables.  */
 int oldstyle_tables;
 
 /* If not zero force output even if warning were issued.  */
@@ -77,15 +79,38 @@ const char *repertoire_global;
 /* List of all locales.  */
 static struct localedef_t *locales;
 
+/* If true don't add locale data to archive.  */
+bool no_archive;
+
+/* If true add named locales to archive.  */
+static bool add_to_archive;
+
+/* If true delete named locales from archive.  */
+static bool delete_from_archive;
+
+/* If true replace archive content when adding.  */
+static bool replace_archive;
+
+/* If true list archive content.  */
+static bool list_archive;
+
+/* Maximum number of retries when opening the locale archive.  */
+int max_locarchive_open_retry = 10;
+
 
 /* Name and version of program.  */
 static void print_version (FILE *stream, struct argp_state *state);
 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
 
-#define OPT_POSIX 1
-#define OPT_QUIET 2
-#define OPT_OLDSTYLE 3
-#define OPT_PREFIX 4
+#define OPT_POSIX 301
+#define OPT_QUIET 302
+#define OPT_OLDSTYLE 303
+#define OPT_PREFIX 304
+#define OPT_NO_ARCHIVE 305
+#define OPT_ADD_TO_ARCHIVE 306
+#define OPT_REPLACE 307
+#define OPT_DELETE_FROM_ARCHIVE 308
+#define OPT_LIST_ARCHIVE 309
 
 /* Definitions of arguments for argp functions.  */
 static const struct argp_option options[] =
@@ -106,6 +131,15 @@ static const struct argp_option options[] =
   { "quiet", OPT_QUIET, NULL, 0,
     N_("Suppress warnings and information messages") },
   { "verbose", 'v', NULL, 0, N_("Print more messages") },
+  { NULL, 0, NULL, 0, N_("Archive control:") },
+  { "no-archive", OPT_NO_ARCHIVE, NULL, 0,
+    N_("Don't add new data to archive") },
+  { "add-to-archive", OPT_ADD_TO_ARCHIVE, NULL, 0,
+    N_("Add locales named by parameters to archive") },
+  { "replace", OPT_REPLACE, NULL, 0, N_("Replace existing archive content") },
+  { "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") },
   { NULL, 0, NULL, 0, NULL }
 };
 
@@ -113,7 +147,10 @@ static const struct argp_option options[] =
 static const char doc[] = N_("Compile locale specification");
 
 /* Strings for arguments in help texts.  */
-static const char args_doc[] = N_("NAME");
+static const char args_doc[] = N_("\
+NAME\n\
+[--add-to-archive|--delete-from-archive] FILE...\n\
+--list-archive [FILE]");
 
 /* Prototype for option handler.  */
 static error_t parse_opt (int key, char *arg, struct argp_state *state);
@@ -163,6 +200,15 @@ main (int argc, char *argv[])
   argp_err_exit_status = 4;
   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
 
+  /* Handle a few special cases.  */
+  if (list_archive)
+    show_archive_content ();
+  if (add_to_archive)
+    return add_locales_to_archive (argc - remaining, &argv[remaining],
+				   replace_archive);
+  if (delete_from_archive)
+    return delete_locales_from_archive (argc - remaining, &argv[remaining]);
+
   /* POSIX.2 requires to be verbose about missing characters in the
      character map.  */
   verbose |= posix_conformance;
@@ -177,10 +223,18 @@ main (int argc, char *argv[])
 
   /* The parameter describes the output path of the constructed files.
      If the described files cannot be written return a NULL pointer.  */
-  output_path  = construct_output_path (argv[remaining]);
-  if (output_path == NULL)
-    error (4, errno, _("cannot create directory for output files"));
-  cannot_write_why = errno;
+  if (no_archive)
+    {
+      output_path  = construct_output_path (argv[remaining]);
+      if (output_path == NULL)
+	error (4, errno, _("cannot create directory for output files"));
+      cannot_write_why = errno;
+    }
+  else
+    {
+      output_path = NULL;
+      cannot_write_why = 0;	/* Just to shut the compiler up.  */
+    }
 
   /* Now that the parameters are processed we have to reset the local
      ctype locale.  (P1003.2 4.35.5.2)  */
@@ -235,7 +289,7 @@ cannot open locale definition file `%s'"), runp->name));
 	WITH_CUR_LOCALE (error (4, cannot_write_why, _("\
 cannot write output files to `%s'"), output_path));
       else
-	write_all_categories (locales, charmap, output_path);
+	write_all_categories (locales, charmap, argv[remaining], output_path);
     }
   else
     WITH_CUR_LOCALE (error (4, 0, _("\
@@ -264,6 +318,21 @@ parse_opt (int key, char *arg, struct argp_state *state)
     case OPT_PREFIX:
       output_prefix = arg;
       break;
+    case OPT_NO_ARCHIVE:
+      no_archive = true;
+      break;
+    case OPT_ADD_TO_ARCHIVE:
+      add_to_archive = true;
+      break;
+    case OPT_REPLACE:
+      replace_archive = true;
+      break;
+    case OPT_DELETE_FROM_ARCHIVE:
+      delete_from_archive = true;
+      break;
+    case OPT_LIST_ARCHIVE:
+      list_archive = true;
+      break;
     case 'c':
       force_output = 1;
       break;
@@ -392,6 +461,10 @@ construct_output_path (char *path)
       size_t len = strlen (path) + 1;
       result = xmalloc (len + 1);
       endp = mempcpy (result, path, len) - 1;
+
+      /* If the user specified an output path we cannot add the output
+	 to the archive.  */
+      no_archive = true;
     }
 
   errno = 0;
diff --git a/locale/programs/localedef.h b/locale/programs/localedef.h
index 717962e10b..9567845080 100644
--- a/locale/programs/localedef.h
+++ b/locale/programs/localedef.h
@@ -1,5 +1,5 @@
 /* General definitions for localedef(1).
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
@@ -22,11 +22,13 @@
 #define _LOCALEDEF_H	1
 
 /* Get the basic locale definitions.  */
+#include <errno.h>
 #include <locale.h>
+#include <stdbool.h>
 #include <stddef.h>
-#include <errno.h>
 
 #include "repertoire.h"
+#include "../locarchive.h"
 
 
 /* We need a bitmask for the locales.  */
@@ -114,6 +116,8 @@ extern int verbose;
 extern int be_quiet;
 extern int oldstyle_tables;
 extern const char *repertoire_global;
+extern int max_locarchive_open_retry;
+extern bool no_archive;
 
 
 /* Prototypes for a few program-wide used functions.  */
@@ -153,4 +157,24 @@ extern struct localedef_t *load_locale (int locale, const char *name,
 					const struct charmap_t *charmap,
 					struct localedef_t *copy_locale);
 
+
+/* Open the locale archive.  */
+extern void open_archive (struct locarhandle *ah);
+
+/* 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);
+
+/* Add content of named directories to locale archive.  */
+extern int add_locales_to_archive (size_t nlist, char *list[], bool replace);
+
+/* Removed named locales from archive.  */
+extern int delete_locales_from_archive (size_t nlist, char *list[]);
+
+/* List content of locale archive.  */
+extern void show_archive_content (void);
+
 #endif /* localedef.h */
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c
new file mode 100644
index 0000000000..861dd5bd68
--- /dev/null
+++ b/locale/programs/locarchive.c
@@ -0,0 +1,931 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   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.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "../../crypt/md5.h"
+#include "../localeinfo.h"
+#include "../locarchive.h"
+#include "simple-hash.h"
+#include "localedef.h"
+
+
+static const char archivefname[] = LOCALEDIR "/locale-archive";
+
+static const char *locnames[] =
+  {
+#define DEFINE_CATEGORY(category, category_name, items, a) \
+  [category] = category_name,
+#include "categories.def"
+#undef  DEFINE_CATEGORY
+  };
+
+
+/* Size of the initial archive header.  */
+#define INITIAL_NUM_NANES	450
+#define INITIAL_SIZE_STRINGS	3500
+#define INITIAL_NUM_LOCREC	350
+#define INITIAL_NUM_SUMS	2000
+
+
+static void
+create_archive (struct locarhandle *ah)
+{
+  int fd;
+  char fname[] = LOCALEDIR "/locale-archive.XXXXXX";
+  struct locarhead head;
+  void *p;
+  size_t total;
+
+  /* Create a temporary file in the correct directory.  */
+  fd = mkstemp (fname);
+  if (fd == -1)
+    error (EXIT_FAILURE, errno, _("cannot create temporary file"));
+
+  /* Create the initial content of the archive.  */
+  head.magic = AR_MAGIC;
+  head.namehash_offset = sizeof (struct locarhead);
+  head.namehash_used = 0;
+  head.namehash_size = next_prime (INITIAL_NUM_NANES);
+
+  head.string_offset = (head.namehash_offset
+			+ head.namehash_size * sizeof (struct namehashent));
+  head.string_used = 0;
+  head.string_size = INITIAL_SIZE_STRINGS;
+
+  head.locrectab_offset = head.string_offset + head.string_size;
+  head.locrectab_used = 0;
+  head.locrectab_size = INITIAL_NUM_LOCREC;
+
+  head.sumhash_offset = (head.locrectab_offset
+			 + head.locrectab_size * sizeof (struct locrecent));
+  head.sumhash_used = 0;
+  head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
+
+  total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
+
+  /* Write out the header and create room for the other data structures.  */
+  if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
+    }
+
+  if (ftruncate64 (fd, total) != 0)
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval, _("cannot resize archive file"));
+    }
+
+  /* Map the header and all the administration data structures.  */
+  p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (p == MAP_FAILED)
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval, _("cannot map archive header"));
+    }
+
+  /* Now try to rename it.  We don't use the rename function since
+     this would overwrite a file which has been created in
+     parallel.  */
+  if (link (fname, archivefname) == -1)
+    {
+      int errval = errno;
+
+      /* We cannot use the just created file.  */
+      close (fd);
+      unlink (fname);
+
+      if (errval == EEXIST)
+	{
+	  /* There is already an archive.  Must have been a localedef run
+	     which happened in parallel.  Simply open this file then.  */
+	  open_archive (ah);
+	  return;
+	}
+
+      error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
+    }
+
+  /* Remove the temporary name.  */
+  unlink (fname);
+
+  /* Make the file globally readable.  */
+  if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
+    {
+      int errval = errno;
+      unlink (archivefname);
+      error (EXIT_FAILURE, errval,
+	     _("cannot change mode of new locale archive"));
+    }
+
+  ah->fd = fd;
+  ah->addr = p;
+  ah->len = total;
+}
+
+
+static void
+enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
+{
+  struct stat64 st;
+  int fd;
+  char fname[] = LOCALEDIR "/locale-archive.XXXXXX";
+  struct locarhead newhead;
+  size_t total;
+  void *p;
+  unsigned int cnt;
+  struct namehashent *oldnamehashtab;
+  struct locrecent *oldlocrectab;
+  struct locarhandle new_ah;
+
+  /* Not all of the old file has to be mapped.  Change this now this
+     we will have to access the whole content.  */
+  if (fstat64 (ah->fd, &st) != 0
+      || (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
+			     MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
+    error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
+  ah->len = st.st_size;
+
+  /* Create a temporary file in the correct directory.  */
+  fd = mkstemp (fname);
+  if (fd == -1)
+    error (EXIT_FAILURE, errno, _("cannot create temporary file"));
+
+  /* Copy the existing head information.  */
+  newhead = *head;
+
+  /* Create the new archive header.  The sizes of the various tables
+     should be double from what is currently used.  */
+  newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
+			       newhead.namehash_size);
+  printf ("name: size: %u, used: %d, new: size: %u\n",
+	  head->namehash_size, head->namehash_used, newhead.namehash_size);
+
+  newhead.string_offset = (newhead.namehash_offset
+			   + (newhead.namehash_size
+			      * sizeof (struct namehashent)));
+  newhead.string_size = MAX (2 * newhead.string_used, newhead.string_size);
+
+  newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
+  newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
+				newhead.locrectab_size);
+
+  newhead.sumhash_offset = (newhead.locrectab_offset
+			    + (newhead.locrectab_size
+			       * sizeof (struct locrecent)));
+  newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
+			      newhead.sumhash_size);
+
+  total = (newhead.sumhash_offset
+	   + newhead.sumhash_size * sizeof (struct sumhashent));
+
+  /* The new file is empty now.  */
+  newhead.namehash_used = 0;
+  newhead.string_used = 0;
+  newhead.locrectab_used = 0;
+  newhead.sumhash_used = 0;
+
+  /* Write out the header and create room for the other data structures.  */
+  if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
+      != sizeof (newhead))
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
+    }
+
+  if (ftruncate64 (fd, total) != 0)
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval, _("cannot resize archive file"));
+    }
+
+  /* Map the header and all the administration data structures.  */
+  p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (p == MAP_FAILED)
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval, _("cannot map archive header"));
+    }
+
+  /* Lock the new file.  */
+  if (lockf64 (fd, F_LOCK, total) != 0)
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval, _("cannot lock new archive"));
+    }
+
+  new_ah.len = total;
+  new_ah.addr = p;
+  new_ah.fd = fd;
+
+  /* Walk through the hash name hash table to find out what data is
+     still referenced and transfer it into the new file.  */
+  oldnamehashtab = (struct namehashent *) ((char *) ah->addr
+					   + head->namehash_offset);
+  oldlocrectab = (struct locrecent *) ((char *) ah->addr
+				       + head->locrectab_offset);
+  for (cnt = 0; cnt < head->namehash_size; ++cnt)
+    if (oldnamehashtab[cnt].locrec_offset != 0)
+      {
+	/* Insert this entry in the new hash table.  */
+	locale_data_t old_data;
+	unsigned int idx;
+	struct locrecent *oldlocrec;
+
+	oldlocrec = (struct locrecent *) ((char *) ah->addr
+					  + oldnamehashtab[cnt].locrec_offset);
+
+	for (idx = 0; idx < __LC_LAST; ++idx)
+	  if (idx != LC_ALL)
+	    {
+	      old_data[idx].size = oldlocrec->record[idx].len;
+	      old_data[idx].addr
+		= ((char *) ah->addr + oldlocrec->record[idx].offset);
+
+	      __md5_buffer (old_data[idx].addr, old_data[idx].size,
+			    old_data[idx].sum);
+	    }
+
+	if (add_locale_to_archive (&new_ah,
+				   ((char *) ah->addr
+				    + oldnamehashtab[cnt].name_offset),
+				   old_data, 0) != 0)
+	  error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
+      }
+
+
+  /* Make the file globally readable.  */
+  if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval,
+	     _("cannot change mode of resized locale archive"));
+    }
+
+  /* Rename the new file.  */
+  if (rename (fname, archivefname) != 0)
+    {
+      int errval = errno;
+      unlink (fname);
+      error (EXIT_FAILURE, errval, _("cannot rename new archive"));
+    }
+
+  /* Close the old file.  */
+  close_archive (ah);
+
+  /* Add the information for the new one.  */
+  *ah = new_ah;
+}
+
+
+void
+open_archive (struct locarhandle *ah)
+{
+  struct stat64 st;
+  struct stat64 st2;
+  int fd;
+  struct locarhead head;
+  int retry = 0;
+
+ again:
+  /* Open the archive.  We must have exclusive write access.  */
+  fd = open64 (archivefname, O_RDWR);
+  if (fd == -1)
+    {
+      /* Maybe the file does not yet exist.  */
+      if (errno == ENOENT)
+	{
+	  create_archive (ah);
+	  return;
+	}
+      else
+	error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
+	       archivefname);
+    }
+
+  if (fstat64 (fd, &st) < 0)
+    error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
+	   archivefname);
+
+  if (lockf64 (fd, F_LOCK, st.st_size) == -1)
+    {
+      close (fd);
+
+      if (retry++ < max_locarchive_open_retry)
+	{
+	  struct timespec req;
+
+	  /* Wait for a bit.  */
+	  req.tv_sec = 0;
+	  req.tv_nsec = 1000000 * (random () % 500 + 1);
+	  (void) nanosleep (&req, NULL);
+
+	  goto again;
+	}
+
+      error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
+	     archivefname);
+    }
+
+  /* One more check.  Maybe another process replaced the archive file
+     with a new, larger one since we opened the file.  */
+  if (stat64 (archivefname, &st2) == -1
+      || st.st_dev != st2.st_dev
+      || st.st_ino != st2.st_ino)
+    {
+      close (fd);
+      goto again;
+    }
+
+  /* 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));
+
+  /* Now we know how large the administrative information part is.
+     Map all of it.  */
+  ah->addr = mmap64 (NULL, ah->len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (ah->addr == MAP_FAILED)
+    error (EXIT_FAILURE, errno, _("cannot map archive header"));
+}
+
+
+void
+close_archive (struct locarhandle *ah)
+{
+  munmap (ah->addr, ah->len);
+  close (ah->fd);
+}
+
+
+/* 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;
+{
+  /* 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
+     we have to allocate a new locale record.  */
+  size_t name_len = strlen (name);
+  uint32_t file_offsets[__LC_LAST];
+  unsigned int num_new_offsets = 0;
+  struct sumhashent *sumhashtab;
+  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;
+
+  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
+     is already somewhere in the archive.  */
+  for (cnt = 0; cnt < __LC_LAST; ++cnt)
+    if (cnt != LC_ALL)
+      {
+	/* By default signal that we have no data.  */
+	file_offsets[cnt] = 0;
+	++num_new_offsets;
+
+	/* Compute the hash value of the checksum to determine a
+	   starting point for the search in the MD5 hash value
+	   table.  */
+	hval = compute_hashval (data[cnt].sum, 16);
+
+	idx = hval % head->sumhash_size;
+	incr = 1 + hval % (head->sumhash_size - 2);
+
+	while (sumhashtab[idx].file_offset != 0)
+	  {
+	    if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
+	      {
+		/* Found it.  */
+		file_offsets[cnt] = sumhashtab[idx].file_offset;
+		--num_new_offsets;
+		break;
+	      }
+
+	    idx += incr;
+	    if (idx >= head->sumhash_size)
+	      idx -= head->sumhash_size;
+	  }
+      }
+
+
+  /* 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 (! 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];
+
+  /* Determine whether we have to resize the file.  */
+  if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
+      || (namehashent->locrec_offset == 0
+	  && (head->locrectab_used == head->locrectab_size
+	      || 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);
+      return add_locale_to_archive (ah, name, data, replace);
+    }
+
+  /* Add the locale data which is not yet in the archive.  */
+  for (cnt = 0; cnt < __LC_LAST; ++cnt)
+    if (cnt != LC_ALL && file_offsets[cnt] == 0)
+      {
+	/* The data for this section is not yet available in the
+	   archive.  Append it.  */
+	off64_t lastpos;
+	uint32_t md5hval;
+
+	lastpos = lseek64 (ah->fd, 0, SEEK_END);
+	if (lastpos == (off64_t) -1)
+	  error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
+
+	/* Align all data to a 16 byte boundary.  */
+	if ((lastpos & 15) != 0)
+	  {
+	    static const char zeros[15] = { 0, };
+
+	    if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
+		!= 16 - (lastpos & 15))
+	      error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
+
+	    lastpos += 16 - (lastpos & 15);
+	  }
+
+	/* Remember the position.  */
+	file_offsets[cnt] = lastpos;
+
+	/* Write the data.  */
+	if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
+	    != data[cnt].size)
+	  error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
+
+	/* Add the hash value to the hash table.  */
+	md5hval = compute_hashval (data[cnt].sum, 16);
+
+	idx = md5hval % head->sumhash_size;
+	incr = 1 + md5hval % (head->sumhash_size - 2);
+
+	while (sumhashtab[idx].file_offset != 0)
+	  {
+	    idx += incr;
+	    if (idx >= head->sumhash_size)
+	      idx -= head->sumhash_size;
+	  }
+
+	memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
+	sumhashtab[idx].file_offset = file_offsets[cnt];
+
+	++head->sumhash_used;
+      }
+
+
+  if (namehashent->locrec_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;
+
+      /* Allocate a name location record.  */
+      namehashent->locrec_offset = (head->locrectab_offset
+				    + (head->locrectab_used++
+				       * sizeof (struct locrecent)));
+
+      namehashent->hashval = hval;
+
+      ++head->namehash_used;
+    }
+
+
+  /* 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)
+      {
+	locrecent->record[cnt].offset = file_offsets[cnt];
+	locrecent->record[cnt].len = data[cnt].size;
+      }
+
+
+  /* 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.  */
+
+
+  return 0;
+}
+
+
+int
+add_locales_to_archive (nlist, list, replace)
+     size_t nlist;
+     char *list[];
+     bool replace;
+{
+  struct locarhandle ah;
+  int result = 0;
+
+  /* Open the archive.  This call never returns if we cannot
+     successfully open the archive.  */
+  open_archive (&ah);
+
+  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;
+
+      if (! be_quiet)
+	printf (_("Adding %s\n"), fname);
+
+      /* 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.  */
+			  strcpy (stpcpy (stpcpy (stpcpy (stpcpy (fullname,
+								  fname),
+							  "/"),
+						  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))
+	      {
+		close (fd);
+		strcpy (stpcpy (stpcpy (stpcpy (stpcpy (fullname, fname),
+						"/"),
+					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, replace);
+
+      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
+delete_locales_from_archive (nlist, list)
+     size_t nlist;
+     char *list[];
+{
+  struct locarhandle ah;
+  struct locarhead *head;
+  struct namehashent *namehashtab;
+
+  /* Open the archive.  This call never returns if we cannot
+     successfully open the archive.  */
+  open_archive (&ah);
+
+  head = ah.addr;
+  namehashtab = (struct namehashent *) ((char *) ah.addr
+					+ head->namehash_offset);
+
+  while (nlist-- > 0)
+    {
+      const char *locname = *list++;
+      uint32_t hval;
+      unsigned int idx;
+      unsigned int incr;
+
+      /* Search for this locale in the archive.  */
+      hval = compute_hashval (locname, strlen (locname));
+
+      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 (locname,
+			  (char *) ah.addr + namehashtab[idx].name_offset)
+		  == 0))
+	    {
+	      /* 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;
+	    }
+
+	  idx += incr;
+	  if (idx >= head->namehash_size)
+	    idx -= head->namehash_size;
+	}
+
+      if (namehashtab[idx].name_offset == 0 && ! be_quiet)
+	error (0, 0, _("locale \"%s\" not in archive"), locname);
+    }
+
+  close_archive (&ah);
+
+  return 0;
+}
+
+
+static int
+xstrcmp (const void *a, const void *b)
+{
+  return strcmp (*(const char **) a, *(const char **) b);
+}
+
+
+void
+show_archive_content (void)
+{
+  struct locarhandle ah;
+  struct locarhead *head;
+  struct namehashent *namehashtab;
+  int cnt;
+  char **names;
+  int used;
+
+  /* Open the archive.  This call never returns if we cannot
+     successfully open the archive.  */
+  open_archive (&ah);
+
+  head = ah.addr;
+
+  names = (char **) xmalloc (head->namehash_used * sizeof (char *));
+
+  namehashtab = (struct namehashent *) ((char *) ah.addr
+					+ head->namehash_offset);
+  for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
+    if (namehashtab[cnt].locrec_offset != 0)
+      {
+	assert (used < head->namehash_used);
+	names[used++] = ah.addr + namehashtab[cnt].name_offset;
+      }
+
+  /* Sort the names.  */
+  qsort (names, used, sizeof (char *), xstrcmp);
+
+  for (cnt = 0; cnt < used; ++cnt)
+    puts (names[cnt]);
+
+  close_archive (&ah);
+
+  exit (EXIT_SUCCESS);
+}
diff --git a/locale/programs/locfile.c b/locale/programs/locfile.c
index 07a65bbb54..4d978d12a1 100644
--- a/locale/programs/locfile.c
+++ b/locale/programs/locfile.c
@@ -30,12 +30,18 @@
 #include <sys/param.h>
 #include <sys/stat.h>
 
+#include "../../crypt/md5.h"
 #include "localedef.h"
 #include "locfile.h"
+#include "simple-hash.h"
 
 #include "locfile-kw.h"
 
 
+/* Temporary storage of the locale data before writing it to the archive.  */
+static locale_data_t to_archive;
+
+
 int
 locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
 {
@@ -312,9 +318,10 @@ static void (*const write_funcs[]) (struct localedef_t *,
   [LC_IDENTIFICATION] = identification_output
 };
 
+
 void
 write_all_categories (struct localedef_t *definitions,
-		      const struct charmap_t *charmap,
+		      const struct charmap_t *charmap, const char *locname,
 		      const char *output_path)
 {
   int cnt;
@@ -322,8 +329,25 @@ write_all_categories (struct localedef_t *definitions,
   for (cnt = 0; cnt < sizeof (write_funcs) / sizeof (write_funcs[0]); ++cnt)
     if (write_funcs[cnt] != NULL)
       write_funcs[cnt] (definitions, charmap, output_path);
+
+  if (! no_archive)
+    {
+      /* The data has to be added to the archive.  Do this now.  */
+      struct locarhandle ah;
+
+      /* Open the archive.  This call never returns if we cannot
+	 successfully open the archive.  */
+      open_archive (&ah);
+
+      if (add_locale_to_archive (&ah, locname, to_archive, true) != 0)
+	error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
+
+      /* We are done.  */
+      close_archive (&ah);
+    }
 }
 
+
 /* Return a NULL terminated list of the directories next to output_path
    that have the same owner, group, permissions and device as output_path.  */
 static const char **
@@ -408,6 +432,7 @@ siblings_uncached (const char *output_path)
   return elems;
 }
 
+
 /* Return a NULL terminated list of the directories next to output_path
    that have the same owner, group, permissions and device as output_path.
    Cache the result for future calls.  */
@@ -434,6 +459,7 @@ siblings (const char *output_path)
   return last_result;
 }
 
+
 /* Read as many bytes from a file descriptor as possible.  */
 static ssize_t
 full_read (int fd, void *bufarea, size_t nbyte)
@@ -457,6 +483,7 @@ full_read (int fd, void *bufarea, size_t nbyte)
   return buf - (char *) bufarea;
 }
 
+
 /* Compare the contents of two regular files of the same size.  Return 0
    if they are equal, 1 if they are different, or -1 if an error occurs.  */
 static int
@@ -506,9 +533,10 @@ compare_files (const char *filename1, const char *filename2, size_t size,
   return ret;
 }
 
+
 /* Write a locale file, with contents given by N_ELEM and VEC.  */
 void
-write_locale_data (const char *output_path, const char *category,
+write_locale_data (const char *output_path, int catidx, const char *category,
 		   size_t n_elem, struct iovec *vec)
 {
   size_t cnt, step, maxiov;
@@ -516,6 +544,32 @@ write_locale_data (const char *output_path, const char *category,
   char *fname;
   const char **other_paths;
 
+  if (! no_archive)
+    {
+      /* The data will be added to the archive.  For now we simply
+	 generate the image which will be written.  First determine
+	 the size.  */
+      int cnt;
+      void *endp;
+
+      to_archive[catidx].size = 0;
+      for (cnt = 0; cnt < n_elem; ++cnt)
+	to_archive[catidx].size += vec[cnt].iov_len;
+
+      /* Allocate the memory for it.  */
+      to_archive[catidx].addr = xmalloc (to_archive[catidx].size);
+
+      /* Fill it in.  */
+      for (cnt = 0, endp = to_archive[catidx].addr; cnt < n_elem; ++cnt)
+	endp = mempcpy (endp, vec[cnt].iov_base, vec[cnt].iov_len);
+
+      /* Compute the MD5 sum for the data.  */
+      __md5_buffer (to_archive[catidx].addr, to_archive[catidx].size,
+		    to_archive[catidx].sum);
+
+      return;
+    }
+
   fname = xmalloc (strlen (output_path) + 2 * strlen (category) + 7);
 
   /* Normally we write to the directory pointed to by the OUTPUT_PATH.
@@ -680,8 +734,7 @@ failure while writing data for category `%s'"), category));
 			  char * tmp_fname =
 			    (char *) xmalloc (strlen (fname) + 4 + 1);
 
-			  strcpy (tmp_fname, fname);
-			  strcat (tmp_fname, ".tmp");
+			  strcpy (stpcpy (tmp_fname, fname), ".tmp");
 
 			  if (link (other_fname, tmp_fname) >= 0)
 			    {
diff --git a/locale/programs/locfile.h b/locale/programs/locfile.h
index 4f6c8fee3e..2e96c62ec7 100644
--- a/locale/programs/locfile.h
+++ b/locale/programs/locfile.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996,1997,1998,1999,2000,2001 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
 
@@ -115,11 +115,13 @@ extern void check_all_categories (struct localedef_t *definitions,
 /* Write out all locale categories.  */
 extern void write_all_categories (struct localedef_t *definitions,
 				  const struct charmap_t *charmap,
+				  const char *locname,
 				  const char *output_path);
 
 /* Write out the data.  */
-extern void write_locale_data (const char *output_path, const char *category,
-			       size_t n_elem, struct iovec *vec);
+extern void write_locale_data (const char *output_path, int catidx,
+			       const char *category, size_t n_elem,
+			       struct iovec *vec);
 
 
 /* Entrypoints for the parsers of the individual categories.  */
diff --git a/locale/programs/simple-hash.c b/locale/programs/simple-hash.c
index e501038a1a..b52b5593d0 100644
--- a/locale/programs/simple-hash.c
+++ b/locale/programs/simple-hash.c
@@ -1,5 +1,5 @@
 /* Implement simple hashing table with string based keys.
-   Copyright (C) 1994-1997, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1994-1997, 2000, 2001, 2002 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, October 1994.
 
@@ -54,6 +54,8 @@
 # define bcopy(s, d, n)	memcpy ((d), (s), (n))
 #endif
 
+#include "hashval.h"
+
 extern void *xmalloc (size_t __n);
 extern void *xcalloc (size_t __n, size_t __m);
 
@@ -72,7 +74,6 @@ static void insert_entry_2 (hash_table *htab, const void *key, size_t keylen,
 			    unsigned long hval, size_t idx, void *data);
 static size_t lookup (const hash_table *htab, const void *key, size_t keylen,
 		      unsigned long int hval);
-static unsigned long compute_hashval (const void *key, size_t keylen);
 static int is_prime (unsigned long int candidate);
 
 
@@ -297,30 +298,7 @@ lookup (htab, key, keylen, hval)
 }
 
 
-static unsigned long
-compute_hashval (key, keylen)
-     const void *key;
-     size_t keylen;
-{
-  size_t cnt;
-  unsigned long int hval;
-
-  /* Compute the hash value for the given string.  The algorithm
-     is taken from [Aho,Sethi,Ullman], modified to reduce the number of
-     collisions for short strings with very varied bit patterns.
-     See http://www.clisp.org/haible/hashfunc.html.  */
-  cnt = 0;
-  hval = keylen;
-  while (cnt < keylen)
-    {
-      hval = (hval << 9) | (hval >> (LONGBITS - 9));
-      hval += (unsigned long int) *(((char *) key) + cnt++);
-    }
-  return hval != 0 ? hval : ~((unsigned long) 0);
-}
-
-
-unsigned long
+unsigned long int
 next_prime (seed)
      unsigned long int seed;
 {
diff --git a/locale/programs/simple-hash.h b/locale/programs/simple-hash.h
index b811a6fd4d..469caedc19 100644
--- a/locale/programs/simple-hash.h
+++ b/locale/programs/simple-hash.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1995, 1996, 1997, 1998, 1999, 2001 Free Software Foundation, Inc.
+/* Copyright (C) 1995-1999, 2001, 2002 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@gnu.org>, 1995.
 
@@ -46,6 +46,8 @@ extern int iterate_table (const hash_table *htab, void **ptr,
 			  const void **key, size_t *keylen, void **data)
      __THROW;
 
+extern unsigned long int compute_hashval (const void *key, size_t keylen)
+     __THROW;
 extern unsigned long int next_prime (unsigned long int seed) __THROW;
 
 #endif /* simple-hash.h */