]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - locale/programs/locfile.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / locale / programs / locfile.c
index 1e8e434ee11ac61b38d4ec17e7c6c68e2f58fc1d..f719c496c0a94ab6e2f197c9436130305249038e 100644 (file)
@@ -1,21 +1,19 @@
-/* Copyright (C) 1996-2001, 2002, 2003 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
 
-   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>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #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;
@@ -64,11 +68,12 @@ locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
          char *i18npath = getenv ("I18NPATH");
          if (i18npath != NULL && *i18npath != '\0')
            {
-             char path[strlen (filename) + 1 + strlen (i18npath)
-                       + sizeof ("/locales/") - 1];
+             const size_t pathlen = strlen (i18npath);
+             char i18npathbuf[pathlen + 1];
+             char path[strlen (filename) + 1 + pathlen
+                       + sizeof ("/locales/") - 1];
              char *next;
-             i18npath = strdupa (i18npath);
-
+             i18npath = memcpy (i18npathbuf, i18npath, pathlen + 1);
 
              while (ldfile == NULL
                     && (next = strsep (&i18npath, ":")) != NULL)
@@ -79,7 +84,7 @@ locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
 
                  if (ldfile == NULL)
                    {
-                     stpcpy (stpcpy (path, next), filename);
+                     stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
 
                      ldfile = lr_open (path, locfile_hash);
                    }
@@ -156,10 +161,11 @@ argument to `%s' must be a single character"),
 
          if (repertoire_name == NULL)
            {
-             repertoire_name = memcpy (xmalloc (arg->val.str.lenmb + 1),
-                                       arg->val.str.startmb,
-                                       arg->val.str.lenmb);
-             ((char *) repertoire_name)[arg->val.str.lenmb] = '\0';
+             char *newp = alloca (arg->val.str.lenmb + 1);
+
+             *((char *) mempcpy (newp, arg->val.str.startmb,
+                                 arg->val.str.lenmb)) = '\0';
+             repertoire_name = newp;
            }
          break;
 
@@ -337,6 +343,7 @@ write_all_categories (struct localedef_t *definitions,
 
       /* Open the archive.  This call never returns if we cannot
         successfully open the archive.  */
+      ah.fname = NULL;
       open_archive (&ah, false);
 
       if (add_locale_to_archive (&ah, locname, to_archive, true) != 0)
@@ -355,7 +362,7 @@ siblings_uncached (const char *output_path)
 {
   size_t len;
   char *base, *p;
-  struct stat output_stat;
+  struct stat64 output_stat;
   DIR *dirp;
   int nelems;
   const char **elems;
@@ -378,7 +385,7 @@ siblings_uncached (const char *output_path)
   len = p - base;
 
   /* Get the properties of output_path.  */
-  if (lstat (output_path, &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
+  if (lstat64 (output_path, &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
     return NULL;
 
   /* Iterate through the directories in base directory.  */
@@ -392,7 +399,7 @@ siblings_uncached (const char *output_path)
       struct dirent64 *other_dentry;
       const char *other_name;
       char *other_path;
-      struct stat other_stat;
+      struct stat64 other_stat;
 
       other_dentry = readdir64 (dirp);
       if (other_dentry == NULL)
@@ -407,7 +414,7 @@ siblings_uncached (const char *output_path)
       other_path[len] = '/';
       strcpy (other_path + len + 1, other_name);
 
-      if (lstat (other_path, &other_stat) >= 0
+      if (lstat64 (other_path, &other_stat) >= 0
          && S_ISDIR (other_stat.st_mode)
          && other_stat.st_uid == output_stat.st_uid
          && other_stat.st_gid == output_stat.st_gid
@@ -533,17 +540,185 @@ compare_files (const char *filename1, const char *filename2, size_t size,
   return ret;
 }
 
+/* True if the locale files use the opposite endianness to the
+   machine running localedef.  */
+bool swap_endianness_p;
+
+/* 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);
+}
+
+/* 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, LOCFILE_ALIGN);
+  record_offset (file);
+  value = maybe_swap_uint32 (value);
+  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, LOCFILE_ALIGN);
+  record_offset (file);
+  obstack_grow (&file->data, data, n_elems * sizeof (uint32_t));
+  maybe_swap_uint32_obstack (&file->data, n_elems);
+}
+
+/* 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;
+}
 
-/* Write a locale file, with contents given by N_ELEM and VEC.  */
+/* 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;
-
+  const char **other_paths = NULL;
+  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);
+  maybe_swap_uint32_array (vec[0].iov_base, 2);
+  maybe_swap_uint32_array (vec[1].iov_base, file->n_elements);
+  n_elem = 3;
   if (! no_archive)
     {
       /* The data will be added to the archive.  For now we simply
@@ -580,9 +755,9 @@ write_locale_data (const char *output_path, int catidx, const char *category,
   fd = -2;
   if (strcmp (category, "LC_MESSAGES") == 0)
     {
-      struct stat st;
+      struct stat64 st;
 
-      if (stat (fname, &st) < 0)
+      if (stat64 (fname, &st) < 0)
        {
          if (mkdir (fname, 0777) >= 0)
            {
@@ -621,9 +796,8 @@ write_locale_data (const char *output_path, int catidx, const char *category,
 
       if (fd == -1)
        {
-         if (!be_quiet)
-           WITH_CUR_LOCALE (error (0, save_err, _("\
-cannot open output file `%s' for category `%s'"), fname, category));
+         record_error (0, save_err, _("\
+cannot open output file `%s' for category `%s'"), fname, category);
          free (fname);
          return;
        }
@@ -645,23 +819,35 @@ cannot open output file `%s' for category `%s'"), fname, category));
 
       if (writev (fd, &vec[cnt], step) < 0)
        {
-         if (!be_quiet)
-           WITH_CUR_LOCALE (error (0, errno, _("\
-failure while writing data for category `%s'"), category));
+         record_error (0, errno, _("\
+failure while writing data for category `%s'"), category);
          break;
        }
     }
 
   close (fd);
 
-  /* Compare the file with the locale data files for the same category in
-     other locales, and see if we can reuse it, to save disk space.  */
-  other_paths = siblings (output_path);
+  /* Compare the file with the locale data files for the same category
+     in other locales, and see if we can reuse it, to save disk space.
+     If the user specified --no-hard-links to localedef then hard_links
+     is false, other_paths remains NULL and we skip the optimization
+     below.  The use of --no-hard-links is distribution specific since
+     some distros have post-processing hard-link steps and so doing this
+     here is a waste of time.  Worse than a waste of time in rpm-based
+     distributions it can result in build determinism issues from
+     build-to-build since some files may get a hard link in one pass but
+     not in another (if the files happened to be created in parallel).  */
+  if (hard_links)
+    other_paths = siblings (output_path);
+
+  /* If there are other paths, then walk the sibling paths looking for
+     files with the same content so we can hard link and reduce disk
+     space usage.  */
   if (other_paths != NULL)
     {
-      struct stat fname_stat;
+      struct stat64 fname_stat;
 
-      if (lstat (fname, &fname_stat) >= 0
+      if (lstat64 (fname, &fname_stat) >= 0
          && S_ISREG (fname_stat.st_mode))
        {
          const char *fname_tail = fname + strlen (output_path);
@@ -680,14 +866,14 @@ failure while writing data for category `%s'"), category));
              const char *other_path = *other_p;
              size_t other_path_len = strlen (other_path);
              char *other_fname;
-             struct stat other_fname_stat;
+             struct stat64 other_fname_stat;
 
              other_fname =
                (char *) xmalloc (other_path_len + strlen (fname_tail) + 1);
              memcpy (other_fname, other_path, other_path_len);
              strcpy (other_fname + other_path_len, fname_tail);
 
-             if (lstat (other_fname, &other_fname_stat) >= 0
+             if (lstat64 (other_fname, &other_fname_stat) >= 0
                  && S_ISREG (other_fname_stat.st_mode)
                  /* Consider only files on the same device.
                     Otherwise hard linking won't work anyway.  */
@@ -741,9 +927,8 @@ failure while writing data for category `%s'"), category));
                              unlink (fname);
                              if (rename (tmp_fname, fname) < 0)
                                {
-                                 if (!be_quiet)
-                                   WITH_CUR_LOCALE (error (0, errno, _("\
-cannot create output file `%s' for category `%s'"), fname, category));
+                                 record_error (0, errno, _("\
+cannot create output file `%s' for category `%s'"), fname, category);
                                }
                              free (tmp_fname);
                              free (other_fname);
@@ -764,3 +949,63 @@ cannot create output file `%s' for category `%s'"), fname, category));
 
   free (fname);
 }
+
+
+/* General handling of `copy'.  */
+void
+handle_copy (struct linereader *ldfile, const struct charmap_t *charmap,
+            const char *repertoire_name, struct localedef_t *result,
+            enum token_t token, int locale, const char *locale_name,
+            int ignore_content)
+{
+  struct token *now;
+  int warned = 0;
+
+  now = lr_token (ldfile, charmap, result, NULL, verbose);
+  if (now->tok != tok_string)
+    lr_error (ldfile, _("expecting string argument for `copy'"));
+  else if (!ignore_content)
+    {
+      if (now->val.str.startmb == NULL)
+       lr_error (ldfile, _("\
+locale name should consist only of portable characters"));
+      else
+       {
+         (void) add_to_readlist (locale, now->val.str.startmb,
+                                 repertoire_name, 1, NULL);
+         result->copy_name[locale] = now->val.str.startmb;
+       }
+    }
+
+  lr_ignore_rest (ldfile, now->tok == tok_string);
+
+  /* The rest of the line must be empty and the next keyword must be
+     `END xxx'.  */
+  while ((now = lr_token (ldfile, charmap, result, NULL, verbose))->tok
+        != tok_end && now->tok != tok_eof)
+    {
+      if (warned == 0)
+       {
+         lr_error (ldfile, _("\
+no other keyword shall be specified when `copy' is used"));
+         warned = 1;
+       }
+
+      lr_ignore_rest (ldfile, 0);
+    }
+
+  if (now->tok != tok_eof)
+    {
+      /* Handle `END xxx'.  */
+      now = lr_token (ldfile, charmap, result, NULL, verbose);
+
+      if (now->tok != token)
+       lr_error (ldfile, _("\
+`%1$s' definition does not end with `END %1$s'"), locale_name);
+
+      lr_ignore_rest (ldfile, now->tok == token);
+    }
+  else
+    /* When we come here we reached the end of the file.  */
+    lr_error (ldfile, _("%s: premature end of file"), locale_name);
+}