summary refs log tree commit diff
diff options
context:
space:
mode:
authorRichard Sandiford <richard@codesourcery.com>2013-09-06 17:20:45 +0000
committerJoseph Myers <joseph@codesourcery.com>2013-09-06 17:20:45 +0000
commit1ecbb381aeed75d52f8abf47e03a31bb8212984e (patch)
treef6244ddf2f45891065af57085bc81775c83142c3
parent2618d9db2da5d5f59adb8120fc6b58d8f96f5528 (diff)
downloadglibc-1ecbb381aeed75d52f8abf47e03a31bb8212984e.tar.gz
glibc-1ecbb381aeed75d52f8abf47e03a31bb8212984e.tar.xz
glibc-1ecbb381aeed75d52f8abf47e03a31bb8212984e.zip
Make localedef output generation use more logical interfaces.
-rw-r--r--ChangeLog80
-rw-r--r--locale/programs/3level.h54
-rw-r--r--locale/programs/ld-address.c108
-rw-r--r--locale/programs/ld-collate.c199
-rw-r--r--locale/programs/ld-ctype.c518
-rw-r--r--locale/programs/ld-identification.c130
-rw-r--r--locale/programs/ld-measurement.c35
-rw-r--r--locale/programs/ld-messages.c52
-rw-r--r--locale/programs/ld-monetary.c307
-rw-r--r--locale/programs/ld-name.c65
-rw-r--r--locale/programs/ld-numeric.c65
-rw-r--r--locale/programs/ld-paper.c41
-rw-r--r--locale/programs/ld-telephone.c53
-rw-r--r--locale/programs/ld-time.c440
-rw-r--r--locale/programs/locfile.c171
-rw-r--r--locale/programs/locfile.h31
16 files changed, 738 insertions, 1611 deletions
diff --git a/ChangeLog b/ChangeLog
index b107551fad..81615fb445 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,83 @@
+2013-09-06  Richard Sandiford  <richard@codesourcery.com>
+	    Joseph Myers  <joseph@codesourcery.com>
+
+	* locale/programs/locfile.c: Include <assert.h>, <wchar.h> and
+	"localeinfo.h".
+	(obstack_chunk_alloc): New macro.
+	(obstack_chunk_free): Likewise.
+	(record_offset): New function.
+	(init_locale_data): Likewise.
+	(align_locale_data): Likewise.
+	(add_locale_empty): Likewise.
+	(add_locale_raw_data): Likewise.
+	(add_locale_raw_obstack): Likewise.
+	(add_locale_string): Likewise.
+	(add_locale_wstring): Likewise.
+	(add_locale_uint32): Likewise.
+	(add_locale_uint32_array): Likewise.
+	(add_locale_char): Likewise.
+	(start_locale_structure): Likewise.
+	(end_locale_structure): Likewise.
+	(start_locale_prelude): Likewise.
+	(end_locale_prelude): Likewise.
+	(write_locale_data): Take locale_file structure rather than an
+	iovec.
+	* locale/programs/locfile.h: Include "obstack.h".
+	(struct locale_file): Change to store locale file contents instead
+	of header.
+	(init_locale_data): New prototype.
+	(align_locale_data): Likewise.
+	(add_locale_empty): Likewise.
+	(add_locale_raw_data): Likewise.
+	(add_locale_raw_obstack): Likewise.
+	(add_locale_string): Likewise.
+	(add_locale_wstring): Likewise.
+	(add_locale_uint32): Likewise.
+	(add_locale_uint32_array): Likewise.
+	(add_locale_char): Likewise.
+	(start_locale_structure): Likewise.
+	(end_locale_structure): Likewise.
+	(start_locale_prelude): Likewise.
+	(end_locale_prelude): Likewise.
+	(write_locale_data): Update prototype.
+	* locale/programs/3level.h (struct TABLE): Remove result field.
+	(CONCAT(TABLE,_finalize)): Change to CONCAT(add_locale_,TABLE).
+	Use new locale_file interface.
+	[!NO_FINALIZE]: Change condition to [!NO_ADD_LOCALE].
+	(NO_FINALIZE): Change #undef to #undef of NO_ADD_LOCALE.
+	* locale/programs/ld-address.c (address_output): Use new
+	locale_file interface.
+	* locale/programs/ld-collate.c (NO_FINALIZE): Change to
+	NO_ADD_LOCALE.
+	(collate_finish): Don't call collseq_table_finalize.
+	(collate_output): Use new locale_file interface.
+	* locale/programs/ld-ctype.c: Move includes of "3level.h" earlier
+	in file.
+	(NO_FINALIZE): Change to NO_ADD_LOCALE.
+	(TABLE): Move defines earlier in file.
+	(ELEMENT): Likewise.
+	(DEFAULT): Likewise.
+	(wctrans_table_add): Move macro and inline function earlier in
+	file.
+	(struct wctype_table): Move type earlier in file.
+	(add_locale_wctype_table): New static prototype.
+	(struct locale_ctype_t): Use logical types instead of struct iovec
+	pointers for members.
+	(ctype_output): Use new locale_file interface.
+	(wctype_table_finalize): Change to add_locale_wctype_table.  Use
+	new locale_file interface.
+	(allocate_arrays): Update for use of new locale_file interface.
+	* locale/programs/ld-identification.c (identification_output): Use
+	new locale_file interface.
+	* locale/programs/ld-measurement.c (measurement_output): Likewise.
+	* locale/programs/ld-messages.c (messages_output): Likewise.
+	* locale/programs/ld-monetary.c (monetary_output): Likewise.
+	* locale/programs/ld-name.c (name_output): Likewise.
+	* locale/programs/ld-numeric.c (numeric_output): Likewise.
+	* locale/programs/ld-paper.c (paper_output): Likewise.
+	* locale/programs/ld-telephone.c (telephone_output): Likewise.
+	* locale/programs/ld-time.c (time_output): Likewise.
+
 2013-09-06  Adhemerval Zanella  <azanella@linux.vnet.ibm.com>
 
 	* benchtests/Makefile: Add memrchr benchmark.
diff --git a/locale/programs/3level.h b/locale/programs/3level.h
index 9b8b1b96ad..b8d6ca0235 100644
--- a/locale/programs/3level.h
+++ b/locale/programs/3level.h
@@ -26,7 +26,8 @@
      ELEMENT      to the type of every entry
      DEFAULT      to the default value for empty entries
      ITERATE      if you want the TABLE_iterate function to be defined
-     NO_FINALIZE  if you don't want the TABLE_finalize function to be defined
+     NO_ADD_LOCALE  if you don't want the add_locale_TABLE function
+		    to be defined
 
    This will define
 
@@ -36,7 +37,7 @@
      void TABLE_add (struct TABLE *t, uint32_t wc, ELEMENT value);
      void TABLE_iterate (struct TABLE *t,
 			 void (*fn) (uint32_t wc, ELEMENT value));
-     void TABLE_finalize (struct TABLE *t);
+     void add_locale_TABLE (struct locale_file *file, struct TABLE *t);
 */
 
 #define CONCAT(a,b) CONCAT1(a,b)
@@ -57,9 +58,8 @@ struct TABLE
   size_t level3_alloc;
   size_t level3_size;
   ELEMENT *level3;
-  /* Compressed representation.  */
+  /* Size of compressed representation.  */
   size_t result_size;
-  char *result;
 };
 
 /* Initialize.  Assumes t->p and t->q have already been set.  */
@@ -206,15 +206,15 @@ CONCAT(TABLE,_iterate) (struct TABLE *t,
 }
 #endif
 
-#ifndef NO_FINALIZE
+#ifndef NO_ADD_LOCALE
 /* Finalize and shrink.  */
 static void
-CONCAT(TABLE,_finalize) (struct TABLE *t)
+CONCAT(add_locale_,TABLE) (struct locale_file *file, struct TABLE *t)
 {
   size_t i, j, k;
   uint32_t reorder3[t->level3_size];
   uint32_t reorder2[t->level2_size];
-  uint32_t level1_offset, level2_offset, level3_offset, last_offset;
+  uint32_t level2_offset, level3_offset, last_offset;
 
   /* Uniquify level3 blocks.  */
   k = 0;
@@ -271,10 +271,7 @@ CONCAT(TABLE,_finalize) (struct TABLE *t)
     + (t->level2_size << t->q) * sizeof (uint32_t)
     + (t->level3_size << t->p) * sizeof (ELEMENT);
   t->result_size = (last_offset + 3) & ~3ul;
-  t->result = (char *) xmalloc (t->result_size);
 
-  level1_offset =
-    5 * sizeof (uint32_t);
   level2_offset =
     5 * sizeof (uint32_t)
     + t->level1_size * sizeof (uint32_t);
@@ -283,29 +280,36 @@ CONCAT(TABLE,_finalize) (struct TABLE *t)
     + t->level1_size * sizeof (uint32_t)
     + (t->level2_size << t->q) * sizeof (uint32_t);
 
-  ((uint32_t *) t->result)[0] = t->q + t->p;
-  ((uint32_t *) t->result)[1] = t->level1_size;
-  ((uint32_t *) t->result)[2] = t->p;
-  ((uint32_t *) t->result)[3] = (1 << t->q) - 1;
-  ((uint32_t *) t->result)[4] = (1 << t->p) - 1;
+  start_locale_structure (file);
+  add_locale_uint32 (file, t->q + t->p);
+  add_locale_uint32 (file, t->level1_size);
+  add_locale_uint32 (file, t->p);
+  add_locale_uint32 (file, (1 << t->q) - 1);
+  add_locale_uint32 (file, (1 << t->p) - 1);
 
   for (i = 0; i < t->level1_size; i++)
-    ((uint32_t *) (t->result + level1_offset))[i] =
-      (t->level1[i] == EMPTY
+    add_locale_uint32
+      (file,
+       t->level1[i] == EMPTY
        ? 0
        : (t->level1[i] << t->q) * sizeof (uint32_t) + level2_offset);
 
   for (i = 0; i < (t->level2_size << t->q); i++)
-    ((uint32_t *) (t->result + level2_offset))[i] =
-      (t->level2[i] == EMPTY
+    add_locale_uint32
+      (file,
+       t->level2[i] == EMPTY
        ? 0
        : (t->level2[i] << t->p) * sizeof (ELEMENT) + level3_offset);
 
-  for (i = 0; i < (t->level3_size << t->p); i++)
-    ((ELEMENT *) (t->result + level3_offset))[i] = t->level3[i];
-
-  if (last_offset < t->result_size)
-    memset (t->result + last_offset, 0, t->result_size - last_offset);
+  if (sizeof (ELEMENT) == 1)
+    add_locale_raw_data (file, t->level3, t->level3_size << t->p);
+  else if (sizeof (ELEMENT) == sizeof (uint32_t))
+    add_locale_uint32_array (file, (uint32_t *) t->level3,
+			     t->level3_size << t->p);
+  else
+    abort ();
+  align_locale_data (file, 4);
+  end_locale_structure (file);
 
   if (t->level1_alloc > 0)
     free (t->level1);
@@ -321,4 +325,4 @@ CONCAT(TABLE,_finalize) (struct TABLE *t)
 #undef ELEMENT
 #undef DEFAULT
 #undef ITERATE
-#undef NO_FINALIZE
+#undef NO_ADD_LOCALE
diff --git a/locale/programs/ld-address.c b/locale/programs/ld-address.c
index 39b9a836f4..291e7b787f 100644
--- a/locale/programs/ld-address.c
+++ b/locale/programs/ld-address.c
@@ -349,97 +349,23 @@ address_output (struct localedef_t *locale, const struct charmap_t *charmap,
 		const char *output_path)
 {
   struct locale_address_t *address = locale->categories[LC_ADDRESS].address;
-  struct iovec iov[3 + _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS)];
-  size_t cnt = 0;
-
-  data.magic = LIMAGIC (LC_ADDRESS);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
-  iov[cnt].iov_base = (void *) address->postal_fmt;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->country_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->country_post;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->country_ab2;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->country_ab3;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->country_car;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-
-  /* Align following data */
-  iov[cnt].iov_base = (void *) "\0\0";
-  iov[cnt].iov_len = ((idx[cnt - 2] + 3) & ~3) - idx[cnt - 2];
-  idx[cnt - 2] = (idx[cnt - 2] + 3) & ~3;
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) &address->country_num;
-  iov[cnt].iov_len = sizeof (uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->country_isbn;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->lang_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->lang_ab;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->lang_term;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) address->lang_lib;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) charmap->code_set_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  assert (cnt == 3 + _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS));
-
-  write_locale_data (output_path, LC_ADDRESS, "LC_ADDRESS",
-		     3 + _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS), iov);
+  struct locale_file file;
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS));
+  add_locale_string (&file, address->postal_fmt);
+  add_locale_string (&file, address->country_name);
+  add_locale_string (&file, address->country_post);
+  add_locale_string (&file, address->country_ab2);
+  add_locale_string (&file, address->country_ab3);
+  add_locale_string (&file, address->country_car);
+  add_locale_uint32 (&file, address->country_num);
+  add_locale_string (&file, address->country_isbn);
+  add_locale_string (&file, address->lang_name);
+  add_locale_string (&file, address->lang_ab);
+  add_locale_string (&file, address->lang_term);
+  add_locale_string (&file, address->lang_lib);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_ADDRESS, "LC_ADDRESS", &file);
 }
 
 
diff --git a/locale/programs/ld-collate.c b/locale/programs/ld-collate.c
index e58c8f7020..c4d7e3d3d2 100644
--- a/locale/programs/ld-collate.c
+++ b/locale/programs/ld-collate.c
@@ -165,7 +165,7 @@ struct symbol_t
 #define ELEMENT struct element_t *
 #define DEFAULT NULL
 #define ITERATE
-#define NO_FINALIZE
+#define NO_ADD_LOCALE
 #include "3level.h"
 
 /* Sparse table of int32_t.  */
@@ -1813,8 +1813,6 @@ symbol `%s' has the same encoding as"), (*eptr)->name);
       runp = runp->next;
     }
 
-  collseq_table_finalize (&collate->wcseqorder);
-
   /* Now determine whether the UNDEFINED entry is needed and if yes,
      whether it was defined.  */
   collate->undefined.used_in_level = need_undefined ? ~0ul : 0;
@@ -2098,10 +2096,7 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
 {
   struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
   const size_t nelems = _NL_ITEM_INDEX (_NL_NUM_LC_COLLATE);
-  struct iovec iov[2 + nelems];
-  struct locale_file data;
-  uint32_t idx[nelems];
-  size_t cnt;
+  struct locale_file file;
   size_t ch;
   int32_t tablemb[256];
   struct obstack weightpool;
@@ -2114,51 +2109,22 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
   int i;
   struct element_t *runp;
 
-  data.magic = LIMAGIC (LC_COLLATE);
-  data.n = nelems;
-  iov[0].iov_base = (void *) &data;
-  iov[0].iov_len = sizeof (data);
-
-  iov[1].iov_base = (void *) idx;
-  iov[1].iov_len = sizeof (idx);
-
-  idx[0] = iov[0].iov_len + iov[1].iov_len;
-  cnt = 0;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_NRULES));
-  iov[2 + cnt].iov_base = &nrules;
-  iov[2 + cnt].iov_len = sizeof (uint32_t);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
+  init_locale_data (&file, nelems);
+  add_locale_uint32 (&file, nrules);
 
   /* If we have no LC_COLLATE data emit only the number of rules as zero.  */
   if (collate == NULL)
     {
-      int32_t dummy = 0;
-
-      while (cnt < _NL_ITEM_INDEX (_NL_NUM_LC_COLLATE))
+      size_t idx;
+      for (idx = 1; idx < nelems; idx++)
 	{
 	  /* The words have to be handled specially.  */
-	  if (cnt == _NL_ITEM_INDEX (_NL_COLLATE_SYMB_HASH_SIZEMB))
-	    {
-	      iov[2 + cnt].iov_base = &dummy;
-	      iov[2 + cnt].iov_len = sizeof (int32_t);
-	    }
+	  if (idx == _NL_ITEM_INDEX (_NL_COLLATE_SYMB_HASH_SIZEMB))
+	    add_locale_uint32 (&file, 0);
 	  else
-	    {
-	      iov[2 + cnt].iov_base = NULL;
-	      iov[2 + cnt].iov_len = 0;
-	    }
-
-	  if (cnt + 1 < _NL_ITEM_INDEX (_NL_NUM_LC_COLLATE))
-	    idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-	  ++cnt;
+	    add_locale_empty (&file);
 	}
-
-      assert (cnt == _NL_ITEM_INDEX (_NL_NUM_LC_COLLATE));
-
-      write_locale_data (output_path, LC_COLLATE, "LC_COLLATE", 2 + cnt, iov);
-
+      write_locale_data (output_path, LC_COLLATE, "LC_COLLATE", &file);
       return;
     }
 
@@ -2191,11 +2157,7 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
       obstack_1grow (&weightpool, '\0');
     while (++i < __alignof__ (int32_t));
 
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_RULESETS));
-  iov[2 + cnt].iov_len = obstack_object_size (&weightpool);
-  iov[2 + cnt].iov_base = obstack_finish (&weightpool);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
+  add_locale_raw_obstack (&file, &weightpool);
 
   /* Generate the 8-bit table.  Walk through the lists of sequences
      starting with the same byte and add them one after the other to
@@ -2382,55 +2344,16 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
     obstack_1grow (&weightpool, 0);
 
   /* Now add the four tables.  */
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_TABLEMB));
-  iov[2 + cnt].iov_base = tablemb;
-  iov[2 + cnt].iov_len = sizeof (tablemb);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert ((iov[2 + cnt].iov_len & (__alignof__ (int32_t) - 1)) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_WEIGHTMB));
-  iov[2 + cnt].iov_len = obstack_object_size (&weightpool);
-  iov[2 + cnt].iov_base = obstack_finish (&weightpool);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_EXTRAMB));
-  iov[2 + cnt].iov_len = obstack_object_size (&extrapool);
-  iov[2 + cnt].iov_base = obstack_finish (&extrapool);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_INDIRECTMB));
-  iov[2 + cnt].iov_len = obstack_object_size (&indirectpool);
-  iov[2 + cnt].iov_base = obstack_finish (&indirectpool);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert ((iov[2 + cnt].iov_len & (__alignof__ (int32_t) - 1)) == 0);
-  ++cnt;
-
+  add_locale_uint32_array (&file, (const uint32_t *) tablemb, 256);
+  add_locale_raw_obstack (&file, &weightpool);
+  add_locale_raw_obstack (&file, &extrapool);
+  add_locale_raw_obstack (&file, &indirectpool);
 
   /* Now the same for the wide character table.  We need to store some
      more information here.  */
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_GAP1));
-  iov[2 + cnt].iov_base = NULL;
-  iov[2 + cnt].iov_len = 0;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_GAP2));
-  iov[2 + cnt].iov_base = NULL;
-  iov[2 + cnt].iov_len = 0;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_GAP3));
-  iov[2 + cnt].iov_base = NULL;
-  iov[2 + cnt].iov_len = 0;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
+  add_locale_empty (&file);
+  add_locale_empty (&file);
+  add_locale_empty (&file);
 
   /* Since we are using the sign of an integer to mark indirection the
      offsets in the arrays we are indirectly referring to must not be
@@ -2462,41 +2385,11 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
 
   memset (&atwc, 0, sizeof (atwc));
 
-  collidx_table_finalize (&tablewc);
-
   /* Now add the four tables.  */
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_TABLEWC));
-  iov[2 + cnt].iov_base = tablewc.result;
-  iov[2 + cnt].iov_len = tablewc.result_size;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (iov[2 + cnt].iov_len % sizeof (int32_t) == 0);
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_WEIGHTWC));
-  iov[2 + cnt].iov_len = obstack_object_size (&weightpool);
-  iov[2 + cnt].iov_base = obstack_finish (&weightpool);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (iov[2 + cnt].iov_len % sizeof (int32_t) == 0);
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_EXTRAWC));
-  iov[2 + cnt].iov_len = obstack_object_size (&extrapool);
-  iov[2 + cnt].iov_base = obstack_finish (&extrapool);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (iov[2 + cnt].iov_len % sizeof (int32_t) == 0);
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_INDIRECTWC));
-  iov[2 + cnt].iov_len = obstack_object_size (&indirectpool);
-  iov[2 + cnt].iov_base = obstack_finish (&indirectpool);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (iov[2 + cnt].iov_len % sizeof (int32_t) == 0);
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
+  add_locale_collidx_table (&file, &tablewc);
+  add_locale_raw_obstack (&file, &weightpool);
+  add_locale_raw_obstack (&file, &extrapool);
+  add_locale_raw_obstack (&file, &indirectpool);
 
   /* Finally write the table with collation element names out.  It is
      a hash table with a simple function which gets the name of the
@@ -2594,47 +2487,13 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
     }
 
   /* Prepare to write out this data.  */
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_SYMB_HASH_SIZEMB));
-  iov[2 + cnt].iov_base = &elem_size;
-  iov[2 + cnt].iov_len = sizeof (int32_t);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_SYMB_TABLEMB));
-  iov[2 + cnt].iov_base = elem_table;
-  iov[2 + cnt].iov_len = elem_size * 2 * sizeof (int32_t);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_SYMB_EXTRAMB));
-  iov[2 + cnt].iov_len = obstack_object_size (&extrapool);
-  iov[2 + cnt].iov_base = obstack_finish (&extrapool);
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_COLLSEQMB));
-  iov[2 + cnt].iov_base = collate->mbseqorder;
-  iov[2 + cnt].iov_len = 256;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_COLLSEQWC));
-  iov[2 + cnt].iov_base = collate->wcseqorder.result;
-  iov[2 + cnt].iov_len = collate->wcseqorder.result_size;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  assert (idx[cnt] % __alignof__ (int32_t) == 0);
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_COLLATE_CODESET));
-  iov[2 + cnt].iov_base = (void *) charmap->code_set_name;
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  ++cnt;
-
-  assert (cnt == _NL_ITEM_INDEX (_NL_NUM_LC_COLLATE));
-
-  write_locale_data (output_path, LC_COLLATE, "LC_COLLATE", 2 + cnt, iov);
+  add_locale_uint32 (&file, elem_size);
+  add_locale_uint32_array (&file, elem_table, 2 * elem_size);
+  add_locale_raw_obstack (&file, &extrapool);
+  add_locale_raw_data (&file, collate->mbseqorder, 256);
+  add_locale_collseq_table (&file, &collate->wcseqorder);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_COLLATE, "LC_COLLATE", &file);
 
   obstack_free (&weightpool, NULL);
   obstack_free (&extrapool, NULL);
diff --git a/locale/programs/ld-ctype.c b/locale/programs/ld-ctype.c
index 8be7fce887..3903ee0bce 100644
--- a/locale/programs/ld-ctype.c
+++ b/locale/programs/ld-ctype.c
@@ -119,9 +119,51 @@ struct translit_include_t
 #define TABLE idx_table
 #define ELEMENT uint32_t
 #define DEFAULT ((uint32_t) ~0)
-#define NO_FINALIZE
+#define NO_ADD_LOCALE
 #include "3level.h"
 
+#define TABLE wcwidth_table
+#define ELEMENT uint8_t
+#define DEFAULT 0xff
+#include "3level.h"
+
+#define TABLE wctrans_table
+#define ELEMENT int32_t
+#define DEFAULT 0
+#define wctrans_table_add wctrans_table_add_internal
+#include "3level.h"
+#undef wctrans_table_add
+/* The wctrans_table must actually store the difference between the
+   desired result and the argument.  */
+static inline void
+wctrans_table_add (struct wctrans_table *t, uint32_t wc, uint32_t mapped_wc)
+{
+  wctrans_table_add_internal (t, wc, mapped_wc - wc);
+}
+
+/* Construction of sparse 3-level tables.
+   See wchar-lookup.h for their structure and the meaning of p and q.  */
+
+struct wctype_table
+{
+  /* Parameters.  */
+  unsigned int p;
+  unsigned int q;
+  /* Working representation.  */
+  size_t level1_alloc;
+  size_t level1_size;
+  uint32_t *level1;
+  size_t level2_alloc;
+  size_t level2_size;
+  uint32_t *level2;
+  size_t level3_alloc;
+  size_t level3_size;
+  uint32_t *level3;
+  size_t result_size;
+};
+
+static void add_locale_wctype_table (struct locale_file *file,
+				     struct wctype_table *t);
 
 /* The real definition of the struct for the LC_CTYPE locale.  */
 struct locale_ctype_t
@@ -189,11 +231,11 @@ struct locale_ctype_t
   uint32_t **map_b;
   uint32_t **map32_b;
   uint32_t **class_b;
-  struct iovec *class_3level;
-  struct iovec *map_3level;
+  struct wctype_table *class_3level;
+  struct wctrans_table *map_3level;
   uint32_t *class_name_ptr;
   uint32_t *map_name_ptr;
-  struct iovec width;
+  struct wcwidth_table width;
   uint32_t mb_cur_max;
   const char *codeset_name;
   uint32_t *translit_from_idx;
@@ -905,33 +947,21 @@ void
 ctype_output (struct localedef_t *locale, const struct charmap_t *charmap,
 	      const char *output_path)
 {
-  static const char nulbytes[4] = { 0, 0, 0, 0 };
   struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
   const size_t nelems = (_NL_ITEM_INDEX (_NL_CTYPE_EXTRA_MAP_1)
 			 + ctype->nr_charclass + ctype->map_collection_nr);
-  struct iovec *iov = alloca (sizeof *iov
-			      * (2 + nelems + 2 * ctype->nr_charclass
-				 + ctype->map_collection_nr + 4));
-  struct locale_file data;
-  uint32_t *idx = alloca (sizeof *idx * (nelems + 1));
+  struct locale_file file;
   uint32_t default_missing_len;
-  size_t elem, cnt, offset, total;
-  char *cp;
+  size_t elem, cnt;
 
   /* Now prepare the output: Find the sizes of the table we can use.  */
   allocate_arrays (ctype, charmap, ctype->repertoire);
 
-  data.magic = LIMAGIC (LC_CTYPE);
-  data.n = nelems;
-  iov[0].iov_base = (void *) &data;
-  iov[0].iov_len = sizeof (data);
-
-  iov[1].iov_base = (void *) idx;
-  iov[1].iov_len = nelems * sizeof (uint32_t);
-
-  idx[0] = iov[0].iov_len + iov[1].iov_len;
-  offset = 0;
+  default_missing_len = (ctype->default_missing
+			 ? wcslen ((wchar_t *) ctype->default_missing)
+			 : 0);
 
+  init_locale_data (&file, nelems);
   for (elem = 0; elem < nelems; ++elem)
     {
       if (elem < _NL_ITEM_INDEX (_NL_CTYPE_EXTRA_MAP_1))
@@ -939,9 +969,7 @@ ctype_output (struct localedef_t *locale, const struct charmap_t *charmap,
 	  {
 #define CTYPE_EMPTY(name) \
 	  case name:							      \
-	    iov[2 + elem + offset].iov_base = NULL;			      \
-	    iov[2 + elem + offset].iov_len = 0;				      \
-	    idx[elem + 1] = idx[elem];					      \
+	    add_locale_empty (&file);					      \
 	    break
 
 	  CTYPE_EMPTY(_NL_CTYPE_GAP1);
@@ -951,273 +979,156 @@ ctype_output (struct localedef_t *locale, const struct charmap_t *charmap,
 	  CTYPE_EMPTY(_NL_CTYPE_GAP5);
 	  CTYPE_EMPTY(_NL_CTYPE_GAP6);
 
-#define CTYPE_DATA(name, base, len)					      \
+#define CTYPE_RAW_DATA(name, base, size)				      \
 	  case _NL_ITEM_INDEX (name):					      \
-	    iov[2 + elem + offset].iov_base = (base);			      \
-	    iov[2 + elem + offset].iov_len = (len);			      \
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;	      \
+	    add_locale_raw_data (&file, base, size);			      \
 	    break
 
-	  CTYPE_DATA (_NL_CTYPE_CLASS,
-		      ctype->ctype_b,
-		      (256 + 128) * sizeof (char_class_t));
+	  CTYPE_RAW_DATA (_NL_CTYPE_CLASS,
+			  ctype->ctype_b,
+			  (256 + 128) * sizeof (char_class_t));
 
-	  CTYPE_DATA (_NL_CTYPE_TOUPPER,
-		      ctype->map_b[0],
-		      (256 + 128) * sizeof (uint32_t));
-	  CTYPE_DATA (_NL_CTYPE_TOLOWER,
-		      ctype->map_b[1],
-		      (256 + 128) * sizeof (uint32_t));
-
-	  CTYPE_DATA (_NL_CTYPE_TOUPPER32,
-		      ctype->map32_b[0],
-		      256 * sizeof (uint32_t));
-	  CTYPE_DATA (_NL_CTYPE_TOLOWER32,
-		      ctype->map32_b[1],
-		      256 * sizeof (uint32_t));
-
-	  CTYPE_DATA (_NL_CTYPE_CLASS32,
-		      ctype->ctype32_b,
-		      256 * sizeof (char_class32_t));
+#define CTYPE_UINT32_ARRAY(name, base, n_elems)				      \
+	  case _NL_ITEM_INDEX (name):					      \
+	    add_locale_uint32_array (&file, base, n_elems);		      \
+	    break
 
-	  CTYPE_DATA (_NL_CTYPE_CLASS_OFFSET,
-		      &ctype->class_offset, sizeof (uint32_t));
+	  CTYPE_UINT32_ARRAY (_NL_CTYPE_TOUPPER, ctype->map_b[0], 256 + 128);
+	  CTYPE_UINT32_ARRAY (_NL_CTYPE_TOLOWER, ctype->map_b[1], 256 + 128);
+	  CTYPE_UINT32_ARRAY (_NL_CTYPE_TOUPPER32, ctype->map32_b[0], 256);
+	  CTYPE_UINT32_ARRAY (_NL_CTYPE_TOLOWER32, ctype->map32_b[1], 256);
+	  CTYPE_RAW_DATA (_NL_CTYPE_CLASS32,
+			  ctype->ctype32_b,
+			  256 * sizeof (char_class32_t));
 
-	  CTYPE_DATA (_NL_CTYPE_MAP_OFFSET,
-		      &ctype->map_offset, sizeof (uint32_t));
+#define CTYPE_UINT32(name, value)					      \
+	  case _NL_ITEM_INDEX (name):					      \
+	    add_locale_uint32 (&file, value);				      \
+	    break
 
-	  CTYPE_DATA (_NL_CTYPE_TRANSLIT_TAB_SIZE,
-		      &ctype->translit_idx_size, sizeof (uint32_t));
+	  CTYPE_UINT32 (_NL_CTYPE_CLASS_OFFSET, ctype->class_offset);
+	  CTYPE_UINT32 (_NL_CTYPE_MAP_OFFSET, ctype->map_offset);
+	  CTYPE_UINT32 (_NL_CTYPE_TRANSLIT_TAB_SIZE, ctype->translit_idx_size);
 
-	  CTYPE_DATA (_NL_CTYPE_TRANSLIT_FROM_IDX,
-		      ctype->translit_from_idx,
-		      ctype->translit_idx_size * sizeof (uint32_t));
+	  CTYPE_UINT32_ARRAY (_NL_CTYPE_TRANSLIT_FROM_IDX,
+			      ctype->translit_from_idx,
+			      ctype->translit_idx_size);
 
-	  CTYPE_DATA (_NL_CTYPE_TRANSLIT_FROM_TBL,
-		      ctype->translit_from_tbl,
-		      ctype->translit_from_tbl_size);
+	  CTYPE_UINT32_ARRAY (_NL_CTYPE_TRANSLIT_FROM_TBL,
+			      ctype->translit_from_tbl,
+			      ctype->translit_from_tbl_size
+			      / sizeof (uint32_t));
 
-	  CTYPE_DATA (_NL_CTYPE_TRANSLIT_TO_IDX,
-		      ctype->translit_to_idx,
-		      ctype->translit_idx_size * sizeof (uint32_t));
+	  CTYPE_UINT32_ARRAY (_NL_CTYPE_TRANSLIT_TO_IDX,
+			      ctype->translit_to_idx,
+			      ctype->translit_idx_size);
 
-	  CTYPE_DATA (_NL_CTYPE_TRANSLIT_TO_TBL,
-		      ctype->translit_to_tbl, ctype->translit_to_tbl_size);
+	  CTYPE_UINT32_ARRAY (_NL_CTYPE_TRANSLIT_TO_TBL,
+			      ctype->translit_to_tbl,
+			      ctype->translit_to_tbl_size / sizeof (uint32_t));
 
 	  case _NL_ITEM_INDEX (_NL_CTYPE_CLASS_NAMES):
 	    /* The class name array.  */
-	    total = 0;
-	    for (cnt = 0; cnt < ctype->nr_charclass; ++cnt, ++offset)
-	      {
-		iov[2 + elem + offset].iov_base
-		  = (void *) ctype->classnames[cnt];
-		iov[2 + elem + offset].iov_len
-		  = strlen (ctype->classnames[cnt]) + 1;
-		total += iov[2 + elem + offset].iov_len;
-	      }
-	    iov[2 + elem + offset].iov_base = (void *) nulbytes;
-	    iov[2 + elem + offset].iov_len = 4 - (total % 4);
-	    total += 4 - (total % 4);
-
-	    idx[elem + 1] = idx[elem] + total;
+	    start_locale_structure (&file);
+	    for (cnt = 0; cnt < ctype->nr_charclass; ++cnt)
+	      add_locale_string (&file, ctype->classnames[cnt]);
+	    add_locale_char (&file, 0);
+	    align_locale_data (&file, 4);
+	    end_locale_structure (&file);
 	    break;
 
 	  case _NL_ITEM_INDEX (_NL_CTYPE_MAP_NAMES):
 	    /* The class name array.  */
-	    total = 0;
-	    for (cnt = 0; cnt < ctype->map_collection_nr; ++cnt, ++offset)
-	      {
-		iov[2 + elem + offset].iov_base
-		  = (void *) ctype->mapnames[cnt];
-		iov[2 + elem + offset].iov_len
-		  = strlen (ctype->mapnames[cnt]) + 1;
-		total += iov[2 + elem + offset].iov_len;
-	      }
-	    iov[2 + elem + offset].iov_base = (void *) nulbytes;
-	    iov[2 + elem + offset].iov_len = 4 - (total % 4);
-	    total += 4 - (total % 4);
-
-	    idx[elem + 1] = idx[elem] + total;
+	    start_locale_structure (&file);
+	    for (cnt = 0; cnt < ctype->map_collection_nr; ++cnt)
+	      add_locale_string (&file, ctype->mapnames[cnt]);
+	    add_locale_char (&file, 0);
+	    align_locale_data (&file, 4);
+	    end_locale_structure (&file);
 	    break;
 
-	  CTYPE_DATA (_NL_CTYPE_WIDTH,
-		      ctype->width.iov_base,
-		      ctype->width.iov_len);
+	  case _NL_ITEM_INDEX (_NL_CTYPE_WIDTH):
+	    add_locale_wcwidth_table (&file, &ctype->width);
+	    break;
 
-	  CTYPE_DATA (_NL_CTYPE_MB_CUR_MAX,
-		      &ctype->mb_cur_max, sizeof (uint32_t));
+	  CTYPE_UINT32 (_NL_CTYPE_MB_CUR_MAX, ctype->mb_cur_max);
 
 	  case _NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME):
-	    total = strlen (ctype->codeset_name) + 1;
-	    if (total % 4 == 0)
-	      iov[2 + elem + offset].iov_base = (char *) ctype->codeset_name;
-	    else
-	      {
-		iov[2 + elem + offset].iov_base = alloca ((total + 3) & ~3);
-		memset (mempcpy (iov[2 + elem + offset].iov_base,
-				 ctype->codeset_name, total),
-			'\0', 4 - (total & 3));
-		total = (total + 3) & ~3;
-	      }
-	    iov[2 + elem + offset].iov_len = total;
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	    add_locale_string (&file, ctype->codeset_name);
 	    break;
 
+	  CTYPE_UINT32 (_NL_CTYPE_MAP_TO_NONASCII, ctype->to_nonascii);
 
-	  CTYPE_DATA (_NL_CTYPE_MAP_TO_NONASCII,
-		      &ctype->to_nonascii, sizeof (uint32_t));
-
-	  CTYPE_DATA (_NL_CTYPE_NONASCII_CASE,
-		      &ctype->nonascii_case, sizeof (uint32_t));
+	  CTYPE_UINT32 (_NL_CTYPE_NONASCII_CASE, ctype->nonascii_case);
 
 	  case _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS_MB_LEN):
-	    iov[2 + elem + offset].iov_base = alloca (sizeof (uint32_t));
-	    iov[2 + elem + offset].iov_len = sizeof (uint32_t);
-	    *(uint32_t *) iov[2 + elem + offset].iov_base =
-	      ctype->mbdigits_act / 10;
-	    idx[elem + 1] = idx[elem] + sizeof (uint32_t);
+	    add_locale_uint32 (&file, ctype->mbdigits_act / 10);
 	    break;
 
 	  case _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS_WC_LEN):
-	    /* Align entries.  */
-	    iov[2 + elem + offset].iov_base = (void *) nulbytes;
-	    iov[2 + elem + offset].iov_len = (4 - idx[elem] % 4) % 4;
-	    idx[elem] += iov[2 + elem + offset].iov_len;
-	    ++offset;
-
-	    iov[2 + elem + offset].iov_base = alloca (sizeof (uint32_t));
-	    iov[2 + elem + offset].iov_len = sizeof (uint32_t);
-	    *(uint32_t *) iov[2 + elem + offset].iov_base =
-	      ctype->wcdigits_act / 10;
-	    idx[elem + 1] = idx[elem] + sizeof (uint32_t);
+	    add_locale_uint32 (&file, ctype->wcdigits_act / 10);
 	    break;
 
 	  case _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS0_MB) ... _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS9_MB):
-	    /* Compute the length of all possible characters.  For INDIGITS
-	       there might be more than one.  We simply concatenate all of
-	       them with a NUL byte following.  The NUL byte wouldn't be
-	       necessary but it makes it easier for the user.  */
-	    total = 0;
-
-	    for (cnt = elem - _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS0_MB);
-		 cnt < ctype->mbdigits_act; cnt += 10)
-	      total += ctype->mbdigits[cnt]->nbytes + 1;
-	    iov[2 + elem + offset].iov_base = (char *) alloca (total);
-	    iov[2 + elem + offset].iov_len = total;
-
-	    cp = iov[2 + elem + offset].iov_base;
+	    start_locale_structure (&file);
 	    for (cnt = elem - _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS0_MB);
 		 cnt < ctype->mbdigits_act; cnt += 10)
 	      {
-		cp = mempcpy (cp, ctype->mbdigits[cnt]->bytes,
-			      ctype->mbdigits[cnt]->nbytes);
-		*cp++ = '\0';
+		add_locale_raw_data (&file, ctype->mbdigits[cnt]->bytes,
+				     ctype->mbdigits[cnt]->nbytes);
+		add_locale_char (&file, 0);
 	      }
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	    end_locale_structure (&file);
 	    break;
 
 	  case _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_MB) ... _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT9_MB):
-	    /* Compute the length of all possible characters.  For INDIGITS
-	       there might be more than one.  We simply concatenate all of
-	       them with a NUL byte following.  The NUL byte wouldn't be
-	       necessary but it makes it easier for the user.  */
+	    start_locale_structure (&file);
 	    cnt = elem - _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_MB);
-	    total = ctype->mboutdigits[cnt]->nbytes + 1;
-	    iov[2 + elem + offset].iov_base = (char *) alloca (total);
-	    iov[2 + elem + offset].iov_len = total;
-
-	    *(char *) mempcpy (iov[2 + elem + offset].iov_base,
-			       ctype->mboutdigits[cnt]->bytes,
-			       ctype->mboutdigits[cnt]->nbytes) = '\0';
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	    add_locale_raw_data (&file, ctype->mboutdigits[cnt]->bytes,
+				 ctype->mboutdigits[cnt]->nbytes);
+	    add_locale_char (&file, 0);
+	    end_locale_structure (&file);
 	    break;
 
 	  case _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS0_WC) ... _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS9_WC):
-	    total = ctype->wcdigits_act / 10;
-
-	    iov[2 + elem + offset].iov_base =
-	      (uint32_t *) alloca (total * sizeof (uint32_t));
-	    iov[2 + elem + offset].iov_len = total * sizeof (uint32_t);
-
+	    start_locale_structure (&file);
 	    for (cnt = elem - _NL_ITEM_INDEX (_NL_CTYPE_INDIGITS0_WC);
 		 cnt < ctype->wcdigits_act; cnt += 10)
-	      ((uint32_t *) iov[2 + elem + offset].iov_base)[cnt / 10]
-		= ctype->wcdigits[cnt];
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	      add_locale_uint32 (&file, ctype->wcdigits[cnt]);
+	    end_locale_structure (&file);
 	    break;
 
-	  case _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_WC):
-	    /* Align entries.  */
-	    iov[2 + elem + offset].iov_base = (void *) nulbytes;
-	    iov[2 + elem + offset].iov_len = (4 - idx[elem] % 4) % 4;
-	    idx[elem] += iov[2 + elem + offset].iov_len;
-	    ++offset;
-	    /* FALLTRHOUGH */
-
-	  case _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT1_WC) ... _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT9_WC):
+	  case _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_WC) ... _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT9_WC):
 	    cnt = elem - _NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_WC);
-	    iov[2 + elem + offset].iov_base = &ctype->wcoutdigits[cnt];
-	    iov[2 + elem + offset].iov_len = sizeof (uint32_t);
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	    add_locale_uint32 (&file, ctype->wcoutdigits[cnt]);
 	    break;
 
 	  case _NL_ITEM_INDEX(_NL_CTYPE_TRANSLIT_DEFAULT_MISSING_LEN):
-	    /* Align entries.  */
-	    iov[2 + elem + offset].iov_base = (void *) nulbytes;
-	    iov[2 + elem + offset].iov_len = (4 - idx[elem] % 4) % 4;
-	    idx[elem] += iov[2 + elem + offset].iov_len;
-	    ++offset;
-
-	    default_missing_len = (ctype->default_missing
-				   ? wcslen ((wchar_t *)ctype->default_missing)
-				   : 0);
-	    iov[2 + elem + offset].iov_base = &default_missing_len;
-	    iov[2 + elem + offset].iov_len = sizeof (uint32_t);
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	    add_locale_uint32 (&file, default_missing_len);
 	    break;
 
 	  case _NL_ITEM_INDEX(_NL_CTYPE_TRANSLIT_DEFAULT_MISSING):
-	    iov[2 + elem + offset].iov_base =
-	      ctype->default_missing ?: (uint32_t *) L"";
-	    iov[2 + elem + offset].iov_len =
-	      wcslen (iov[2 + elem + offset].iov_base) * sizeof (uint32_t);
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	    add_locale_uint32_array (&file, ctype->default_missing,
+				     default_missing_len);
 	    break;
 
 	  case _NL_ITEM_INDEX(_NL_CTYPE_TRANSLIT_IGNORE_LEN):
-	    /* Align entries.  */
-	    iov[2 + elem + offset].iov_base = (void *) nulbytes;
-	    iov[2 + elem + offset].iov_len = (4 - idx[elem] % 4) % 4;
-	    idx[elem] += iov[2 + elem + offset].iov_len;
-	    ++offset;
-
-	    iov[2 + elem + offset].iov_base = &ctype->ntranslit_ignore;
-	    iov[2 + elem + offset].iov_len = sizeof (uint32_t);
-	    idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	    add_locale_uint32 (&file, ctype->ntranslit_ignore);
 	    break;
 
 	  case _NL_ITEM_INDEX(_NL_CTYPE_TRANSLIT_IGNORE):
+	    start_locale_structure (&file);
 	    {
-	      uint32_t *ranges = (uint32_t *) alloca (ctype->ntranslit_ignore
-						      * 3 * sizeof (uint32_t));
 	      struct translit_ignore_t *runp;
-
-	      iov[2 + elem + offset].iov_base = ranges;
-	      iov[2 + elem + offset].iov_len = (ctype->ntranslit_ignore
-						* 3 * sizeof (uint32_t));
-
 	      for (runp = ctype->translit_ignore; runp != NULL;
 		   runp = runp->next)
 		{
-		  *ranges++ = runp->from;
-		  *ranges++ = runp->to;
-		  *ranges++ = runp->step;
+		  add_locale_uint32 (&file, runp->from);
+		  add_locale_uint32 (&file, runp->to);
+		  add_locale_uint32 (&file, runp->step);
 		}
 	    }
-	    /* Remove the following line in case a new entry is added
-	       after _NL_CTYPE_TRANSLIT_DEFAULT_MISSING_LEN.  */
-	    if (elem < nelems)
-	      idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
+	    end_locale_structure (&file);
 	    break;
 
 	  default:
@@ -1229,28 +1140,21 @@ ctype_output (struct localedef_t *locale, const struct charmap_t *charmap,
 	  size_t nr = elem - _NL_ITEM_INDEX (_NL_CTYPE_EXTRA_MAP_1);
 	  if (nr < ctype->nr_charclass)
 	    {
-	      iov[2 + elem + offset].iov_base = ctype->class_b[nr];
-	      iov[2 + elem + offset].iov_len = 256 / 32 * sizeof (uint32_t);
-	      idx[elem] += iov[2 + elem + offset].iov_len;
-	      ++offset;
-
-	      iov[2 + elem + offset] = ctype->class_3level[nr];
+	      start_locale_prelude (&file);
+	      add_locale_uint32_array (&file, ctype->class_b[nr], 256 / 32);
+	      end_locale_prelude (&file);
+	      add_locale_wctype_table (&file, &ctype->class_3level[nr]);
 	    }
 	  else
 	    {
 	      nr -= ctype->nr_charclass;
 	      assert (nr < ctype->map_collection_nr);
-	      iov[2 + elem + offset] = ctype->map_3level[nr];
+	      add_locale_wctrans_table (&file, &ctype->map_3level[nr]);
 	    }
-	  idx[elem + 1] = idx[elem] + iov[2 + elem + offset].iov_len;
 	}
     }
 
-  assert (2 + elem + offset == (nelems + 2 * ctype->nr_charclass
-				+ ctype->map_collection_nr + 4 + 2));
-
-  write_locale_data (output_path, LC_CTYPE, "LC_CTYPE", 2 + elem + offset,
-		     iov);
+  write_locale_data (output_path, LC_CTYPE, "LC_CTYPE", &file);
 }
 
 
@@ -3529,29 +3433,6 @@ no output digits defined and none of the standard names in the charmap")));
 }
 
 
-/* Construction of sparse 3-level tables.
-   See wchar-lookup.h for their structure and the meaning of p and q.  */
-
-struct wctype_table
-{
-  /* Parameters.  */
-  unsigned int p;
-  unsigned int q;
-  /* Working representation.  */
-  size_t level1_alloc;
-  size_t level1_size;
-  uint32_t *level1;
-  size_t level2_alloc;
-  size_t level2_size;
-  uint32_t *level2;
-  size_t level3_alloc;
-  size_t level3_size;
-  uint32_t *level3;
-  /* Compressed representation.  */
-  size_t result_size;
-  char *result;
-};
-
 /* Initialize.  Assumes t->p and t->q have already been set.  */
 static inline void
 wctype_table_init (struct wctype_table *t)
@@ -3657,12 +3538,12 @@ wctype_table_add (struct wctype_table *t, uint32_t wc)
 
 /* Finalize and shrink.  */
 static void
-wctype_table_finalize (struct wctype_table *t)
+add_locale_wctype_table (struct locale_file *file, struct wctype_table *t)
 {
   size_t i, j, k;
   uint32_t reorder3[t->level3_size];
   uint32_t reorder2[t->level2_size];
-  uint32_t level1_offset, level2_offset, level3_offset;
+  uint32_t level2_offset, level3_offset;
 
   /* Uniquify level3 blocks.  */
   k = 0;
@@ -3712,16 +3593,12 @@ wctype_table_finalize (struct wctype_table *t)
     if (t->level1[i] != EMPTY)
       t->level1[i] = reorder2[t->level1[i]];
 
-  /* Create and fill the resulting compressed representation.  */
   t->result_size =
     5 * sizeof (uint32_t)
     + t->level1_size * sizeof (uint32_t)
     + (t->level2_size << t->q) * sizeof (uint32_t)
     + (t->level3_size << t->p) * sizeof (uint32_t);
-  t->result = (char *) xmalloc (t->result_size);
 
-  level1_offset =
-    5 * sizeof (uint32_t);
   level2_offset =
     5 * sizeof (uint32_t)
     + t->level1_size * sizeof (uint32_t);
@@ -3730,26 +3607,29 @@ wctype_table_finalize (struct wctype_table *t)
     + t->level1_size * sizeof (uint32_t)
     + (t->level2_size << t->q) * sizeof (uint32_t);
 
-  ((uint32_t *) t->result)[0] = t->q + t->p + 5;
-  ((uint32_t *) t->result)[1] = t->level1_size;
-  ((uint32_t *) t->result)[2] = t->p + 5;
-  ((uint32_t *) t->result)[3] = (1 << t->q) - 1;
-  ((uint32_t *) t->result)[4] = (1 << t->p) - 1;
+  start_locale_structure (file);
+  add_locale_uint32 (file, t->q + t->p + 5);
+  add_locale_uint32 (file, t->level1_size);
+  add_locale_uint32 (file, t->p + 5);
+  add_locale_uint32 (file, (1 << t->q) - 1);
+  add_locale_uint32 (file, (1 << t->p) - 1);
 
   for (i = 0; i < t->level1_size; i++)
-    ((uint32_t *) (t->result + level1_offset))[i] =
-      (t->level1[i] == EMPTY
+    add_locale_uint32
+      (file,
+       t->level1[i] == EMPTY
        ? 0
        : (t->level1[i] << t->q) * sizeof (uint32_t) + level2_offset);
 
   for (i = 0; i < (t->level2_size << t->q); i++)
-    ((uint32_t *) (t->result + level2_offset))[i] =
-      (t->level2[i] == EMPTY
+    add_locale_uint32
+      (file,
+       t->level2[i] == EMPTY
        ? 0
        : (t->level2[i] << t->p) * sizeof (uint32_t) + level3_offset);
 
-  for (i = 0; i < (t->level3_size << t->p); i++)
-    ((uint32_t *) (t->result + level3_offset))[i] = t->level3[i];
+  add_locale_uint32_array (file, t->level3, t->level3_size << t->p);
+  end_locale_structure (file);
 
   if (t->level1_alloc > 0)
     free (t->level1);
@@ -3759,26 +3639,6 @@ wctype_table_finalize (struct wctype_table *t)
     free (t->level3);
 }
 
-#define TABLE wcwidth_table
-#define ELEMENT uint8_t
-#define DEFAULT 0xff
-#include "3level.h"
-
-#define TABLE wctrans_table
-#define ELEMENT int32_t
-#define DEFAULT 0
-#define wctrans_table_add wctrans_table_add_internal
-#include "3level.h"
-#undef wctrans_table_add
-/* The wctrans_table must actually store the difference between the
-   desired result and the argument.  */
-static inline void
-wctrans_table_add (struct wctrans_table *t, uint32_t wc, uint32_t mapped_wc)
-{
-  wctrans_table_add_internal (t, wc, mapped_wc - wc);
-}
-
-
 /* Flattens the included transliterations into a translit list.
    Inserts them in the list at `cursor', and returns the new cursor.  */
 static struct translit_t **
@@ -3855,8 +3715,8 @@ allocate_arrays (struct locale_ctype_t *ctype, const struct charmap_t *charmap,
   ctype->ctype32_b = (char_class32_t *) xcalloc (256, sizeof (char_class32_t));
   ctype->class_b = (uint32_t **)
     xmalloc (ctype->nr_charclass * sizeof (uint32_t *));
-  ctype->class_3level = (struct iovec *)
-    xmalloc (ctype->nr_charclass * sizeof (struct iovec));
+  ctype->class_3level = (struct wctype_table *)
+    xmalloc (ctype->nr_charclass * sizeof (struct wctype_table));
 
   /* This is the array accessed using the multibyte string elements.  */
   for (idx = 0; idx < 256; ++idx)
@@ -3888,34 +3748,30 @@ allocate_arrays (struct locale_ctype_t *ctype, const struct charmap_t *charmap,
 
   for (nr = 0; nr < ctype->nr_charclass; nr++)
     {
-      struct wctype_table t;
+      struct wctype_table *t;
 
-      t.p = 4; /* or: 5 */
-      t.q = 7; /* or: 6 */
-      wctype_table_init (&t);
+      t = &ctype->class_3level[nr];
+      t->p = 4; /* or: 5 */
+      t->q = 7; /* or: 6 */
+      wctype_table_init (t);
 
       for (idx = 0; idx < ctype->class_collection_act; ++idx)
 	if (ctype->class_collection[idx] & _ISwbit (nr))
-	  wctype_table_add (&t, ctype->charnames[idx]);
-
-      wctype_table_finalize (&t);
+	  wctype_table_add (t, ctype->charnames[idx]);
 
       if (verbose)
 	WITH_CUR_LOCALE (fprintf (stderr, _("\
 %s: table for class \"%s\": %lu bytes\n"),
 				 "LC_CTYPE", ctype->classnames[nr],
-				 (unsigned long int) t.result_size));
-
-      ctype->class_3level[nr].iov_base = t.result;
-      ctype->class_3level[nr].iov_len = t.result_size;
+				 (unsigned long int) t->result_size));
     }
 
   /* Room for table of mappings.  */
   ctype->map_b = (uint32_t **) xmalloc (2 * sizeof (uint32_t *));
   ctype->map32_b = (uint32_t **) xmalloc (ctype->map_collection_nr
 					  * sizeof (uint32_t *));
-  ctype->map_3level = (struct iovec *)
-    xmalloc (ctype->map_collection_nr * sizeof (struct iovec));
+  ctype->map_3level = (struct wctrans_table *)
+    xmalloc (ctype->map_collection_nr * sizeof (struct wctrans_table));
 
   /* Fill in all mappings.  */
   for (idx = 0; idx < 2; ++idx)
@@ -3956,27 +3812,23 @@ allocate_arrays (struct locale_ctype_t *ctype, const struct charmap_t *charmap,
 
   for (nr = 0; nr < ctype->map_collection_nr; nr++)
     {
-      struct wctrans_table t;
+      struct wctrans_table *t;
 
-      t.p = 7;
-      t.q = 9;
-      wctrans_table_init (&t);
+      t = &ctype->map_3level[nr];
+      t->p = 7;
+      t->q = 9;
+      wctrans_table_init (t);
 
       for (idx = 0; idx < ctype->map_collection_act[nr]; ++idx)
 	if (ctype->map_collection[nr][idx] != 0)
-	  wctrans_table_add (&t, ctype->charnames[idx],
+	  wctrans_table_add (t, ctype->charnames[idx],
 			     ctype->map_collection[nr][idx]);
 
-      wctrans_table_finalize (&t);
-
       if (verbose)
 	WITH_CUR_LOCALE (fprintf (stderr, _("\
 %s: table for map \"%s\": %lu bytes\n"),
 				 "LC_CTYPE", ctype->mapnames[nr],
-				 (unsigned long int) t.result_size));
-
-      ctype->map_3level[nr].iov_base = t.result;
-      ctype->map_3level[nr].iov_len = t.result_size;
+				 (unsigned long int) t->result_size));
     }
 
   /* Extra array for class and map names.  */
@@ -3996,11 +3848,12 @@ allocate_arrays (struct locale_ctype_t *ctype, const struct charmap_t *charmap,
      saves a run-time check.
      But we put L'\0' in the table.  This again saves a run-time check.  */
   {
-    struct wcwidth_table t;
+    struct wcwidth_table *t;
 
-    t.p = 7;
-    t.q = 9;
-    wcwidth_table_init (&t);
+    t = &ctype->width;
+    t->p = 7;
+    t->q = 9;
+    wcwidth_table_init (t);
 
     /* First set all the printable characters of the character set to
        the default width.  */
@@ -4020,7 +3873,7 @@ allocate_arrays (struct locale_ctype_t *ctype, const struct charmap_t *charmap,
 			&ctype->class_collection_act, data->ucs4);
 
 	    if (class_bits != NULL && (*class_bits & BITw (tok_print)))
-	      wcwidth_table_add (&t, data->ucs4, charmap->width_default);
+	      wcwidth_table_add (t, data->ucs4, charmap->width_default);
 	  }
       }
 
@@ -4068,7 +3921,7 @@ allocate_arrays (struct locale_ctype_t *ctype, const struct charmap_t *charmap,
 				&ctype->class_collection_act, wch);
 
 		    if (class_bits != NULL && (*class_bits & BITw (tok_print)))
-		      wcwidth_table_add (&t, wch,
+		      wcwidth_table_add (t, wch,
 					 charmap->width_rules[cnt].width);
 		  }
 
@@ -4098,16 +3951,11 @@ allocate_arrays (struct locale_ctype_t *ctype, const struct charmap_t *charmap,
       }
 
     /* Set the width of L'\0' to 0.  */
-    wcwidth_table_add (&t, 0, 0);
-
-    wcwidth_table_finalize (&t);
+    wcwidth_table_add (t, 0, 0);
 
     if (verbose)
       WITH_CUR_LOCALE (fprintf (stderr, _("%s: table for width: %lu bytes\n"),
-			       "LC_CTYPE", (unsigned long int) t.result_size));
-
-    ctype->width.iov_base = t.result;
-    ctype->width.iov_len = t.result_size;
+			       "LC_CTYPE", (unsigned long int) t->result_size));
   }
 
   /* Set MB_CUR_MAX.  */
diff --git a/locale/programs/ld-identification.c b/locale/programs/ld-identification.c
index 4b03fb6987..5487aae7ce 100644
--- a/locale/programs/ld-identification.c
+++ b/locale/programs/ld-identification.c
@@ -182,116 +182,32 @@ identification_output (struct localedef_t *locale,
 {
   struct locale_identification_t *identification
     = locale->categories[LC_IDENTIFICATION].identification;
-  struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION)
-		  + (__LC_LAST - 2)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION)];
-  size_t cnt = 0;
+  struct locale_file file;
   size_t num;
-  size_t last_idx;
-
-  data.magic = LIMAGIC (LC_IDENTIFICATION);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
-  iov[cnt].iov_base = (void *) identification->title;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->source;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->address;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->contact;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->email;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->tel;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->fax;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->language;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->territory;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->audience;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->application;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->abbreviation;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->revision;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) identification->date;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  last_idx = cnt - 1;
-  idx[last_idx] = idx[cnt - 2];
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION));
+  add_locale_string (&file, identification->title);
+  add_locale_string (&file, identification->source);
+  add_locale_string (&file, identification->address);
+  add_locale_string (&file, identification->contact);
+  add_locale_string (&file, identification->email);
+  add_locale_string (&file, identification->tel);
+  add_locale_string (&file, identification->fax);
+  add_locale_string (&file, identification->language);
+  add_locale_string (&file, identification->territory);
+  add_locale_string (&file, identification->audience);
+  add_locale_string (&file, identification->application);
+  add_locale_string (&file, identification->abbreviation);
+  add_locale_string (&file, identification->revision);
+  add_locale_string (&file, identification->date);
+  start_locale_structure (&file);
   for (num = 0; num < __LC_LAST; ++num)
     if (num != LC_ALL)
-      {
-	iov[cnt].iov_base = (void *) identification->category[num];
-	iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-	idx[last_idx] += iov[cnt].iov_len;
-	++cnt;
-      }
-
-  assert (last_idx == _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION) - 1);
-  iov[cnt].iov_base = (void *) charmap->code_set_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  assert (cnt == (2 + _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION)
-		  + (__LC_LAST - 2)));
-
-  write_locale_data (output_path, LC_IDENTIFICATION, "LC_IDENTIFICATION", cnt,
-		     iov);
+      add_locale_string (&file, identification->category[num]);
+  end_locale_structure (&file);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_IDENTIFICATION, "LC_IDENTIFICATION",
+		     &file);
 }
 
 
diff --git a/locale/programs/ld-measurement.c b/locale/programs/ld-measurement.c
index 4aa0ea99a7..5be54e739d 100644
--- a/locale/programs/ld-measurement.c
+++ b/locale/programs/ld-measurement.c
@@ -122,35 +122,12 @@ measurement_output (struct localedef_t *locale,
 {
   struct locale_measurement_t *measurement =
     locale->categories[LC_MEASUREMENT].measurement;
-  struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_MEASUREMENT)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_MEASUREMENT)];
-  size_t cnt = 0;
-
-  data.magic = LIMAGIC (LC_MEASUREMENT);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_MEASUREMENT);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
-  iov[cnt].iov_base = &measurement->measurement;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) charmap->code_set_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  assert (cnt == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MEASUREMENT));
-
-  write_locale_data (output_path, LC_MEASUREMENT, "LC_MEASUREMENT",
-		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_MEASUREMENT), iov);
+  struct locale_file file;
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_MEASUREMENT));
+  add_locale_char (&file, measurement->measurement);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_MEASUREMENT, "LC_MEASUREMENT", &file);
 }
 
 
diff --git a/locale/programs/ld-messages.c b/locale/programs/ld-messages.c
index f1b9f93f26..116f3a2bb6 100644
--- a/locale/programs/ld-messages.c
+++ b/locale/programs/ld-messages.c
@@ -184,49 +184,15 @@ messages_output (struct localedef_t *locale, const struct charmap_t *charmap,
 {
   struct locale_messages_t *messages
     = locale->categories[LC_MESSAGES].messages;
-  struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES)];
-  size_t cnt = 0;
-
-  data.magic = LIMAGIC (LC_MESSAGES);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
-  iov[cnt].iov_base = (char *) messages->yesexpr;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (char *) messages->noexpr;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (char *) messages->yesstr;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (char *) messages->nostr;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (char *) charmap->code_set_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-
-  assert (cnt + 1 == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES));
-
-  write_locale_data (output_path, LC_MESSAGES, "LC_MESSAGES",
-		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES), iov);
+  struct locale_file file;
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES));
+  add_locale_string (&file, messages->yesexpr);
+  add_locale_string (&file, messages->noexpr);
+  add_locale_string (&file, messages->yesstr);
+  add_locale_string (&file, messages->nostr);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_MESSAGES, "LC_MESSAGES", &file);
 }
 
 
diff --git a/locale/programs/ld-monetary.c b/locale/programs/ld-monetary.c
index 4dfca3dd7b..ec86223029 100644
--- a/locale/programs/ld-monetary.c
+++ b/locale/programs/ld-monetary.c
@@ -364,262 +364,57 @@ monetary_output (struct localedef_t *locale, const struct charmap_t *charmap,
 {
   struct locale_monetary_t *monetary
     = locale->categories[LC_MONETARY].monetary;
-  struct iovec iov[3 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_MONETARY)];
-  size_t cnt = 0;
-
-  data.magic = LIMAGIC (LC_MONETARY);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->int_curr_symbol;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->currency_symbol;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->mon_decimal_point;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->mon_thousands_sep;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = monetary->mon_grouping;
-  iov[cnt].iov_len = monetary->mon_grouping_len;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->positive_sign;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->negative_sign;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->int_frac_digits;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->frac_digits;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->p_cs_precedes;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->p_sep_by_space;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->n_cs_precedes;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->n_sep_by_space;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->p_sign_posn;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->n_sign_posn;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->crncystr;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->int_p_cs_precedes;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->int_p_sep_by_space;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->int_n_cs_precedes;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->int_n_sep_by_space;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->int_p_sign_posn;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->int_n_sign_posn;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->duo_int_curr_symbol;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->duo_currency_symbol;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_int_frac_digits;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_frac_digits;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_p_cs_precedes;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_p_sep_by_space;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_n_cs_precedes;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_n_sep_by_space;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_int_p_cs_precedes;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_int_p_sep_by_space;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_int_n_cs_precedes;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_int_n_sep_by_space;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_p_sign_posn;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_n_sign_posn;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_int_p_sign_posn;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_int_n_sign_posn;
-  iov[cnt].iov_len = 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-
-  /* Align following data */
-  iov[cnt].iov_base = (void *) "\0\0";
-  iov[cnt].iov_len = ((idx[cnt - 2] + 3) & ~3) - idx[cnt - 2];
-  idx[cnt - 2] = (idx[cnt - 2] + 3) & ~3;
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) &monetary->uno_valid_from;
-  iov[cnt].iov_len = sizeof(uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->uno_valid_to;
-  iov[cnt].iov_len = sizeof(uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_valid_from;
-  iov[cnt].iov_len = sizeof(uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->duo_valid_to;
-  iov[cnt].iov_len = sizeof(uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) monetary->conversion_rate;
-  iov[cnt].iov_len = 2 * sizeof(uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->mon_decimal_point_wc;
-  iov[cnt].iov_len = sizeof (uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &monetary->mon_thousands_sep_wc;
-  iov[cnt].iov_len = sizeof (uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) charmap->code_set_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  assert (cnt == 3 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY));
-
-  write_locale_data (output_path, LC_MONETARY, "LC_MONETARY",
-		     3 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY), iov);
+  struct locale_file file;
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY));
+  add_locale_string (&file, monetary->int_curr_symbol);
+  add_locale_string (&file, monetary->currency_symbol);
+  add_locale_string (&file, monetary->mon_decimal_point);
+  add_locale_string (&file, monetary->mon_thousands_sep);
+  add_locale_raw_data (&file, monetary->mon_grouping,
+		       monetary->mon_grouping_len);
+  add_locale_string (&file, monetary->positive_sign);
+  add_locale_string (&file, monetary->negative_sign);
+  add_locale_char (&file, monetary->int_frac_digits);
+  add_locale_char (&file, monetary->frac_digits);
+  add_locale_char (&file, monetary->p_cs_precedes);
+  add_locale_char (&file, monetary->p_sep_by_space);
+  add_locale_char (&file, monetary->n_cs_precedes);
+  add_locale_char (&file, monetary->n_sep_by_space);
+  add_locale_char (&file, monetary->p_sign_posn);
+  add_locale_char (&file, monetary->n_sign_posn);
+  add_locale_string (&file, monetary->crncystr);
+  add_locale_char (&file, monetary->int_p_cs_precedes);
+  add_locale_char (&file, monetary->int_p_sep_by_space);
+  add_locale_char (&file, monetary->int_n_cs_precedes);
+  add_locale_char (&file, monetary->int_n_sep_by_space);
+  add_locale_char (&file, monetary->int_p_sign_posn);
+  add_locale_char (&file, monetary->int_n_sign_posn);
+  add_locale_string (&file, monetary->duo_int_curr_symbol);
+  add_locale_string (&file, monetary->duo_currency_symbol);
+  add_locale_char (&file, monetary->duo_int_frac_digits);
+  add_locale_char (&file, monetary->duo_frac_digits);
+  add_locale_char (&file, monetary->duo_p_cs_precedes);
+  add_locale_char (&file, monetary->duo_p_sep_by_space);
+  add_locale_char (&file, monetary->duo_n_cs_precedes);
+  add_locale_char (&file, monetary->duo_n_sep_by_space);
+  add_locale_char (&file, monetary->duo_int_p_cs_precedes);
+  add_locale_char (&file, monetary->duo_int_p_sep_by_space);
+  add_locale_char (&file, monetary->duo_int_n_cs_precedes);
+  add_locale_char (&file, monetary->duo_int_n_sep_by_space);
+  add_locale_char (&file, monetary->duo_p_sign_posn);
+  add_locale_char (&file, monetary->duo_n_sign_posn);
+  add_locale_char (&file, monetary->duo_int_p_sign_posn);
+  add_locale_char (&file, monetary->duo_int_n_sign_posn);
+  add_locale_uint32 (&file, monetary->uno_valid_from);
+  add_locale_uint32 (&file, monetary->uno_valid_to);
+  add_locale_uint32 (&file, monetary->duo_valid_from);
+  add_locale_uint32 (&file, monetary->duo_valid_to);
+  add_locale_uint32_array (&file, monetary->conversion_rate, 2);
+  add_locale_uint32 (&file, monetary->mon_decimal_point_wc);
+  add_locale_uint32 (&file, monetary->mon_thousands_sep_wc);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_MONETARY, "LC_MONETARY", &file);
 }
 
 
diff --git a/locale/programs/ld-name.c b/locale/programs/ld-name.c
index 207bf0194e..efc541e47d 100644
--- a/locale/programs/ld-name.c
+++ b/locale/programs/ld-name.c
@@ -157,60 +157,17 @@ name_output (struct localedef_t *locale, const struct charmap_t *charmap,
 	     const char *output_path)
 {
   struct locale_name_t *name = locale->categories[LC_NAME].name;
-  struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_NAME)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_NAME)];
-  size_t cnt = 0;
-
-  data.magic = LIMAGIC (LC_NAME);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_NAME);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
-  iov[cnt].iov_base = (void *) name->name_fmt;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) name->name_gen;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) name->name_mr;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) name->name_mrs;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) name->name_miss;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) name->name_ms;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) charmap->code_set_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  assert (cnt == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_NAME));
-
-  write_locale_data (output_path, LC_NAME, "LC_NAME",
-		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_NAME), iov);
+  struct locale_file file;
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_NAME));
+  add_locale_string (&file, name->name_fmt);
+  add_locale_string (&file, name->name_gen);
+  add_locale_string (&file, name->name_mr);
+  add_locale_string (&file, name->name_mrs);
+  add_locale_string (&file, name->name_miss);
+  add_locale_string (&file, name->name_ms);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_NAME, "LC_NAME", &file);
 }
 
 
diff --git a/locale/programs/ld-numeric.c b/locale/programs/ld-numeric.c
index 929409cb7a..093a04951e 100644
--- a/locale/programs/ld-numeric.c
+++ b/locale/programs/ld-numeric.c
@@ -133,61 +133,16 @@ numeric_output (struct localedef_t *locale, const struct charmap_t *charmap,
 		const char *output_path)
 {
   struct locale_numeric_t *numeric = locale->categories[LC_NUMERIC].numeric;
-  struct iovec iov[3 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC)];
-  size_t cnt = 0;
-
-  data.magic = LIMAGIC (LC_NUMERIC);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
-  iov[cnt].iov_base = (void *) (numeric->decimal_point ?: "");
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) (numeric->thousands_sep ?: "");
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = numeric->grouping;
-  iov[cnt].iov_len = numeric->grouping_len;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-
-  /* Align following data */
-  iov[cnt].iov_base = (void *) "\0\0";
-  iov[cnt].iov_len = ((idx[cnt - 2] + 3) & ~3) - idx[cnt - 2];
-  idx[cnt - 2] = (idx[cnt - 2] + 3) & ~3;
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) &numeric->decimal_point_wc;
-  iov[cnt].iov_len = sizeof (uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) &numeric->thousands_sep_wc;
-  iov[cnt].iov_len = sizeof (uint32_t);
-  ++cnt;
-
-  idx[cnt - 3] = idx[cnt - 4] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) charmap->code_set_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-
-  assert (cnt + 1 == 3 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC));
-
-  write_locale_data (output_path, LC_NUMERIC, "LC_NUMERIC",
-		     3 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC), iov);
+  struct locale_file file;
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC));
+  add_locale_string (&file, numeric->decimal_point ?: "");
+  add_locale_string (&file, numeric->thousands_sep ?: "");
+  add_locale_raw_data (&file, numeric->grouping, numeric->grouping_len);
+  add_locale_uint32 (&file, numeric->decimal_point_wc);
+  add_locale_uint32 (&file, numeric->thousands_sep_wc);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_NUMERIC, "LC_NUMERIC", &file);
 }
 
 
diff --git a/locale/programs/ld-paper.c b/locale/programs/ld-paper.c
index c6239dfd9d..595a600543 100644
--- a/locale/programs/ld-paper.c
+++ b/locale/programs/ld-paper.c
@@ -121,40 +121,13 @@ paper_output (struct localedef_t *locale, const struct charmap_t *charmap,
 	      const char *output_path)
 {
   struct locale_paper_t *paper = locale->categories[LC_PAPER].paper;
-  struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_PAPER)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_PAPER)];
-  size_t cnt = 0;
-
-  data.magic = LIMAGIC (LC_PAPER);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_PAPER);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[cnt - 2].iov_len + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = &paper->height;
-  iov[cnt].iov_len = 4;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = &paper->width;
-  iov[cnt].iov_len = 4;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) charmap->code_set_name;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  assert (cnt == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_PAPER));
-
-  write_locale_data (output_path, LC_PAPER, "LC_PAPER",
-		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_PAPER), iov);
+  struct locale_file file;
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_PAPER));
+  add_locale_uint32 (&file, paper->height);
+  add_locale_uint32 (&file, paper->width);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_PAPER, "LC_PAPER", &file);
 }
 
 
diff --git a/locale/programs/ld-telephone.c b/locale/programs/ld-telephone.c
index 4452750921..3e71a36eb8 100644
--- a/locale/programs/ld-telephone.c
+++ b/locale/programs/ld-telephone.c
@@ -175,50 +175,15 @@ telephone_output (struct localedef_t *locale, const struct charmap_t *charmap,
 {
   struct locale_telephone_t *telephone =
     locale->categories[LC_TELEPHONE].telephone;
-  struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_TELEPHONE)];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TELEPHONE)];
-  size_t cnt = 0;
-
-  data.magic = LIMAGIC (LC_TELEPHONE);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_TELEPHONE);
-  iov[cnt].iov_base = (void *) &data;
-  iov[cnt].iov_len = sizeof (data);
-  ++cnt;
-
-  iov[cnt].iov_base = (void *) idx;
-  iov[cnt].iov_len = sizeof (idx);
-  ++cnt;
-
-  idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
-  iov[cnt].iov_base = (void *) telephone->tel_int_fmt;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) telephone->tel_dom_fmt;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) telephone->int_select;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) telephone->int_prefix;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
-  iov[cnt].iov_base = (void *) charmap->code_set_name;;
-  iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
-  ++cnt;
-
-  assert (cnt == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_TELEPHONE));
-
-  write_locale_data (output_path, LC_TELEPHONE, "LC_TELEPHONE",
-		     2 + _NL_ITEM_INDEX (_NL_NUM_LC_TELEPHONE), iov);
+  struct locale_file file;
+
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_TELEPHONE));
+  add_locale_string (&file, telephone->tel_int_fmt);
+  add_locale_string (&file, telephone->tel_dom_fmt);
+  add_locale_string (&file, telephone->int_select);
+  add_locale_string (&file, telephone->int_prefix);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_TELEPHONE, "LC_TELEPHONE", &file);
 }
 
 
diff --git a/locale/programs/ld-time.c b/locale/programs/ld-time.c
index d3646b8717..5956cb32cf 100644
--- a/locale/programs/ld-time.c
+++ b/locale/programs/ld-time.c
@@ -539,394 +539,116 @@ time_output (struct localedef_t *locale, const struct charmap_t *charmap,
 	     const char *output_path)
 {
   struct locale_time_t *time = locale->categories[LC_TIME].time;
-  struct iovec *iov = alloca (sizeof *iov
-			      * (2 + _NL_ITEM_INDEX (_NL_NUM_LC_TIME)
-				 + time->num_era - 1
-				 + 2 * 99
-				 + 2 + time->num_era * 10));
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TIME)];
-  size_t cnt, last_idx, num, n;
+  struct locale_file file;
+  size_t num, n;
 
-  data.magic = LIMAGIC (LC_TIME);
-  data.n = _NL_ITEM_INDEX (_NL_NUM_LC_TIME);
-  iov[0].iov_base = (void *) &data;
-  iov[0].iov_len = sizeof (data);
-
-  iov[1].iov_base = (void *) idx;
-  iov[1].iov_len = sizeof (idx);
-
-  idx[0] = iov[0].iov_len + iov[1].iov_len;
+  init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_TIME));
 
   /* The ab'days.  */
-  for (cnt = 0; cnt <= _NL_ITEM_INDEX (ABDAY_7); ++cnt)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->abday[cnt - _NL_ITEM_INDEX (ABDAY_1)] ?: "");
-      iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-      idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 7; ++n)
+    add_locale_string (&file, time->abday[n] ?: "");
 
   /* The days.  */
-  for (; cnt <= _NL_ITEM_INDEX (DAY_7); ++cnt)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->day[cnt - _NL_ITEM_INDEX (DAY_1)] ?: "");
-      iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-      idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 7; ++n)
+    add_locale_string (&file, time->day[n] ?: "");
 
   /* The ab'mons.  */
-  for (; cnt <= _NL_ITEM_INDEX (ABMON_12); ++cnt)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->abmon[cnt - _NL_ITEM_INDEX (ABMON_1)] ?: "");
-      iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-      idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 12; ++n)
+    add_locale_string (&file, time->abmon[n] ?: "");
 
   /* The mons.  */
-  for (; cnt <= _NL_ITEM_INDEX (MON_12); ++cnt)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->mon[cnt - _NL_ITEM_INDEX (MON_1)] ?: "");
-      iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-      idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 12; ++n)
+    add_locale_string (&file, time->mon[n] ?: "");
 
   /* AM/PM.  */
-  for (; cnt <= _NL_ITEM_INDEX (PM_STR); ++cnt)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->am_pm[cnt - _NL_ITEM_INDEX (AM_STR)] ?: "");
-      iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-      idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 2; ++n)
+    add_locale_string (&file, time->am_pm[n]);
 
-  iov[2 + cnt].iov_base = (void *) (time->d_t_fmt ?: "");
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
+  add_locale_string (&file, time->d_t_fmt ?: "");
+  add_locale_string (&file, time->d_fmt ?: "");
+  add_locale_string (&file, time->t_fmt ?: "");
+  add_locale_string (&file, time->t_fmt_ampm ?: "");
 
-  iov[2 + cnt].iov_base = (void *) (time->d_fmt ?: "");
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
+  start_locale_structure (&file);
+  for (num = 0; num < time->num_era; ++num)
+    add_locale_string (&file, time->era[num]);
+  end_locale_structure (&file);
 
-  iov[2 + cnt].iov_base = (void *) (time->t_fmt ?: "");
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  ++cnt;
+  add_locale_string (&file, time->era_year ?: "");
+  add_locale_string (&file, time->era_d_fmt ?: "");
 
-  iov[2 + cnt].iov_base = (void *) (time->t_fmt_ampm ?: "");
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
-  last_idx = ++cnt;
+  start_locale_structure (&file);
+  for (num = 0; num < 100; ++num)
+    add_locale_string (&file, time->alt_digits[num] ?: "");
+  end_locale_structure (&file);
 
-  idx[1 + last_idx] = idx[last_idx];
-  for (num = 0; num < time->num_era; ++num, ++cnt)
-    {
-      iov[2 + cnt].iov_base = (void *) time->era[num];
-      iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-      idx[1 + last_idx] += iov[2 + cnt].iov_len;
-    }
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->era_year ?: "");
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->era_d_fmt ?: "");
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  idx[1 + last_idx] = idx[last_idx];
-  for (num = 0; num < 100; ++num, ++cnt)
-    {
-      iov[2 + cnt].iov_base = (void *) (time->alt_digits[num] ?: "");
-      iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-      idx[1 + last_idx] += iov[2 + cnt].iov_len;
-    }
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->era_d_t_fmt ?: "");
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->era_t_fmt ?: "");
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-
-  /* We must align the following data.  */
-  iov[2 + cnt].iov_base = (void *) "\0\0";
-  iov[2 + cnt].iov_len = ((idx[last_idx] + 3) & ~3) - idx[last_idx];
-  idx[last_idx] = (idx[last_idx] + 3) & ~3;
-  ++cnt;
-
-  /* The `era' data in usable form.  */
-  iov[2 + cnt].iov_base = (void *) &time->num_era;
-  iov[2 + cnt].iov_len = sizeof (uint32_t);
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  idx[1 + last_idx] = idx[last_idx];
+  add_locale_string (&file, time->era_d_t_fmt ?: "");
+  add_locale_string (&file, time->era_t_fmt ?: "");
+  add_locale_uint32 (&file, time->num_era);
+
+  start_locale_structure (&file);
   for (num = 0; num < time->num_era; ++num)
     {
-      size_t l, l2;
-
-      iov[2 + cnt].iov_base = (void *) &time->era_entries[num].direction;
-      iov[2 + cnt].iov_len = sizeof (int32_t);
-      ++cnt;
-      iov[2 + cnt].iov_base = (void *) &time->era_entries[num].offset;
-      iov[2 + cnt].iov_len = sizeof (int32_t);
-      ++cnt;
-      iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[0];
-      iov[2 + cnt].iov_len = sizeof (int32_t);
-      ++cnt;
-      iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[1];
-      iov[2 + cnt].iov_len = sizeof (int32_t);
-      ++cnt;
-      iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[2];
-      iov[2 + cnt].iov_len = sizeof (int32_t);
-      ++cnt;
-      iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[0];
-      iov[2 + cnt].iov_len = sizeof (int32_t);
-      ++cnt;
-      iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[1];
-      iov[2 + cnt].iov_len = sizeof (int32_t);
-      ++cnt;
-      iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[2];
-      iov[2 + cnt].iov_len = sizeof (int32_t);
-      ++cnt;
-
-      l = ((char *) rawmemchr (time->era_entries[num].format, '\0')
-	   - time->era_entries[num].name) + 1;
-      l2 = (l + 3) & ~3;
-      iov[2 + cnt].iov_base = alloca (l2);
-      memset (mempcpy (iov[2 + cnt].iov_base, time->era_entries[num].name, l),
-	      '\0', l2 - l);
-      iov[2 + cnt].iov_len = l2;
-      ++cnt;
-
-      idx[1 + last_idx] += 8 * sizeof (int32_t) + l2;
-
-      assert (idx[1 + last_idx] % 4 == 0);
-
-      iov[2 + cnt].iov_base = (void *) time->era_entries[num].wname;
-      iov[2 + cnt].iov_len = ((wcschr ((wchar_t *) time->era_entries[num].wformat, L'\0')
-			       - (wchar_t *) time->era_entries[num].wname + 1)
-			      * sizeof (uint32_t));
-      idx[1 + last_idx] += iov[2 + cnt].iov_len;
-      ++cnt;
+      add_locale_uint32 (&file, time->era_entries[num].direction);
+      add_locale_uint32 (&file, time->era_entries[num].offset);
+      add_locale_uint32 (&file, time->era_entries[num].start_date[0]);
+      add_locale_uint32 (&file, time->era_entries[num].start_date[1]);
+      add_locale_uint32 (&file, time->era_entries[num].start_date[2]);
+      add_locale_uint32 (&file, time->era_entries[num].stop_date[0]);
+      add_locale_uint32 (&file, time->era_entries[num].stop_date[1]);
+      add_locale_uint32 (&file, time->era_entries[num].stop_date[2]);
+      add_locale_string (&file, time->era_entries[num].name);
+      add_locale_string (&file, time->era_entries[num].format);
+      add_locale_wstring (&file, time->era_entries[num].wname);
+      add_locale_wstring (&file, time->era_entries[num].wformat);
     }
-  ++last_idx;
+  end_locale_structure (&file);
 
   /* The wide character ab'days.  */
-  for (n = 0; n < 7; ++n, ++cnt, ++last_idx)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->wabday[n] ?: empty_wstr);
-      iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			      * sizeof (uint32_t));
-      idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 7; ++n)
+    add_locale_wstring (&file, time->wabday[n] ?: empty_wstr);
 
   /* The wide character days.  */
-  for (n = 0; n < 7; ++n, ++cnt, ++last_idx)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->wday[n] ?: empty_wstr);
-      iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			      * sizeof (uint32_t));
-      idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 7; ++n)
+    add_locale_wstring (&file, time->wday[n] ?: empty_wstr);
 
   /* The wide character ab'mons.  */
-  for (n = 0; n < 12; ++n, ++cnt, ++last_idx)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->wabmon[n] ?: empty_wstr);
-      iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			      * sizeof (uint32_t));
-      idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 12; ++n)
+    add_locale_wstring (&file, time->wabmon[n] ?: empty_wstr);
 
   /* The wide character mons.  */
-  for (n = 0; n < 12; ++n, ++cnt, ++last_idx)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->wmon[n] ?: empty_wstr);
-      iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			      * sizeof (uint32_t));
-      idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-    }
+  for (n = 0; n < 12; ++n)
+    add_locale_wstring (&file, time->wmon[n] ?: empty_wstr);
 
   /* Wide character AM/PM.  */
-  for (n = 0; n < 2; ++n, ++cnt, ++last_idx)
-    {
-      iov[2 + cnt].iov_base =
-	(void *) (time->wam_pm[n] ?: empty_wstr);
-      iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			      * sizeof (uint32_t));
-      idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-    }
-
-  iov[2 + cnt].iov_base = (void *) (time->wd_t_fmt ?: empty_wstr);
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			  * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->wd_fmt ?: empty_wstr);
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			  * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->wt_fmt ?: empty_wstr);
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			  * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->wt_fmt_ampm ?: empty_wstr);
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			  * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->wera_year ?: empty_wstr);
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			  * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->wera_d_fmt ?: empty_wstr);
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			  * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  idx[1 + last_idx] = idx[last_idx];
-  for (num = 0; num < 100; ++num, ++cnt)
-    {
-      iov[2 + cnt].iov_base = (void *) (time->walt_digits[num]
-					?: empty_wstr);
-      iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			      * sizeof (uint32_t));
-      idx[1 + last_idx] += iov[2 + cnt].iov_len;
-    }
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->wera_d_t_fmt ?: empty_wstr);
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			  * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) (time->wera_t_fmt ?: empty_wstr);
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-			  * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) &time->week_ndays;
-  iov[2 + cnt].iov_len = 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  /* We must align the following data.  */
-  iov[2 + cnt].iov_base = (void *) "\0\0";
-  iov[2 + cnt].iov_len = ((idx[last_idx] + 3) & ~3) - idx[last_idx];
-  idx[last_idx] = (idx[last_idx] + 3) & ~3;
-  ++cnt;
-
-  iov[2 + cnt].iov_base = (void *) &time->week_1stday;
-  iov[2 + cnt].iov_len = sizeof(uint32_t);
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) &time->week_1stweek;
-  iov[2 + cnt].iov_len = 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) &time->first_weekday;
-  iov[2 + cnt].iov_len = 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) &time->first_workday;
-  iov[2 + cnt].iov_len = 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) &time->cal_direction;
-  iov[2 + cnt].iov_len = 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) time->timezone;
-  iov[2 + cnt].iov_len = strlen (time->timezone) + 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) time->date_fmt;
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  /* We must align the following data.  */
-  iov[2 + cnt].iov_base = (void *) "\0\0";
-  iov[2 + cnt].iov_len = -idx[last_idx] & 3;
-  idx[last_idx] += -idx[last_idx] & 3;
-  ++cnt;
-
-  iov[2 + cnt].iov_base = (void *) time->wdate_fmt;
-  iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
-                          * sizeof (uint32_t));
-  idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
-  ++cnt;
-  ++last_idx;
-
-  iov[2 + cnt].iov_base = (void *) charmap->code_set_name;
-  iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
-  ++cnt;
-  ++last_idx;
-
-  assert (cnt == (_NL_ITEM_INDEX (_NL_NUM_LC_TIME)
-		  + time->num_era - 1
-		  + 2 * 99
-		  + 2 + time->num_era * 10));
-  assert (last_idx  == _NL_ITEM_INDEX (_NL_NUM_LC_TIME));
-
-  write_locale_data (output_path, LC_TIME, "LC_TIME", 2 + cnt, iov);
+  for (n = 0; n < 2; ++n)
+    add_locale_wstring (&file, time->wam_pm[n] ?: empty_wstr);
+
+  add_locale_wstring (&file, time->wd_t_fmt ?: empty_wstr);
+  add_locale_wstring (&file, time->wd_fmt ?: empty_wstr);
+  add_locale_wstring (&file, time->wt_fmt ?: empty_wstr);
+  add_locale_wstring (&file, time->wt_fmt_ampm ?: empty_wstr);
+  add_locale_wstring (&file, time->wera_year ?: empty_wstr);
+  add_locale_wstring (&file, time->wera_d_fmt ?: empty_wstr);
+
+  start_locale_structure (&file);
+  for (num = 0; num < 100; ++num)
+    add_locale_wstring (&file, time->walt_digits[num] ?: empty_wstr);
+  end_locale_structure (&file);
+
+  add_locale_wstring (&file, time->wera_d_t_fmt ?: empty_wstr);
+  add_locale_wstring (&file, time->wera_t_fmt ?: empty_wstr);
+  add_locale_char (&file, time->week_ndays);
+  add_locale_uint32 (&file, time->week_1stday);
+  add_locale_char (&file, time->week_1stweek);
+  add_locale_char (&file, time->first_weekday);
+  add_locale_char (&file, time->first_workday);
+  add_locale_char (&file, time->cal_direction);
+  add_locale_string (&file, time->timezone);
+  add_locale_string (&file, time->date_fmt);
+  add_locale_wstring (&file, time->wdate_fmt);
+  add_locale_string (&file, charmap->code_set_name);
+  write_locale_data (output_path, LC_TIME, "LC_TIME", &file);
 }
 
 
diff --git a/locale/programs/locfile.c b/locale/programs/locfile.c
index 3cbd6f1314..cb53bbc406 100644
--- a/locale/programs/locfile.c
+++ b/locale/programs/locfile.c
@@ -27,14 +27,19 @@
 #include <unistd.h>
 #include <sys/param.h>
 #include <sys/stat.h>
+#include <assert.h>
+#include <wchar.h>
 
 #include "../../crypt/md5.h"
 #include "localedef.h"
+#include "localeinfo.h"
 #include "locfile.h"
 #include "simple-hash.h"
 
 #include "locfile-kw.h"
 
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
 
 /* Temporary storage of the locale data before writing it to the archive.  */
 static locale_data_t to_archive;
@@ -533,17 +538,177 @@ compare_files (const char *filename1, const char *filename2, size_t size,
   return ret;
 }
 
+/* When called outside a start_locale_structure/end_locale_structure
+   or start_locale_prelude/end_locale_prelude block, record that the
+   next byte in FILE's obstack will be the first byte of a new element.
+   Do likewise for the first call inside a start_locale_structure/
+   end_locale_structure block.  */
+static void
+record_offset (struct locale_file *file)
+{
+  if (file->structure_stage < 2)
+    {
+      assert (file->next_element < file->n_elements);
+      file->offsets[file->next_element++]
+	= (obstack_object_size (&file->data)
+	   + (file->n_elements + 2) * sizeof (uint32_t));
+      if (file->structure_stage == 1)
+	file->structure_stage = 2;
+    }
+}
+
+/* Initialize FILE for a new output file.  N_ELEMENTS is the number
+   of elements in the file.  */
+void
+init_locale_data (struct locale_file *file, size_t n_elements)
+{
+  file->n_elements = n_elements;
+  file->next_element = 0;
+  file->offsets = xmalloc (sizeof (uint32_t) * n_elements);
+  obstack_init (&file->data);
+  file->structure_stage = 0;
+}
+
+/* Align the size of FILE's obstack object to BOUNDARY bytes.  */
+void
+align_locale_data (struct locale_file *file, size_t boundary)
+{
+  size_t size = -obstack_object_size (&file->data) & (boundary - 1);
+  obstack_blank (&file->data, size);
+  memset (obstack_next_free (&file->data) - size, 0, size);
+}
+
+/* Record that FILE's next element contains no data.  */
+void
+add_locale_empty (struct locale_file *file)
+{
+  record_offset (file);
+}
 
-/* Write a locale file, with contents given by N_ELEM and VEC.  */
+/* Record that FILE's next element consists of SIZE bytes starting at DATA.  */
+void
+add_locale_raw_data (struct locale_file *file, const void *data, size_t size)
+{
+  record_offset (file);
+  obstack_grow (&file->data, data, size);
+}
+
+/* Finish the current object on OBSTACK and use it as the data for FILE's
+   next element.  */
+void
+add_locale_raw_obstack (struct locale_file *file, struct obstack *obstack)
+{
+  size_t size = obstack_object_size (obstack);
+  record_offset (file);
+  obstack_grow (&file->data, obstack_finish (obstack), size);
+}
+
+/* Use STRING as FILE's next element.  */
+void
+add_locale_string (struct locale_file *file, const char *string)
+{
+  record_offset (file);
+  obstack_grow (&file->data, string, strlen (string) + 1);
+}
+
+/* Likewise for wide strings.  */
+void
+add_locale_wstring (struct locale_file *file, const uint32_t *string)
+{
+  add_locale_uint32_array (file, string, wcslen ((const wchar_t *) string) + 1);
+}
+
+/* Record that FILE's next element is the 32-bit integer VALUE.  */
+void
+add_locale_uint32 (struct locale_file *file, uint32_t value)
+{
+  align_locale_data (file, sizeof (uint32_t));
+  record_offset (file);
+  obstack_grow (&file->data, &value, sizeof (value));
+}
+
+/* Record that FILE's next element is an array of N_ELEMS integers
+   starting at DATA.  */
+void
+add_locale_uint32_array (struct locale_file *file,
+			 const uint32_t *data, size_t n_elems)
+{
+  align_locale_data (file, sizeof (uint32_t));
+  record_offset (file);
+  obstack_grow (&file->data, data, n_elems * sizeof (uint32_t));
+}
+
+/* Record that FILE's next element is the single byte given by VALUE.  */
+void
+add_locale_char (struct locale_file *file, char value)
+{
+  record_offset (file);
+  obstack_1grow (&file->data, value);
+}
+
+/* Start building an element that contains several different pieces of data.
+   Subsequent calls to add_locale_* will add data to the same element up
+   till the next call to end_locale_structure.  The element's alignment
+   is dictated by the first piece of data added to it.  */
+void
+start_locale_structure (struct locale_file *file)
+{
+  assert (file->structure_stage == 0);
+  file->structure_stage = 1;
+}
+
+/* Finish a structure element that was started by start_locale_structure.
+   Empty structures are OK and behave like add_locale_empty.  */
+void
+end_locale_structure (struct locale_file *file)
+{
+  record_offset (file);
+  assert (file->structure_stage == 2);
+  file->structure_stage = 0;
+}
+
+/* Start building data that goes before the next element's recorded offset.
+   Subsequent calls to add_locale_* will add data to the file without
+   treating any of it as the start of a new element.  Calling
+   end_locale_prelude switches back to the usual behavior.  */
+void
+start_locale_prelude (struct locale_file *file)
+{
+  assert (file->structure_stage == 0);
+  file->structure_stage = 3;
+}
+
+/* End a block started by start_locale_prelude.  */
+void
+end_locale_prelude (struct locale_file *file)
+{
+  assert (file->structure_stage == 3);
+  file->structure_stage = 0;
+}
+
+/* Write a locale file, with contents given by FILE.  */
 void
 write_locale_data (const char *output_path, int catidx, const char *category,
-		   size_t n_elem, struct iovec *vec)
+		   struct locale_file *file)
 {
   size_t cnt, step, maxiov;
   int fd;
   char *fname;
   const char **other_paths;
-
+  uint32_t header[2];
+  size_t n_elem;
+  struct iovec vec[3];
+
+  assert (file->n_elements == file->next_element);
+  header[0] = LIMAGIC (catidx);
+  header[1] = file->n_elements;
+  vec[0].iov_len = sizeof (header);
+  vec[0].iov_base = header;
+  vec[1].iov_len = sizeof (uint32_t) * file->n_elements;
+  vec[1].iov_base = file->offsets;
+  vec[2].iov_len = obstack_object_size (&file->data);
+  vec[2].iov_base = obstack_finish (&file->data);
+  n_elem = 3;
   if (! no_archive)
     {
       /* The data will be added to the archive.  For now we simply
diff --git a/locale/programs/locfile.h b/locale/programs/locfile.h
index 83bf421ece..bdd87cc426 100644
--- a/locale/programs/locfile.h
+++ b/locale/programs/locfile.h
@@ -21,15 +21,17 @@
 #include <stdint.h>
 #include <sys/uio.h>
 
+#include "obstack.h"
 #include "linereader.h"
 #include "localedef.h"
 
-
-/* Header of the locale data files.  */
+/* Structure for storing the contents of a category file.  */
 struct locale_file
 {
-  int magic;
-  int n;
+  size_t n_elements, next_element;
+  uint32_t *offsets;
+  struct obstack data;
+  int structure_stage;
 };
 
 
@@ -66,9 +68,26 @@ extern void write_all_categories (struct localedef_t *definitions,
 				  const char *output_path);
 
 /* Write out the data.  */
+extern void init_locale_data (struct locale_file *file, size_t n_elements);
+extern void align_locale_data (struct locale_file *file, size_t boundary);
+extern void add_locale_empty (struct locale_file *file);
+extern void add_locale_raw_data (struct locale_file *file, const void *data,
+				 size_t size);
+extern void add_locale_raw_obstack (struct locale_file *file,
+				    struct obstack *obstack);
+extern void add_locale_string (struct locale_file *file, const char *string);
+extern void add_locale_wstring (struct locale_file *file,
+				const uint32_t *string);
+extern void add_locale_uint32 (struct locale_file *file, uint32_t value);
+extern void add_locale_uint32_array (struct locale_file *file,
+				     const uint32_t *data, size_t n_elems);
+extern void add_locale_char (struct locale_file *file, char value);
+extern void start_locale_structure (struct locale_file *file);
+extern void end_locale_structure (struct locale_file *file);
+extern void start_locale_prelude (struct locale_file *file);
+extern void end_locale_prelude (struct locale_file *file);
 extern void write_locale_data (const char *output_path, int catidx,
-			       const char *category, size_t n_elem,
-			       struct iovec *vec);
+			       const char *category, struct locale_file *file);
 
 
 /* Entrypoints for the parsers of the individual categories.  */