]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - locale/programs/ld-time.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / locale / programs / ld-time.c
index 2ff56b00344c889d62796716393a72002593f738..a1721e05c6f5838b45cab70d3a966a7aec391d3d 100644 (file)
@@ -1,21 +1,19 @@
-/* Copyright (C) 1995-1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+/* Copyright (C) 1995-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@gnu.org>, 1995.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
@@ -26,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <wchar.h>
+#include <stdint.h>
 #include <sys/uio.h>
 
 #include <assert.h>
@@ -92,6 +91,12 @@ struct locale_time_t
   const char *date_fmt;
   const uint32_t *wdate_fmt;
   int alt_digits_defined;
+  const char *alt_mon[12];
+  const uint32_t *walt_mon[12];
+  int alt_mon_defined;
+  const char *ab_alt_mon[12];
+  const uint32_t *wab_alt_mon[12];
+  int ab_alt_mon_defined;
   unsigned char week_ndays;
   uint32_t week_1stday;
   unsigned char week_1stweek;
@@ -156,9 +161,8 @@ time_finish (struct localedef_t *locale, const struct charmap_t *charmap)
         empty one.  */
       if (time == NULL)
        {
-         if (! be_quiet)
-           WITH_CUR_LOCALE (error (0, 0, _("\
-No definition for %s category found"), "LC_TIME"));
+         record_warning (_("\
+No definition for %s category found"), "LC_TIME");
          time_startup (NULL, locale, 0);
          time = locale->categories[LC_TIME].time;
          nothing = 1;
@@ -170,11 +174,11 @@ No definition for %s category found"), "LC_TIME"));
   if (!time->cat##_defined)                                                  \
     {                                                                        \
       const char *initval[] = { noparen val };                               \
-      int i;                                                                 \
+      unsigned int i;                                                        \
                                                                              \
-      if (! be_quiet && ! nothing)                                           \
-       WITH_CUR_LOCALE (error (0, 0, _("%s: field `%s' not defined"),        \
-                               "LC_TIME", #cat));                            \
+      if (! nothing)                                                         \
+       record_error (0, 0, _("%s: field `%s' not defined"),                  \
+                     "LC_TIME", #cat);                                       \
                                                                              \
       for (i = 0; i < sizeof (initval) / sizeof (initval[0]); ++i)           \
        time->cat[i] = initval[i];                                            \
@@ -193,9 +197,9 @@ No definition for %s category found"), "LC_TIME"));
 #define TEST_ELEM(cat, initval) \
   if (time->cat == NULL)                                                     \
     {                                                                        \
-      if (! be_quiet && ! nothing)                                           \
-       WITH_CUR_LOCALE (error (0, 0, _("%s: field `%s' not defined"),        \
-                               "LC_TIME", #cat));                            \
+      if (! nothing)                                                         \
+       record_error (0, 0, _("%s: field `%s' not defined"),                  \
+                     "LC_TIME", #cat);                                       \
                                                                              \
       time->cat = initval;                                                   \
     }
@@ -244,10 +248,9 @@ No definition for %s category found"), "LC_TIME"));
          /* First character must be + or - for the direction.  */
          if (*str != '+' && *str != '-')
            {
-             if (!be_quiet)
-               WITH_CUR_LOCALE (error (0, 0, _("\
+             record_error (0, 0, _("\
 %s: direction flag in string %Zd in `era' field is not '+' nor '-'"),
-                                       "LC_TIME", idx + 1));
+                           "LC_TIME", idx + 1);
              /* Default arbitrarily to '+'.  */
              time->era_entries[idx].direction = '+';
            }
@@ -255,10 +258,9 @@ No definition for %s category found"), "LC_TIME"));
            time->era_entries[idx].direction = *str;
          if (*++str != ':')
            {
-             if (!be_quiet)
-               WITH_CUR_LOCALE (error (0, 0, _("\
+             record_error (0, 0, _("\
 %s: direction flag in string %Zd in `era' field is not a single character"),
-                                       "LC_TIME", idx + 1));
+                           "LC_TIME", idx + 1);
              (void) strsep (&str, ":");
            }
          else
@@ -268,18 +270,16 @@ No definition for %s category found"), "LC_TIME"));
          time->era_entries[idx].offset = strtol (str, &endp, 10);
          if (endp == str)
            {
-             if (!be_quiet)
-               WITH_CUR_LOCALE (error (0, 0, _("\
+             record_error (0, 0, _("\
 %s: invalid number for offset in string %Zd in `era' field"),
-                                       "LC_TIME", idx + 1));
+                           "LC_TIME", idx + 1);
              (void) strsep (&str, ":");
            }
          else if (*endp != ':')
            {
-             if (!be_quiet)
-               WITH_CUR_LOCALE (error (0, 0, _("\
+             record_error (0, 0, _("\
 %s: garbage at end of offset value in string %Zd in `era' field"),
-                                       "LC_TIME", idx + 1));
+                           "LC_TIME", idx + 1);
              (void) strsep (&str, ":");
            }
          else
@@ -327,19 +327,17 @@ No definition for %s category found"), "LC_TIME"));
              if (endp == str)
                {
                invalid_start_date:
-                 if (!be_quiet)
-                   WITH_CUR_LOCALE (error (0, 0, _("\
+                 record_error (0, 0, _("\
 %s: invalid starting date in string %Zd in `era' field"),
-                                           "LC_TIME", idx + 1));
+                               "LC_TIME", idx + 1);
                  (void) strsep (&str, ":");
                }
              else if (*endp != ':')
                {
                garbage_start_date:
-                 if (!be_quiet)
-                   WITH_CUR_LOCALE (error (0, 0, _("\
+                 record_error (0, 0, _("\
 %s: garbage at end of starting date in string %Zd in `era' field "),
-                                           "LC_TIME", idx + 1));
+                               "LC_TIME", idx + 1);
                  (void) strsep (&str, ":");
                }
              else
@@ -354,11 +352,10 @@ No definition for %s category found"), "LC_TIME"));
                           > days_per_month[time->era_entries[idx].start_date[1]])
                       || (time->era_entries[idx].start_date[1] == 2
                           && time->era_entries[idx].start_date[2] == 29
-                          && !__isleap (time->era_entries[idx].start_date[0])))
-                     && !be_quiet)
-                         WITH_CUR_LOCALE (error (0, 0, _("\
+                          && !__isleap (time->era_entries[idx].start_date[0]))))
+                   record_error (0, 0, _("\
 %s: starting date is invalid in string %Zd in `era' field"),
-                                                 "LC_TIME", idx + 1));
+                                 "LC_TIME", idx + 1);
                }
            }
 
@@ -404,19 +401,17 @@ No definition for %s category found"), "LC_TIME"));
              if (endp == str)
                {
                invalid_stop_date:
-                 if (!be_quiet)
-                   WITH_CUR_LOCALE (error (0, 0, _("\
+                 record_error (0, 0, _("\
 %s: invalid stopping date in string %Zd in `era' field"),
-                                           "LC_TIME", idx + 1));
+                               "LC_TIME", idx + 1);
                  (void) strsep (&str, ":");
                }
              else if (*endp != ':')
                {
                garbage_stop_date:
-                 if (!be_quiet)
-                   WITH_CUR_LOCALE (error (0, 0, _("\
+                 record_error (0, 0, _("\
 %s: garbage at end of stopping date in string %Zd in `era' field"),
-                                           "LC_TIME", idx + 1));
+                               "LC_TIME", idx + 1);
                  (void) strsep (&str, ":");
                }
              else
@@ -431,19 +426,17 @@ No definition for %s category found"), "LC_TIME"));
                           > days_per_month[time->era_entries[idx].stop_date[1]])
                       || (time->era_entries[idx].stop_date[1] == 2
                           && time->era_entries[idx].stop_date[2] == 29
-                          && !__isleap (time->era_entries[idx].stop_date[0])))
-                     && !be_quiet)
-                         WITH_CUR_LOCALE (error (0, 0, _("\
-%s: stopping date is invalid in string %Zd in `era' field"),
-                                                 "LC_TIME", idx + 1));
+                          && !__isleap (time->era_entries[idx].stop_date[0]))))
+                   record_error (0, 0, _("\
+%s: invalid stopping date in string %Zd in `era' field"),
+                                 "LC_TIME", idx + 1);
                }
            }
 
          if (str == NULL || *str == '\0')
            {
-             if (!be_quiet)
-               WITH_CUR_LOCALE (error (0, 0, _("\
-%s: missing era name in string %Zd in `era' field"), "LC_TIME", idx + 1));
+             record_error (0, 0, _("\
+%s: missing era name in string %Zd in `era' field"), "LC_TIME", idx + 1);
              time->era_entries[idx].name =
                time->era_entries[idx].format = "";
            }
@@ -453,10 +446,9 @@ No definition for %s category found"), "LC_TIME"));
 
              if (str == NULL || *str == '\0')
                {
-                 if (!be_quiet)
-                   WITH_CUR_LOCALE (error (0, 0, _("\
+                 record_error (0, 0, _("\
 %s: missing era format in string %Zd in `era' field"),
-                                           "LC_TIME", idx + 1));
+                               "LC_TIME", idx + 1);
                  time->era_entries[idx].name =
                    time->era_entries[idx].format = "";
                }
@@ -469,47 +461,63 @@ No definition for %s category found"), "LC_TIME"));
          wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end offset */
          wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end start */
          wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end end */
-         time->era_entries[idx].wname = (uint32_t *) wstr + 1;
-         wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end name */
-         *wstr = L'\0';
-         time->era_entries[idx].wformat = (uint32_t *) wstr + 1;
+         if (wstr != NULL)
+           {
+             time->era_entries[idx].wname = (uint32_t *) wstr + 1;
+             wstr = wcschr (wstr + 1, L':');   /* end name */
+             if (wstr != NULL)
+               {
+                 *wstr = L'\0';
+                 time->era_entries[idx].wformat = (uint32_t *) wstr + 1;
+               }
+             else
+               time->era_entries[idx].wname =
+                 time->era_entries[idx].wformat = (uint32_t *) L"";
+           }
+         else
+           time->era_entries[idx].wname =
+             time->era_entries[idx].wformat = (uint32_t *) L"";
        }
     }
 
+  /* Set up defaults based on ISO 30112 WD10 [2014].  */
   if (time->week_ndays == 0)
     time->week_ndays = 7;
 
   if (time->week_1stday == 0)
     time->week_1stday = 19971130;
 
+  if (time->week_1stweek == 0)
+    time->week_1stweek = 7;
+
   if (time->week_1stweek > time->week_ndays)
-    WITH_CUR_LOCALE (error (0, 0, _("\
+    record_error (0, 0, _("\
 %s: third operand for value of field `%s' must not be larger than %d"),
-                           "LC_TIME", "week", 7));
+                 "LC_TIME", "week", 7);
 
   if (time->first_weekday == '\0')
     /* The definition does not specify this so the default is used.  */
     time->first_weekday = 1;
   else if (time->first_weekday > time->week_ndays)
-    WITH_CUR_LOCALE (error (0, 0, _("\
-%s: values of field `%s' must not be larger than %d"),
-                           "LC_TIME", "first_weekday", 7));
+    record_error (0, 0, _("\
+%s: values for field `%s' must not be larger than %d"),
+                 "LC_TIME", "first_weekday", 7);
 
   if (time->first_workday == '\0')
     /* The definition does not specify this so the default is used.  */
-    time->first_workday = 1;
+    time->first_workday = 2;
   else if (time->first_workday > time->week_ndays)
-    WITH_CUR_LOCALE (error (0, 0, _("\
-%s: values of field `%s' must not be larger than %d"),
-                           "LC_TIME", "first_workday", 7));
+    record_error (0, 0, _("\
+%s: values for field `%s' must not be larger than %d"),
+                 "LC_TIME", "first_workday", 7);
 
   if (time->cal_direction == '\0')
     /* The definition does not specify this so the default is used.  */
     time->cal_direction = 1;
   else if (time->cal_direction > 3)
-    WITH_CUR_LOCALE (error (0, 0, _("\
+    record_error (0, 0, _("\
 %s: values for field `%s' must not be larger than %d"),
-                           "LC_TIME", "cal_direction", 3));
+                 "LC_TIME", "cal_direction", 3);
 
   /* XXX We don't perform any tests on the timezone value since this is
      simply useless, stupid $&$!@...  */
@@ -528,385 +536,133 @@ 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[2 + _NL_ITEM_INDEX (_NL_NUM_LC_TIME)
-                 + time->num_era - 1
-                 + 2 * 99
-                 + 2 + time->num_era * 10 - 1];
-  struct locale_file data;
-  uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TIME)];
-  size_t cnt, last_idx, 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);
+  struct locale_file file;
+  size_t num, n;
 
-  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;
-
-      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 = (strchr (time->era_entries[num].format, '\0')
-          - time->era_entries[num].name) + 1;
-      l = (l + 3) & ~3;
-      iov[2 + cnt].iov_base = (void *) time->era_entries[num].name;
-      iov[2 + cnt].iov_len = l;
-      ++cnt;
-
-      idx[1 + last_idx] += 8 * sizeof (int32_t) + l;
-
-      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;
-
-  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 - 1));
-  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);
+
+  /* The alt'mons.  */
+  for (n = 0; n < 12; ++n)
+    add_locale_string (&file, time->alt_mon[n] ?: "");
+
+  /* The wide character alt'mons.  */
+  for (n = 0; n < 12; ++n)
+    add_locale_wstring (&file, time->walt_mon[n] ?: empty_wstr);
+
+  /* The ab'alt'mons.  */
+  for (n = 0; n < 12; ++n)
+    add_locale_string (&file, time->ab_alt_mon[n] ?: "");
+
+  /* The wide character ab'alt'mons.  */
+  for (n = 0; n < 12; ++n)
+    add_locale_wstring (&file, time->wab_alt_mon[n] ?: empty_wstr);
+
+  write_locale_data (output_path, LC_TIME, "LC_TIME", &file);
 }
 
 
@@ -1020,7 +776,7 @@ time_read (struct linereader *ldfile, struct localedef_t *result,
                  time->cat[cnt] = "";                                        \
                  time->w##cat[cnt++] = empty_wstr;                           \
                }                                                             \
-                                                                             \
+                                                                             \
              if (now->tok == tok_semicolon)                                  \
                {                                                             \
                  now = lr_token (ldfile, charmap, result, repertoire,        \
@@ -1049,6 +805,8 @@ time_read (struct linereader *ldfile, struct localedef_t *result,
          STRARR_ELEM (mon, 12, 12);
          STRARR_ELEM (am_pm, 2, 2);
          STRARR_ELEM (alt_digits, 0, 100);
+         STRARR_ELEM (alt_mon, 12, 12);
+         STRARR_ELEM (ab_alt_mon, 12, 12);
 
        case tok_era:
          /* Ignore the rest of the line if we don't need the input of
@@ -1201,6 +959,21 @@ time_read (struct linereader *ldfile, struct localedef_t *result,
            lr_error (ldfile, _("\
 %1$s: definition does not end with `END %1$s'"), "LC_TIME");
          lr_ignore_rest (ldfile, now->tok == tok_lc_time);
+
+         /* If alt_mon was not specified, make it a copy of mon.  */
+         if (!ignore_content && !time->alt_mon_defined)
+           {
+             memcpy (time->alt_mon, time->mon, sizeof (time->mon));
+             memcpy (time->walt_mon, time->wmon, sizeof (time->wmon));
+             time->alt_mon_defined = 1;
+           }
+         /* The same for abbreviated versions.  */
+         if (!ignore_content && !time->ab_alt_mon_defined)
+           {
+             memcpy (time->ab_alt_mon, time->abmon, sizeof (time->abmon));
+             memcpy (time->wab_alt_mon, time->wabmon, sizeof (time->wabmon));
+             time->ab_alt_mon_defined = 1;
+           }
          return;
 
        default: