-/* 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;
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)
if (ldfile == NULL)
{
- stpcpy (stpcpy (path, next), filename);
+ stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
ldfile = lr_open (path, locfile_hash);
}
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;
/* 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)
{
size_t len;
char *base, *p;
- struct stat output_stat;
+ struct stat64 output_stat;
DIR *dirp;
int nelems;
const char **elems;
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. */
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)
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
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
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)
{
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;
}
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);
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. */
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);
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);
+}