mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-18 05:53:36 +08:00
a7b65cdc9a
2002-04-18 Ulrich Drepper <drepper@redhat.com> * locale/programs/locfile.c (to_archive): New variable. To collect data which has to be added to the locale archive. (write_all_categories): Take new third parameter with locale name. Unless no_archive flag set add new locale data to the archive (write_locale_data): Unless no-archive flag set store generated data in to_archive data structure instead of generation output file. Add new parameter with locale category index. * locale/programs/locfile.h: Add new parameters in declaractions of write_all_categories and write_locale_data. * locale/programs/localedef.c: Recognize --no-archive, --list-archive, --add-to-archive, and --delete-from-archive options. Pass extra parameter to write_all_categories. * locale/programs/localedef.h: Add prototypes for functions in locarchive.c. * locale/locarchive.h: New file. * locale/programs/locarchive.c: New file. * locale/Makefile (distribute): Add programs/locarchive.c and locarchive.h. (localedef-modules): Add md5 and locarchive. Add vpath to crypt subdir for md5.c. * locale/programs/ld-address.c: Pass locale category ID as new second parameter to write_locale_data. * locale/programs/ld-collate.c: Likewise. * locale/programs/ld-ctype.c: Likewise. * locale/programs/ld-identification.c: Likewise. * locale/programs/ld-measurement.c: Likewise. * locale/programs/ld-messages.c: Likewise. * locale/programs/ld-monetary.c: Likewise. * locale/programs/ld-name.c: Likewise. * locale/programs/ld-numeric.c: Likewise. * locale/programs/ld-paper.c: Likewise. * locale/programs/ld-telephone.c: Likewise. * locale/programs/ld-time.c: Likewise. * locale/simple-hash.c: Move compute_hashval function from here... * locale/hashval.h: ...to here. New file. * locale/simple-hash.h: Add prototype for compute_hashval. * include/libintl.h: Minor cleanups. * elf/reldep7.c: New file. * elf/reldep7mod1.c: New file. * elf/reldep7mod2.c: New file. * elf/Makefile: Add rules to build and run reldep7.
932 lines
24 KiB
C
932 lines
24 KiB
C
/* Copyright (C) 2002 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, write to the Free
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307 USA. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <error.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <libintl.h>
|
|
#include <locale.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "../../crypt/md5.h"
|
|
#include "../localeinfo.h"
|
|
#include "../locarchive.h"
|
|
#include "simple-hash.h"
|
|
#include "localedef.h"
|
|
|
|
|
|
static const char archivefname[] = LOCALEDIR "/locale-archive";
|
|
|
|
static const char *locnames[] =
|
|
{
|
|
#define DEFINE_CATEGORY(category, category_name, items, a) \
|
|
[category] = category_name,
|
|
#include "categories.def"
|
|
#undef DEFINE_CATEGORY
|
|
};
|
|
|
|
|
|
/* Size of the initial archive header. */
|
|
#define INITIAL_NUM_NANES 450
|
|
#define INITIAL_SIZE_STRINGS 3500
|
|
#define INITIAL_NUM_LOCREC 350
|
|
#define INITIAL_NUM_SUMS 2000
|
|
|
|
|
|
static void
|
|
create_archive (struct locarhandle *ah)
|
|
{
|
|
int fd;
|
|
char fname[] = LOCALEDIR "/locale-archive.XXXXXX";
|
|
struct locarhead head;
|
|
void *p;
|
|
size_t total;
|
|
|
|
/* Create a temporary file in the correct directory. */
|
|
fd = mkstemp (fname);
|
|
if (fd == -1)
|
|
error (EXIT_FAILURE, errno, _("cannot create temporary file"));
|
|
|
|
/* Create the initial content of the archive. */
|
|
head.magic = AR_MAGIC;
|
|
head.namehash_offset = sizeof (struct locarhead);
|
|
head.namehash_used = 0;
|
|
head.namehash_size = next_prime (INITIAL_NUM_NANES);
|
|
|
|
head.string_offset = (head.namehash_offset
|
|
+ head.namehash_size * sizeof (struct namehashent));
|
|
head.string_used = 0;
|
|
head.string_size = INITIAL_SIZE_STRINGS;
|
|
|
|
head.locrectab_offset = head.string_offset + head.string_size;
|
|
head.locrectab_used = 0;
|
|
head.locrectab_size = INITIAL_NUM_LOCREC;
|
|
|
|
head.sumhash_offset = (head.locrectab_offset
|
|
+ head.locrectab_size * sizeof (struct locrecent));
|
|
head.sumhash_used = 0;
|
|
head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
|
|
|
|
total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
|
|
|
|
/* Write out the header and create room for the other data structures. */
|
|
if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
|
|
}
|
|
|
|
if (ftruncate64 (fd, total) != 0)
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval, _("cannot resize archive file"));
|
|
}
|
|
|
|
/* Map the header and all the administration data structures. */
|
|
p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (p == MAP_FAILED)
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval, _("cannot map archive header"));
|
|
}
|
|
|
|
/* Now try to rename it. We don't use the rename function since
|
|
this would overwrite a file which has been created in
|
|
parallel. */
|
|
if (link (fname, archivefname) == -1)
|
|
{
|
|
int errval = errno;
|
|
|
|
/* We cannot use the just created file. */
|
|
close (fd);
|
|
unlink (fname);
|
|
|
|
if (errval == EEXIST)
|
|
{
|
|
/* There is already an archive. Must have been a localedef run
|
|
which happened in parallel. Simply open this file then. */
|
|
open_archive (ah);
|
|
return;
|
|
}
|
|
|
|
error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
|
|
}
|
|
|
|
/* Remove the temporary name. */
|
|
unlink (fname);
|
|
|
|
/* Make the file globally readable. */
|
|
if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
|
|
{
|
|
int errval = errno;
|
|
unlink (archivefname);
|
|
error (EXIT_FAILURE, errval,
|
|
_("cannot change mode of new locale archive"));
|
|
}
|
|
|
|
ah->fd = fd;
|
|
ah->addr = p;
|
|
ah->len = total;
|
|
}
|
|
|
|
|
|
static void
|
|
enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
|
|
{
|
|
struct stat64 st;
|
|
int fd;
|
|
char fname[] = LOCALEDIR "/locale-archive.XXXXXX";
|
|
struct locarhead newhead;
|
|
size_t total;
|
|
void *p;
|
|
unsigned int cnt;
|
|
struct namehashent *oldnamehashtab;
|
|
struct locrecent *oldlocrectab;
|
|
struct locarhandle new_ah;
|
|
|
|
/* Not all of the old file has to be mapped. Change this now this
|
|
we will have to access the whole content. */
|
|
if (fstat64 (ah->fd, &st) != 0
|
|
|| (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
|
|
error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
|
|
ah->len = st.st_size;
|
|
|
|
/* Create a temporary file in the correct directory. */
|
|
fd = mkstemp (fname);
|
|
if (fd == -1)
|
|
error (EXIT_FAILURE, errno, _("cannot create temporary file"));
|
|
|
|
/* Copy the existing head information. */
|
|
newhead = *head;
|
|
|
|
/* Create the new archive header. The sizes of the various tables
|
|
should be double from what is currently used. */
|
|
newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
|
|
newhead.namehash_size);
|
|
printf ("name: size: %u, used: %d, new: size: %u\n",
|
|
head->namehash_size, head->namehash_used, newhead.namehash_size);
|
|
|
|
newhead.string_offset = (newhead.namehash_offset
|
|
+ (newhead.namehash_size
|
|
* sizeof (struct namehashent)));
|
|
newhead.string_size = MAX (2 * newhead.string_used, newhead.string_size);
|
|
|
|
newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
|
|
newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
|
|
newhead.locrectab_size);
|
|
|
|
newhead.sumhash_offset = (newhead.locrectab_offset
|
|
+ (newhead.locrectab_size
|
|
* sizeof (struct locrecent)));
|
|
newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
|
|
newhead.sumhash_size);
|
|
|
|
total = (newhead.sumhash_offset
|
|
+ newhead.sumhash_size * sizeof (struct sumhashent));
|
|
|
|
/* The new file is empty now. */
|
|
newhead.namehash_used = 0;
|
|
newhead.string_used = 0;
|
|
newhead.locrectab_used = 0;
|
|
newhead.sumhash_used = 0;
|
|
|
|
/* Write out the header and create room for the other data structures. */
|
|
if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
|
|
!= sizeof (newhead))
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
|
|
}
|
|
|
|
if (ftruncate64 (fd, total) != 0)
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval, _("cannot resize archive file"));
|
|
}
|
|
|
|
/* Map the header and all the administration data structures. */
|
|
p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (p == MAP_FAILED)
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval, _("cannot map archive header"));
|
|
}
|
|
|
|
/* Lock the new file. */
|
|
if (lockf64 (fd, F_LOCK, total) != 0)
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval, _("cannot lock new archive"));
|
|
}
|
|
|
|
new_ah.len = total;
|
|
new_ah.addr = p;
|
|
new_ah.fd = fd;
|
|
|
|
/* Walk through the hash name hash table to find out what data is
|
|
still referenced and transfer it into the new file. */
|
|
oldnamehashtab = (struct namehashent *) ((char *) ah->addr
|
|
+ head->namehash_offset);
|
|
oldlocrectab = (struct locrecent *) ((char *) ah->addr
|
|
+ head->locrectab_offset);
|
|
for (cnt = 0; cnt < head->namehash_size; ++cnt)
|
|
if (oldnamehashtab[cnt].locrec_offset != 0)
|
|
{
|
|
/* Insert this entry in the new hash table. */
|
|
locale_data_t old_data;
|
|
unsigned int idx;
|
|
struct locrecent *oldlocrec;
|
|
|
|
oldlocrec = (struct locrecent *) ((char *) ah->addr
|
|
+ oldnamehashtab[cnt].locrec_offset);
|
|
|
|
for (idx = 0; idx < __LC_LAST; ++idx)
|
|
if (idx != LC_ALL)
|
|
{
|
|
old_data[idx].size = oldlocrec->record[idx].len;
|
|
old_data[idx].addr
|
|
= ((char *) ah->addr + oldlocrec->record[idx].offset);
|
|
|
|
__md5_buffer (old_data[idx].addr, old_data[idx].size,
|
|
old_data[idx].sum);
|
|
}
|
|
|
|
if (add_locale_to_archive (&new_ah,
|
|
((char *) ah->addr
|
|
+ oldnamehashtab[cnt].name_offset),
|
|
old_data, 0) != 0)
|
|
error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
|
|
}
|
|
|
|
|
|
/* Make the file globally readable. */
|
|
if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval,
|
|
_("cannot change mode of resized locale archive"));
|
|
}
|
|
|
|
/* Rename the new file. */
|
|
if (rename (fname, archivefname) != 0)
|
|
{
|
|
int errval = errno;
|
|
unlink (fname);
|
|
error (EXIT_FAILURE, errval, _("cannot rename new archive"));
|
|
}
|
|
|
|
/* Close the old file. */
|
|
close_archive (ah);
|
|
|
|
/* Add the information for the new one. */
|
|
*ah = new_ah;
|
|
}
|
|
|
|
|
|
void
|
|
open_archive (struct locarhandle *ah)
|
|
{
|
|
struct stat64 st;
|
|
struct stat64 st2;
|
|
int fd;
|
|
struct locarhead head;
|
|
int retry = 0;
|
|
|
|
again:
|
|
/* Open the archive. We must have exclusive write access. */
|
|
fd = open64 (archivefname, O_RDWR);
|
|
if (fd == -1)
|
|
{
|
|
/* Maybe the file does not yet exist. */
|
|
if (errno == ENOENT)
|
|
{
|
|
create_archive (ah);
|
|
return;
|
|
}
|
|
else
|
|
error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
|
|
archivefname);
|
|
}
|
|
|
|
if (fstat64 (fd, &st) < 0)
|
|
error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
|
|
archivefname);
|
|
|
|
if (lockf64 (fd, F_LOCK, st.st_size) == -1)
|
|
{
|
|
close (fd);
|
|
|
|
if (retry++ < max_locarchive_open_retry)
|
|
{
|
|
struct timespec req;
|
|
|
|
/* Wait for a bit. */
|
|
req.tv_sec = 0;
|
|
req.tv_nsec = 1000000 * (random () % 500 + 1);
|
|
(void) nanosleep (&req, NULL);
|
|
|
|
goto again;
|
|
}
|
|
|
|
error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
|
|
archivefname);
|
|
}
|
|
|
|
/* One more check. Maybe another process replaced the archive file
|
|
with a new, larger one since we opened the file. */
|
|
if (stat64 (archivefname, &st2) == -1
|
|
|| st.st_dev != st2.st_dev
|
|
|| st.st_ino != st2.st_ino)
|
|
{
|
|
close (fd);
|
|
goto again;
|
|
}
|
|
|
|
/* Read the header. */
|
|
if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
|
|
error (EXIT_FAILURE, errno, _("cannot read archive header"));
|
|
|
|
ah->fd = fd;
|
|
ah->len = (head.sumhash_offset
|
|
+ head.sumhash_size * sizeof (struct sumhashent));
|
|
|
|
/* Now we know how large the administrative information part is.
|
|
Map all of it. */
|
|
ah->addr = mmap64 (NULL, ah->len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (ah->addr == MAP_FAILED)
|
|
error (EXIT_FAILURE, errno, _("cannot map archive header"));
|
|
}
|
|
|
|
|
|
void
|
|
close_archive (struct locarhandle *ah)
|
|
{
|
|
munmap (ah->addr, ah->len);
|
|
close (ah->fd);
|
|
}
|
|
|
|
|
|
/* Check the content of the archive for duplicates. Add the content
|
|
of the files if necessary. Add all the names, possibly overwriting
|
|
old files. */
|
|
int
|
|
add_locale_to_archive (ah, name, data, replace)
|
|
struct locarhandle *ah;
|
|
const char *name;
|
|
locale_data_t data;
|
|
bool replace;
|
|
{
|
|
/* First look for the name. If it already exists and we are not
|
|
supposed to replace it don't do anything. If it does not exist
|
|
we have to allocate a new locale record. */
|
|
size_t name_len = strlen (name);
|
|
uint32_t file_offsets[__LC_LAST];
|
|
unsigned int num_new_offsets = 0;
|
|
struct sumhashent *sumhashtab;
|
|
uint32_t hval;
|
|
unsigned int cnt;
|
|
unsigned int idx;
|
|
unsigned int insert_idx;
|
|
struct locarhead *head;
|
|
struct namehashent *namehashtab;
|
|
struct namehashent *namehashent;
|
|
unsigned int incr;
|
|
struct locrecent *locrecent;
|
|
|
|
head = ah->addr;
|
|
sumhashtab = (struct sumhashent *) ((char *) ah->addr
|
|
+ head->sumhash_offset);
|
|
namehashtab = (struct namehashent *) ((char *) ah->addr
|
|
+ head->namehash_offset);
|
|
|
|
|
|
/* For each locale category data set determine whether the same data
|
|
is already somewhere in the archive. */
|
|
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
if (cnt != LC_ALL)
|
|
{
|
|
/* By default signal that we have no data. */
|
|
file_offsets[cnt] = 0;
|
|
++num_new_offsets;
|
|
|
|
/* Compute the hash value of the checksum to determine a
|
|
starting point for the search in the MD5 hash value
|
|
table. */
|
|
hval = compute_hashval (data[cnt].sum, 16);
|
|
|
|
idx = hval % head->sumhash_size;
|
|
incr = 1 + hval % (head->sumhash_size - 2);
|
|
|
|
while (sumhashtab[idx].file_offset != 0)
|
|
{
|
|
if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
|
|
{
|
|
/* Found it. */
|
|
file_offsets[cnt] = sumhashtab[idx].file_offset;
|
|
--num_new_offsets;
|
|
break;
|
|
}
|
|
|
|
idx += incr;
|
|
if (idx >= head->sumhash_size)
|
|
idx -= head->sumhash_size;
|
|
}
|
|
}
|
|
|
|
|
|
/* Hash value of the locale name. */
|
|
hval = compute_hashval (name, name_len);
|
|
|
|
insert_idx = -1;
|
|
idx = hval % head->namehash_size;
|
|
incr = 1 + hval % (head->namehash_size - 2);
|
|
|
|
/* If the name_offset field is zero this means this is no
|
|
deleted entry and therefore no entry can be found. */
|
|
while (namehashtab[idx].name_offset != 0)
|
|
{
|
|
if (namehashtab[idx].hashval == hval
|
|
&& strcmp (name,
|
|
(char *) ah->addr + namehashtab[idx].name_offset) == 0)
|
|
{
|
|
/* Found the entry. */
|
|
if (! replace)
|
|
{
|
|
if (! be_quiet)
|
|
error (0, 0, _("locale '%s' already exists"), name);
|
|
return 1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Remember the first place we can insert the new entry. */
|
|
if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
|
|
insert_idx = idx;
|
|
|
|
idx += incr;
|
|
if (idx >= head->namehash_size)
|
|
idx -= head->namehash_size;
|
|
}
|
|
|
|
/* Add as early as possible. */
|
|
if (insert_idx != -1)
|
|
idx = insert_idx;
|
|
|
|
namehashent = &namehashtab[idx];
|
|
|
|
/* Determine whether we have to resize the file. */
|
|
if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
|
|
|| (namehashent->locrec_offset == 0
|
|
&& (head->locrectab_used == head->locrectab_size
|
|
|| head->string_used + name_len + 1 > head->string_size
|
|
|| 100 * head->namehash_used > 75 * head->namehash_size)))
|
|
{
|
|
/* The current archive is not large enough. */
|
|
enlarge_archive (ah, head);
|
|
return add_locale_to_archive (ah, name, data, replace);
|
|
}
|
|
|
|
/* Add the locale data which is not yet in the archive. */
|
|
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
if (cnt != LC_ALL && file_offsets[cnt] == 0)
|
|
{
|
|
/* The data for this section is not yet available in the
|
|
archive. Append it. */
|
|
off64_t lastpos;
|
|
uint32_t md5hval;
|
|
|
|
lastpos = lseek64 (ah->fd, 0, SEEK_END);
|
|
if (lastpos == (off64_t) -1)
|
|
error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
|
|
|
|
/* Align all data to a 16 byte boundary. */
|
|
if ((lastpos & 15) != 0)
|
|
{
|
|
static const char zeros[15] = { 0, };
|
|
|
|
if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
|
|
!= 16 - (lastpos & 15))
|
|
error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
|
|
|
|
lastpos += 16 - (lastpos & 15);
|
|
}
|
|
|
|
/* Remember the position. */
|
|
file_offsets[cnt] = lastpos;
|
|
|
|
/* Write the data. */
|
|
if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
|
|
!= data[cnt].size)
|
|
error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
|
|
|
|
/* Add the hash value to the hash table. */
|
|
md5hval = compute_hashval (data[cnt].sum, 16);
|
|
|
|
idx = md5hval % head->sumhash_size;
|
|
incr = 1 + md5hval % (head->sumhash_size - 2);
|
|
|
|
while (sumhashtab[idx].file_offset != 0)
|
|
{
|
|
idx += incr;
|
|
if (idx >= head->sumhash_size)
|
|
idx -= head->sumhash_size;
|
|
}
|
|
|
|
memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
|
|
sumhashtab[idx].file_offset = file_offsets[cnt];
|
|
|
|
++head->sumhash_used;
|
|
}
|
|
|
|
|
|
if (namehashent->locrec_offset == 0)
|
|
{
|
|
/* Add the name string. */
|
|
memcpy ((char *) ah->addr + head->string_offset + head->string_used,
|
|
name, name_len + 1);
|
|
namehashent->name_offset = head->string_offset + head->string_used;
|
|
head->string_used += name_len + 1;
|
|
|
|
/* Allocate a name location record. */
|
|
namehashent->locrec_offset = (head->locrectab_offset
|
|
+ (head->locrectab_used++
|
|
* sizeof (struct locrecent)));
|
|
|
|
namehashent->hashval = hval;
|
|
|
|
++head->namehash_used;
|
|
}
|
|
|
|
|
|
/* Fill in the table with the locations of the locale data. */
|
|
locrecent = (struct locrecent *) ((char *) ah->addr
|
|
+ namehashent->locrec_offset);
|
|
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
if (cnt != LC_ALL)
|
|
{
|
|
locrecent->record[cnt].offset = file_offsets[cnt];
|
|
locrecent->record[cnt].len = data[cnt].size;
|
|
}
|
|
|
|
|
|
/* Read the locale.alias file to see whether any matching record is
|
|
found. If an entry is available check whether it is already in
|
|
the archive. If this is the case check whether the new locale's
|
|
name is more specific than the one currently referred to by the
|
|
alias. */
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
add_locales_to_archive (nlist, list, replace)
|
|
size_t nlist;
|
|
char *list[];
|
|
bool replace;
|
|
{
|
|
struct locarhandle ah;
|
|
int result = 0;
|
|
|
|
/* Open the archive. This call never returns if we cannot
|
|
successfully open the archive. */
|
|
open_archive (&ah);
|
|
|
|
while (nlist-- > 0)
|
|
{
|
|
const char *fname = *list++;
|
|
size_t fnamelen = strlen (fname);
|
|
struct stat64 st;
|
|
DIR *dirp;
|
|
struct dirent64 *d;
|
|
int seen;
|
|
locale_data_t data;
|
|
int cnt;
|
|
|
|
if (! be_quiet)
|
|
printf (_("Adding %s\n"), fname);
|
|
|
|
/* First see whether this really is a directory and whether it
|
|
contains all the require locale category files. */
|
|
if (stat64 (fname, &st) < 0)
|
|
{
|
|
error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
|
|
strerror (errno));
|
|
continue;
|
|
}
|
|
if (!S_ISDIR (st.st_mode))
|
|
{
|
|
error (0, 0, _("\"%s\" is no directory; ignored"), fname);
|
|
continue;
|
|
}
|
|
|
|
dirp = opendir (fname);
|
|
if (dirp == NULL)
|
|
{
|
|
error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
|
|
fname, strerror (errno));
|
|
continue;
|
|
}
|
|
|
|
seen = 0;
|
|
while ((d = readdir64 (dirp)) != NULL)
|
|
{
|
|
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
if (cnt != LC_ALL)
|
|
if (strcmp (d->d_name, locnames[cnt]) == 0)
|
|
{
|
|
unsigned char d_type;
|
|
|
|
/* We have an object of the required name. If it's
|
|
a directory we have to look at a file with the
|
|
prefix "SYS_". Otherwise we have found what we
|
|
are looking for. */
|
|
#ifdef _DIRENT_HAVE_D_TYPE
|
|
d_type = d->d_type;
|
|
|
|
if (d_type != DT_REG)
|
|
#endif
|
|
{
|
|
char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
|
|
|
|
#ifdef _DIRENT_HAVE_D_TYPE
|
|
if (d_type == DT_UNKNOWN)
|
|
#endif
|
|
{
|
|
strcpy (stpcpy (stpcpy (fullname, fname), "/"),
|
|
d->d_name);
|
|
|
|
if (stat64 (fullname, &st) == -1)
|
|
/* We cannot stat the file, ignore it. */
|
|
break;
|
|
|
|
d_type = IFTODT (st.st_mode);
|
|
}
|
|
|
|
if (d_type == DT_DIR)
|
|
{
|
|
/* We have to do more tests. The file is a
|
|
directory and it therefore must contain a
|
|
regular file with the same name except a
|
|
"SYS_" prefix. */
|
|
strcpy (stpcpy (stpcpy (stpcpy (stpcpy (fullname,
|
|
fname),
|
|
"/"),
|
|
d->d_name),
|
|
"/SYS_"),
|
|
d->d_name);
|
|
|
|
if (stat64 (fullname, &st) == -1)
|
|
/* There is no SYS_* file or we cannot
|
|
access it. */
|
|
break;
|
|
|
|
d_type = IFTODT (st.st_mode);
|
|
}
|
|
}
|
|
|
|
/* If we found a regular file (eventually after
|
|
following a symlink) we are successful. */
|
|
if (d_type == DT_REG)
|
|
++seen;
|
|
break;
|
|
}
|
|
}
|
|
|
|
closedir (dirp);
|
|
|
|
if (seen != __LC_LAST - 1)
|
|
{
|
|
/* We don't have all locale category files. Ignore the name. */
|
|
error (0, 0, _("incomplete set of locale files in \"%s\""),
|
|
fname);
|
|
continue;
|
|
}
|
|
|
|
/* Add the files to the archive. To do this we first compute
|
|
sizes and the MD5 sums of all the files. */
|
|
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
if (cnt != LC_ALL)
|
|
{
|
|
char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
|
|
int fd;
|
|
|
|
strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
|
|
fd = open64 (fullname, O_RDONLY);
|
|
if (fd == -1 || fstat64 (fd, &st) == -1)
|
|
{
|
|
/* Cannot read the file. */
|
|
if (fd != -1)
|
|
close (fd);
|
|
break;
|
|
}
|
|
|
|
if (S_ISDIR (st.st_mode))
|
|
{
|
|
close (fd);
|
|
strcpy (stpcpy (stpcpy (stpcpy (stpcpy (fullname, fname),
|
|
"/"),
|
|
locnames[cnt]),
|
|
"/SYS_"),
|
|
locnames[cnt]);
|
|
|
|
fd = open64 (fullname, O_RDONLY);
|
|
if (fd == -1 || fstat64 (fd, &st) == -1
|
|
|| !S_ISREG (st.st_mode))
|
|
{
|
|
if (fd != -1)
|
|
close (fd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Map the file. */
|
|
data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
|
|
fd, 0);
|
|
if (data[cnt].addr == MAP_FAILED)
|
|
{
|
|
/* Cannot map it. */
|
|
close (fd);
|
|
break;
|
|
}
|
|
|
|
data[cnt].size = st.st_size;
|
|
__md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
|
|
|
|
/* We don't need the file descriptor anymore. */
|
|
close (fd);
|
|
}
|
|
|
|
if (cnt != __LC_LAST)
|
|
{
|
|
while (cnt-- > 0)
|
|
if (cnt != LC_ALL)
|
|
munmap (data[cnt].addr, data[cnt].size);
|
|
|
|
error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
|
|
|
|
continue;
|
|
}
|
|
|
|
result |= add_locale_to_archive (&ah, basename (fname), data, replace);
|
|
|
|
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
if (cnt != LC_ALL)
|
|
munmap (data[cnt].addr, data[cnt].size);
|
|
}
|
|
|
|
/* We are done. */
|
|
close_archive (&ah);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int
|
|
delete_locales_from_archive (nlist, list)
|
|
size_t nlist;
|
|
char *list[];
|
|
{
|
|
struct locarhandle ah;
|
|
struct locarhead *head;
|
|
struct namehashent *namehashtab;
|
|
|
|
/* Open the archive. This call never returns if we cannot
|
|
successfully open the archive. */
|
|
open_archive (&ah);
|
|
|
|
head = ah.addr;
|
|
namehashtab = (struct namehashent *) ((char *) ah.addr
|
|
+ head->namehash_offset);
|
|
|
|
while (nlist-- > 0)
|
|
{
|
|
const char *locname = *list++;
|
|
uint32_t hval;
|
|
unsigned int idx;
|
|
unsigned int incr;
|
|
|
|
/* Search for this locale in the archive. */
|
|
hval = compute_hashval (locname, strlen (locname));
|
|
|
|
idx = hval % head->namehash_size;
|
|
incr = 1 + hval % (head->namehash_size - 2);
|
|
|
|
/* If the name_offset field is zero this means this is no
|
|
deleted entry and therefore no entry can be found. */
|
|
while (namehashtab[idx].name_offset != 0)
|
|
{
|
|
if (namehashtab[idx].hashval == hval
|
|
&& (strcmp (locname,
|
|
(char *) ah.addr + namehashtab[idx].name_offset)
|
|
== 0))
|
|
{
|
|
/* Found the entry. Now mark it as removed by zero-ing
|
|
the reference to the locale record. */
|
|
namehashtab[idx].locrec_offset = 0;
|
|
--head->namehash_used;
|
|
break;
|
|
}
|
|
|
|
idx += incr;
|
|
if (idx >= head->namehash_size)
|
|
idx -= head->namehash_size;
|
|
}
|
|
|
|
if (namehashtab[idx].name_offset == 0 && ! be_quiet)
|
|
error (0, 0, _("locale \"%s\" not in archive"), locname);
|
|
}
|
|
|
|
close_archive (&ah);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
xstrcmp (const void *a, const void *b)
|
|
{
|
|
return strcmp (*(const char **) a, *(const char **) b);
|
|
}
|
|
|
|
|
|
void
|
|
show_archive_content (void)
|
|
{
|
|
struct locarhandle ah;
|
|
struct locarhead *head;
|
|
struct namehashent *namehashtab;
|
|
int cnt;
|
|
char **names;
|
|
int used;
|
|
|
|
/* Open the archive. This call never returns if we cannot
|
|
successfully open the archive. */
|
|
open_archive (&ah);
|
|
|
|
head = ah.addr;
|
|
|
|
names = (char **) xmalloc (head->namehash_used * sizeof (char *));
|
|
|
|
namehashtab = (struct namehashent *) ((char *) ah.addr
|
|
+ head->namehash_offset);
|
|
for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
|
|
if (namehashtab[cnt].locrec_offset != 0)
|
|
{
|
|
assert (used < head->namehash_used);
|
|
names[used++] = ah.addr + namehashtab[cnt].name_offset;
|
|
}
|
|
|
|
/* Sort the names. */
|
|
qsort (names, used, sizeof (char *), xstrcmp);
|
|
|
|
for (cnt = 0; cnt < used; ++cnt)
|
|
puts (names[cnt]);
|
|
|
|
close_archive (&ah);
|
|
|
|
exit (EXIT_SUCCESS);
|
|
}
|