|
|
50f89d |
#define _GNU_SOURCE
|
|
|
50f89d |
#include <assert.h>
|
|
|
50f89d |
#include <dirent.h>
|
|
|
50f89d |
#include <errno.h>
|
|
|
50f89d |
#include <fcntl.h>
|
|
|
50f89d |
#include <locale.h>
|
|
|
50f89d |
#include <stdarg.h>
|
|
|
50f89d |
#include <stdbool.h>
|
|
|
50f89d |
#include <stdio.h>
|
|
|
50f89d |
#include <stdlib.h>
|
|
|
50f89d |
#include <getopt.h>
|
|
|
50f89d |
#include <string.h>
|
|
|
50f89d |
#include <sys/mman.h>
|
|
|
50f89d |
#include <sys/stat.h>
|
|
|
50f89d |
#include <unistd.h>
|
|
|
50f89d |
#include "../locale/hashval.h"
|
|
|
50f89d |
#define __LC_LAST 13
|
|
|
50f89d |
#include "../locale/locarchive.h"
|
|
|
50f89d |
#include "../crypt/md5.h"
|
|
|
50f89d |
|
|
|
50f89d |
const char *alias_file = DATADIR "/locale/locale.alias";
|
|
|
50f89d |
const char *locar_file = PREFIX "/lib/locale/locale-archive";
|
|
|
50f89d |
const char *tmpl_file = PREFIX "/lib/locale/locale-archive.tmpl";
|
|
|
50f89d |
const char *loc_path = PREFIX "/lib/locale/";
|
|
|
50f89d |
/* Flags set by `--verbose` option. */
|
|
|
50f89d |
int be_quiet = 1;
|
|
|
50f89d |
int verbose = 0;
|
|
|
50f89d |
int max_locarchive_open_retry = 10;
|
|
|
50f89d |
const char *output_prefix;
|
|
|
50f89d |
|
|
|
50f89d |
/* Endianness should have been taken care of by localedef. We don't need to do
|
|
|
50f89d |
additional swapping. We need this variable exported however, since
|
|
|
50f89d |
locarchive.c uses it to determine if it needs to swap endianness of a value
|
|
|
50f89d |
before writing to or reading from the archive. */
|
|
|
50f89d |
bool swap_endianness_p = false;
|
|
|
50f89d |
|
|
|
50f89d |
static const char *locnames[] =
|
|
|
50f89d |
{
|
|
|
50f89d |
#define DEFINE_CATEGORY(category, category_name, items, a) \
|
|
|
50f89d |
[category] = category_name,
|
|
|
50f89d |
#include "../locale/categories.def"
|
|
|
50f89d |
#undef DEFINE_CATEGORY
|
|
|
50f89d |
};
|
|
|
50f89d |
|
|
|
50f89d |
static int
|
|
|
50f89d |
is_prime (unsigned long candidate)
|
|
|
50f89d |
{
|
|
|
50f89d |
/* No even number and none less than 10 will be passed here. */
|
|
|
50f89d |
unsigned long int divn = 3;
|
|
|
50f89d |
unsigned long int sq = divn * divn;
|
|
|
50f89d |
|
|
|
50f89d |
while (sq < candidate && candidate % divn != 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
++divn;
|
|
|
50f89d |
sq += 4 * divn;
|
|
|
50f89d |
++divn;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
return candidate % divn != 0;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
unsigned long
|
|
|
50f89d |
next_prime (unsigned long seed)
|
|
|
50f89d |
{
|
|
|
50f89d |
/* Make it definitely odd. */
|
|
|
50f89d |
seed |= 1;
|
|
|
50f89d |
|
|
|
50f89d |
while (!is_prime (seed))
|
|
|
50f89d |
seed += 2;
|
|
|
50f89d |
|
|
|
50f89d |
return seed;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
void
|
|
|
50f89d |
error (int status, int errnum, const char *message, ...)
|
|
|
50f89d |
{
|
|
|
50f89d |
va_list args;
|
|
|
50f89d |
|
|
|
50f89d |
va_start (args, message);
|
|
|
50f89d |
fflush (stdout);
|
|
|
50f89d |
fprintf (stderr, "%s: ", program_invocation_name);
|
|
|
50f89d |
vfprintf (stderr, message, args);
|
|
|
50f89d |
va_end (args);
|
|
|
50f89d |
if (errnum)
|
|
|
50f89d |
fprintf (stderr, ": %s", strerror (errnum));
|
|
|
50f89d |
putc ('\n', stderr);
|
|
|
50f89d |
fflush (stderr);
|
|
|
50f89d |
if (status)
|
|
|
50f89d |
exit (errnum == EROFS ? 0 : status);
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
void *
|
|
|
50f89d |
xmalloc (size_t size)
|
|
|
50f89d |
{
|
|
|
50f89d |
void *p = malloc (size);
|
|
|
50f89d |
if (p == NULL)
|
|
|
50f89d |
error (EXIT_FAILURE, errno, "could not allocate %zd bytes of memory", size);
|
|
|
50f89d |
return p;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
static void
|
|
|
50f89d |
open_tmpl_archive (struct locarhandle *ah)
|
|
|
50f89d |
{
|
|
|
50f89d |
struct stat64 st;
|
|
|
50f89d |
int fd;
|
|
|
50f89d |
struct locarhead head;
|
|
|
50f89d |
const char *archivefname = ah->fname == NULL ? tmpl_file : ah->fname;
|
|
|
50f89d |
|
|
|
50f89d |
/* Open the archive. We must have exclusive write access. */
|
|
|
50f89d |
fd = open64 (archivefname, O_RDONLY);
|
|
|
50f89d |
if (fd == -1)
|
|
|
50f89d |
error (EXIT_FAILURE, errno, "cannot open locale archive template file \"%s\"",
|
|
|
50f89d |
archivefname);
|
|
|
50f89d |
|
|
|
50f89d |
if (fstat64 (fd, &st) < 0)
|
|
|
50f89d |
error (EXIT_FAILURE, errno, "cannot stat locale archive template file \"%s\"",
|
|
|
50f89d |
archivefname);
|
|
|
50f89d |
|
|
|
50f89d |
/* Read the header. */
|
|
|
50f89d |
if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
|
|
|
50f89d |
error (EXIT_FAILURE, errno, "cannot read archive header");
|
|
|
50f89d |
|
|
|
50f89d |
ah->fd = fd;
|
|
|
50f89d |
ah->mmaped = (head.sumhash_offset
|
|
|
50f89d |
+ head.sumhash_size * sizeof (struct sumhashent));
|
|
|
50f89d |
if (ah->mmaped > (unsigned long) st.st_size)
|
|
|
50f89d |
error (EXIT_FAILURE, 0, "locale archive template file truncated");
|
|
|
50f89d |
ah->mmaped = st.st_size;
|
|
|
50f89d |
ah->reserved = st.st_size;
|
|
|
50f89d |
|
|
|
50f89d |
/* Now we know how large the administrative information part is.
|
|
|
50f89d |
Map all of it. */
|
|
|
50f89d |
ah->addr = mmap64 (NULL, ah->mmaped, PROT_READ, MAP_SHARED, fd, 0);
|
|
|
50f89d |
if (ah->addr == MAP_FAILED)
|
|
|
50f89d |
error (EXIT_FAILURE, errno, "cannot map archive header");
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
/* Open the locale archive. */
|
|
|
50f89d |
extern void open_archive (struct locarhandle *ah, bool readonly);
|
|
|
50f89d |
|
|
|
50f89d |
/* Close the locale archive. */
|
|
|
50f89d |
extern void close_archive (struct locarhandle *ah);
|
|
|
50f89d |
|
|
|
50f89d |
/* Add given locale data to the archive. */
|
|
|
50f89d |
extern int add_locale_to_archive (struct locarhandle *ah, const char *name,
|
|
|
50f89d |
locale_data_t data, bool replace);
|
|
|
50f89d |
|
|
|
50f89d |
extern void add_alias (struct locarhandle *ah, const char *alias,
|
|
|
50f89d |
bool replace, const char *oldname,
|
|
|
50f89d |
uint32_t *locrec_offset_p);
|
|
|
50f89d |
|
|
|
50f89d |
extern struct namehashent *
|
|
|
50f89d |
insert_name (struct locarhandle *ah,
|
|
|
50f89d |
const char *name, size_t name_len, bool replace);
|
|
|
50f89d |
|
|
|
50f89d |
struct nameent
|
|
|
50f89d |
{
|
|
|
50f89d |
char *name;
|
|
|
50f89d |
struct locrecent *locrec;
|
|
|
50f89d |
};
|
|
|
50f89d |
|
|
|
50f89d |
struct dataent
|
|
|
50f89d |
{
|
|
|
50f89d |
const unsigned char *sum;
|
|
|
50f89d |
uint32_t file_offset;
|
|
|
50f89d |
};
|
|
|
50f89d |
|
|
|
50f89d |
static int
|
|
|
50f89d |
nameentcmp (const void *a, const void *b)
|
|
|
50f89d |
{
|
|
|
50f89d |
struct locrecent *la = ((const struct nameent *) a)->locrec;
|
|
|
50f89d |
struct locrecent *lb = ((const struct nameent *) b)->locrec;
|
|
|
50f89d |
uint32_t start_a = -1, end_a = 0;
|
|
|
50f89d |
uint32_t start_b = -1, end_b = 0;
|
|
|
50f89d |
int cnt;
|
|
|
50f89d |
|
|
|
50f89d |
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
|
50f89d |
if (cnt != LC_ALL)
|
|
|
50f89d |
{
|
|
|
50f89d |
if (la->record[cnt].offset < start_a)
|
|
|
50f89d |
start_a = la->record[cnt].offset;
|
|
|
50f89d |
if (la->record[cnt].offset + la->record[cnt].len > end_a)
|
|
|
50f89d |
end_a = la->record[cnt].offset + la->record[cnt].len;
|
|
|
50f89d |
}
|
|
|
50f89d |
assert (start_a != (uint32_t)-1);
|
|
|
50f89d |
assert (end_a != 0);
|
|
|
50f89d |
|
|
|
50f89d |
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
|
50f89d |
if (cnt != LC_ALL)
|
|
|
50f89d |
{
|
|
|
50f89d |
if (lb->record[cnt].offset < start_b)
|
|
|
50f89d |
start_b = lb->record[cnt].offset;
|
|
|
50f89d |
if (lb->record[cnt].offset + lb->record[cnt].len > end_b)
|
|
|
50f89d |
end_b = lb->record[cnt].offset + lb->record[cnt].len;
|
|
|
50f89d |
}
|
|
|
50f89d |
assert (start_b != (uint32_t)-1);
|
|
|
50f89d |
assert (end_b != 0);
|
|
|
50f89d |
|
|
|
50f89d |
if (start_a != start_b)
|
|
|
50f89d |
return (int)start_a - (int)start_b;
|
|
|
50f89d |
return (int)end_a - (int)end_b;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
static int
|
|
|
50f89d |
dataentcmp (const void *a, const void *b)
|
|
|
50f89d |
{
|
|
|
50f89d |
if (((const struct dataent *) a)->file_offset
|
|
|
50f89d |
< ((const struct dataent *) b)->file_offset)
|
|
|
50f89d |
return -1;
|
|
|
50f89d |
|
|
|
50f89d |
if (((const struct dataent *) a)->file_offset
|
|
|
50f89d |
> ((const struct dataent *) b)->file_offset)
|
|
|
50f89d |
return 1;
|
|
|
50f89d |
|
|
|
50f89d |
return 0;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
static int
|
|
|
50f89d |
sumsearchfn (const void *key, const void *ent)
|
|
|
50f89d |
{
|
|
|
50f89d |
uint32_t keyn = *(uint32_t *)key;
|
|
|
50f89d |
uint32_t entn = ((struct dataent *)ent)->file_offset;
|
|
|
50f89d |
|
|
|
50f89d |
if (keyn < entn)
|
|
|
50f89d |
return -1;
|
|
|
50f89d |
if (keyn > entn)
|
|
|
50f89d |
return 1;
|
|
|
50f89d |
return 0;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
static void
|
|
|
50f89d |
compute_data (struct locarhandle *ah, struct nameent *name, size_t sumused,
|
|
|
50f89d |
struct dataent *files, locale_data_t data)
|
|
|
50f89d |
{
|
|
|
50f89d |
int cnt;
|
|
|
50f89d |
struct locrecent *locrec = name->locrec;
|
|
|
50f89d |
struct dataent *file;
|
|
|
50f89d |
data[LC_ALL].addr = ((char *) ah->addr) + locrec->record[LC_ALL].offset;
|
|
|
50f89d |
data[LC_ALL].size = locrec->record[LC_ALL].len;
|
|
|
50f89d |
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
|
50f89d |
if (cnt != LC_ALL)
|
|
|
50f89d |
{
|
|
|
50f89d |
data[cnt].addr = ((char *) ah->addr) + locrec->record[cnt].offset;
|
|
|
50f89d |
data[cnt].size = locrec->record[cnt].len;
|
|
|
50f89d |
if (data[cnt].addr >= data[LC_ALL].addr
|
|
|
50f89d |
&& data[cnt].addr + data[cnt].size
|
|
|
50f89d |
<= data[LC_ALL].addr + data[LC_ALL].size)
|
|
|
50f89d |
__md5_buffer (data[cnt].addr, data[cnt].size, data[cnt].sum);
|
|
|
50f89d |
else
|
|
|
50f89d |
{
|
|
|
50f89d |
file = bsearch (&locrec->record[cnt].offset, files, sumused,
|
|
|
50f89d |
sizeof (*files), sumsearchfn);
|
|
|
50f89d |
if (file == NULL)
|
|
|
50f89d |
error (EXIT_FAILURE, 0, "inconsistent template file");
|
|
|
50f89d |
memcpy (data[cnt].sum, file->sum, sizeof (data[cnt].sum));
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
static int
|
|
|
50f89d |
fill_archive (struct locarhandle *tmpl_ah,
|
|
|
50f89d |
const char *fname,
|
|
|
50f89d |
size_t install_langs_count, char *install_langs_list[],
|
|
|
50f89d |
size_t nlist, char *list[],
|
|
|
50f89d |
const char *primary)
|
|
|
50f89d |
{
|
|
|
50f89d |
struct locarhandle ah;
|
|
|
50f89d |
struct locarhead *head;
|
|
|
50f89d |
int result = 0;
|
|
|
50f89d |
struct nameent *names;
|
|
|
50f89d |
struct namehashent *namehashtab;
|
|
|
50f89d |
size_t cnt, used;
|
|
|
50f89d |
struct dataent *files;
|
|
|
50f89d |
struct sumhashent *sumhashtab;
|
|
|
50f89d |
size_t sumused;
|
|
|
50f89d |
struct locrecent *primary_locrec = NULL;
|
|
|
50f89d |
struct nameent *primary_nameent = NULL;
|
|
|
50f89d |
|
|
|
50f89d |
head = tmpl_ah->addr;
|
|
|
50f89d |
names = (struct nameent *) malloc (head->namehash_used
|
|
|
50f89d |
* sizeof (struct nameent));
|
|
|
50f89d |
files = (struct dataent *) malloc (head->sumhash_used
|
|
|
50f89d |
* sizeof (struct dataent));
|
|
|
50f89d |
if (names == NULL || files == NULL)
|
|
|
50f89d |
error (EXIT_FAILURE, errno, "could not allocate tables");
|
|
|
50f89d |
|
|
|
50f89d |
namehashtab = (struct namehashent *) ((char *) tmpl_ah->addr
|
|
|
50f89d |
+ head->namehash_offset);
|
|
|
50f89d |
sumhashtab = (struct sumhashent *) ((char *) tmpl_ah->addr
|
|
|
50f89d |
+ head->sumhash_offset);
|
|
|
50f89d |
|
|
|
50f89d |
for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
|
|
|
50f89d |
if (namehashtab[cnt].locrec_offset != 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
char * name;
|
|
|
50f89d |
int i;
|
|
|
50f89d |
assert (used < head->namehash_used);
|
|
|
50f89d |
name = tmpl_ah->addr + namehashtab[cnt].name_offset;
|
|
|
50f89d |
if (install_langs_count == 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
/* Always intstall the entry. */
|
|
|
50f89d |
names[used].name = name;
|
|
|
50f89d |
names[used++].locrec
|
|
|
50f89d |
= (struct locrecent *) ((char *) tmpl_ah->addr +
|
|
|
50f89d |
namehashtab[cnt].locrec_offset);
|
|
|
50f89d |
}
|
|
|
50f89d |
else
|
|
|
50f89d |
{
|
|
|
50f89d |
/* Only install the entry if the user asked for it via
|
|
|
50f89d |
--install-langs. */
|
|
|
50f89d |
for (i = 0; i < install_langs_count; i++)
|
|
|
50f89d |
{
|
|
|
50f89d |
/* Add one for "_" and one for the null terminator. */
|
|
|
50f89d |
size_t len = strlen (install_langs_list[i]) + 2;
|
|
|
50f89d |
char *install_lang = (char *)xmalloc (len);
|
|
|
50f89d |
strcpy (install_lang, install_langs_list[i]);
|
|
|
50f89d |
if (strchr (install_lang, '_') == NULL)
|
|
|
50f89d |
strcat (install_lang, "_");
|
|
|
50f89d |
if (strncmp (name, install_lang, strlen (install_lang)) == 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
names[used].name = name;
|
|
|
50f89d |
names[used++].locrec
|
|
|
50f89d |
= (struct locrecent *) ((char *)tmpl_ah->addr
|
|
|
50f89d |
+ namehashtab[cnt].locrec_offset);
|
|
|
50f89d |
}
|
|
|
50f89d |
free (install_lang);
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
/* Sort the names. */
|
|
|
50f89d |
qsort (names, used, sizeof (struct nameent), nameentcmp);
|
|
|
50f89d |
|
|
|
50f89d |
for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
|
|
|
50f89d |
if (sumhashtab[cnt].file_offset != 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
assert (sumused < head->sumhash_used);
|
|
|
50f89d |
files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
|
|
|
50f89d |
files[sumused++].file_offset = sumhashtab[cnt].file_offset;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
/* Sort by file locations. */
|
|
|
50f89d |
qsort (files, sumused, sizeof (struct dataent), dataentcmp);
|
|
|
50f89d |
|
|
|
50f89d |
/* Open the archive. This call never returns if we cannot
|
|
|
50f89d |
successfully open the archive. */
|
|
|
50f89d |
ah.fname = NULL;
|
|
|
50f89d |
if (fname != NULL)
|
|
|
50f89d |
ah.fname = fname;
|
|
|
50f89d |
open_archive (&ah, false);
|
|
|
50f89d |
|
|
|
50f89d |
if (primary != NULL)
|
|
|
50f89d |
{
|
|
|
50f89d |
for (cnt = 0; cnt < used; ++cnt)
|
|
|
50f89d |
if (strcmp (names[cnt].name, primary) == 0)
|
|
|
50f89d |
break;
|
|
|
50f89d |
if (cnt < used)
|
|
|
50f89d |
{
|
|
|
50f89d |
locale_data_t data;
|
|
|
50f89d |
|
|
|
50f89d |
compute_data (tmpl_ah, &names[cnt], sumused, files, data);
|
|
|
50f89d |
result |= add_locale_to_archive (&ah, primary, data, 0);
|
|
|
50f89d |
primary_locrec = names[cnt].locrec;
|
|
|
50f89d |
primary_nameent = &names[cnt];
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
for (cnt = 0; cnt < used; ++cnt)
|
|
|
50f89d |
if (&names[cnt] == primary_nameent)
|
|
|
50f89d |
continue;
|
|
|
50f89d |
else if ((cnt > 0 && names[cnt - 1].locrec == names[cnt].locrec)
|
|
|
50f89d |
|| names[cnt].locrec == primary_locrec)
|
|
|
50f89d |
{
|
|
|
50f89d |
const char *oldname;
|
|
|
50f89d |
struct namehashent *namehashent;
|
|
|
50f89d |
uint32_t locrec_offset;
|
|
|
50f89d |
|
|
|
50f89d |
if (names[cnt].locrec == primary_locrec)
|
|
|
50f89d |
oldname = primary;
|
|
|
50f89d |
else
|
|
|
50f89d |
oldname = names[cnt - 1].name;
|
|
|
50f89d |
namehashent = insert_name (&ah, oldname, strlen (oldname), true);
|
|
|
50f89d |
assert (namehashent->name_offset != 0);
|
|
|
50f89d |
assert (namehashent->locrec_offset != 0);
|
|
|
50f89d |
locrec_offset = namehashent->locrec_offset;
|
|
|
50f89d |
add_alias (&ah, names[cnt].name, 0, oldname, &locrec_offset);
|
|
|
50f89d |
}
|
|
|
50f89d |
else
|
|
|
50f89d |
{
|
|
|
50f89d |
locale_data_t data;
|
|
|
50f89d |
|
|
|
50f89d |
compute_data (tmpl_ah, &names[cnt], sumused, files, data);
|
|
|
50f89d |
result |= add_locale_to_archive (&ah, names[cnt].name, data, 0);
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
while (nlist-- > 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
const char *fname = *list++;
|
|
|
50f89d |
size_t fnamelen = strlen (fname);
|
|
|
50f89d |
struct stat64 st;
|
|
|
50f89d |
DIR *dirp;
|
|
|
50f89d |
struct dirent64 *d;
|
|
|
50f89d |
int seen;
|
|
|
50f89d |
locale_data_t data;
|
|
|
50f89d |
int cnt;
|
|
|
50f89d |
|
|
|
50f89d |
/* First see whether this really is a directory and whether it
|
|
|
50f89d |
contains all the require locale category files. */
|
|
|
50f89d |
if (stat64 (fname, &st) < 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
error (0, 0, "stat of \"%s\" failed: %s: ignored", fname,
|
|
|
50f89d |
strerror (errno));
|
|
|
50f89d |
continue;
|
|
|
50f89d |
}
|
|
|
50f89d |
if (!S_ISDIR (st.st_mode))
|
|
|
50f89d |
{
|
|
|
50f89d |
error (0, 0, "\"%s\" is no directory; ignored", fname);
|
|
|
50f89d |
continue;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
dirp = opendir (fname);
|
|
|
50f89d |
if (dirp == NULL)
|
|
|
50f89d |
{
|
|
|
50f89d |
error (0, 0, "cannot open directory \"%s\": %s: ignored",
|
|
|
50f89d |
fname, strerror (errno));
|
|
|
50f89d |
continue;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
seen = 0;
|
|
|
50f89d |
while ((d = readdir64 (dirp)) != NULL)
|
|
|
50f89d |
{
|
|
|
50f89d |
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
|
50f89d |
if (cnt != LC_ALL)
|
|
|
50f89d |
if (strcmp (d->d_name, locnames[cnt]) == 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
unsigned char d_type;
|
|
|
50f89d |
|
|
|
50f89d |
/* We have an object of the required name. If it's
|
|
|
50f89d |
a directory we have to look at a file with the
|
|
|
50f89d |
prefix "SYS_". Otherwise we have found what we
|
|
|
50f89d |
are looking for. */
|
|
|
50f89d |
#ifdef _DIRENT_HAVE_D_TYPE
|
|
|
50f89d |
d_type = d->d_type;
|
|
|
50f89d |
|
|
|
50f89d |
if (d_type != DT_REG)
|
|
|
50f89d |
#endif
|
|
|
50f89d |
{
|
|
|
50f89d |
char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
|
|
|
50f89d |
|
|
|
50f89d |
#ifdef _DIRENT_HAVE_D_TYPE
|
|
|
50f89d |
if (d_type == DT_UNKNOWN)
|
|
|
50f89d |
#endif
|
|
|
50f89d |
{
|
|
|
50f89d |
strcpy (stpcpy (stpcpy (fullname, fname), "/"),
|
|
|
50f89d |
d->d_name);
|
|
|
50f89d |
|
|
|
50f89d |
if (stat64 (fullname, &st) == -1)
|
|
|
50f89d |
/* We cannot stat the file, ignore it. */
|
|
|
50f89d |
break;
|
|
|
50f89d |
|
|
|
50f89d |
d_type = IFTODT (st.st_mode);
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
if (d_type == DT_DIR)
|
|
|
50f89d |
{
|
|
|
50f89d |
/* We have to do more tests. The file is a
|
|
|
50f89d |
directory and it therefore must contain a
|
|
|
50f89d |
regular file with the same name except a
|
|
|
50f89d |
"SYS_" prefix. */
|
|
|
50f89d |
char *t = stpcpy (stpcpy (fullname, fname), "/");
|
|
|
50f89d |
strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
|
|
|
50f89d |
d->d_name);
|
|
|
50f89d |
|
|
|
50f89d |
if (stat64 (fullname, &st) == -1)
|
|
|
50f89d |
/* There is no SYS_* file or we cannot
|
|
|
50f89d |
access it. */
|
|
|
50f89d |
break;
|
|
|
50f89d |
|
|
|
50f89d |
d_type = IFTODT (st.st_mode);
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
/* If we found a regular file (eventually after
|
|
|
50f89d |
following a symlink) we are successful. */
|
|
|
50f89d |
if (d_type == DT_REG)
|
|
|
50f89d |
++seen;
|
|
|
50f89d |
break;
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
closedir (dirp);
|
|
|
50f89d |
|
|
|
50f89d |
if (seen != __LC_LAST - 1)
|
|
|
50f89d |
{
|
|
|
50f89d |
/* We don't have all locale category files. Ignore the name. */
|
|
|
50f89d |
error (0, 0, "incomplete set of locale files in \"%s\"",
|
|
|
50f89d |
fname);
|
|
|
50f89d |
continue;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
/* Add the files to the archive. To do this we first compute
|
|
|
50f89d |
sizes and the MD5 sums of all the files. */
|
|
|
50f89d |
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
|
50f89d |
if (cnt != LC_ALL)
|
|
|
50f89d |
{
|
|
|
50f89d |
char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
|
|
|
50f89d |
int fd;
|
|
|
50f89d |
|
|
|
50f89d |
strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
|
|
|
50f89d |
fd = open64 (fullname, O_RDONLY);
|
|
|
50f89d |
if (fd == -1 || fstat64 (fd, &st) == -1)
|
|
|
50f89d |
{
|
|
|
50f89d |
/* Cannot read the file. */
|
|
|
50f89d |
if (fd != -1)
|
|
|
50f89d |
close (fd);
|
|
|
50f89d |
break;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
if (S_ISDIR (st.st_mode))
|
|
|
50f89d |
{
|
|
|
50f89d |
char *t;
|
|
|
50f89d |
close (fd);
|
|
|
50f89d |
t = stpcpy (stpcpy (fullname, fname), "/");
|
|
|
50f89d |
strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
|
|
|
50f89d |
locnames[cnt]);
|
|
|
50f89d |
|
|
|
50f89d |
fd = open64 (fullname, O_RDONLY);
|
|
|
50f89d |
if (fd == -1 || fstat64 (fd, &st) == -1
|
|
|
50f89d |
|| !S_ISREG (st.st_mode))
|
|
|
50f89d |
{
|
|
|
50f89d |
if (fd != -1)
|
|
|
50f89d |
close (fd);
|
|
|
50f89d |
break;
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
/* Map the file. */
|
|
|
50f89d |
data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
|
|
|
50f89d |
fd, 0);
|
|
|
50f89d |
if (data[cnt].addr == MAP_FAILED)
|
|
|
50f89d |
{
|
|
|
50f89d |
/* Cannot map it. */
|
|
|
50f89d |
close (fd);
|
|
|
50f89d |
break;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
data[cnt].size = st.st_size;
|
|
|
50f89d |
__md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
|
|
|
50f89d |
|
|
|
50f89d |
/* We don't need the file descriptor anymore. */
|
|
|
50f89d |
close (fd);
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
if (cnt != __LC_LAST)
|
|
|
50f89d |
{
|
|
|
50f89d |
while (cnt-- > 0)
|
|
|
50f89d |
if (cnt != LC_ALL)
|
|
|
50f89d |
munmap (data[cnt].addr, data[cnt].size);
|
|
|
50f89d |
|
|
|
50f89d |
error (0, 0, "cannot read all files in \"%s\": ignored", fname);
|
|
|
50f89d |
|
|
|
50f89d |
continue;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
result |= add_locale_to_archive (&ah, basename (fname), data, 0);
|
|
|
50f89d |
|
|
|
50f89d |
for (cnt = 0; cnt < __LC_LAST; ++cnt)
|
|
|
50f89d |
if (cnt != LC_ALL)
|
|
|
50f89d |
munmap (data[cnt].addr, data[cnt].size);
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
/* We are done. */
|
|
|
50f89d |
close_archive (&ah;;
|
|
|
50f89d |
|
|
|
50f89d |
return result;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
void usage()
|
|
|
50f89d |
{
|
|
|
50f89d |
printf ("\
|
|
|
50f89d |
Usage: build-locale-archive [OPTION]... [TEMPLATE-FILE] [ARCHIVE-FILE]\n\
|
|
|
50f89d |
Builds a locale archive from a template file.\n\
|
|
|
50f89d |
Options:\n\
|
|
|
50f89d |
-h, --help Print this usage message.\n\
|
|
|
50f89d |
-v, --verbose Verbose execution.\n\
|
|
|
50f89d |
-l, --install-langs=LIST Only include locales given in LIST into the \n\
|
|
|
50f89d |
locale archive. LIST is a colon separated list\n\
|
|
|
50f89d |
of locale prefixes, for example \"de:en:ja\".\n\
|
|
|
50f89d |
The special argument \"all\" means to install\n\
|
|
|
50f89d |
all languages and it must be present by itself.\n\
|
|
|
50f89d |
If \"all\" is present with any other language it\n\
|
|
|
50f89d |
will be treated as the name of a locale.\n\
|
|
|
50f89d |
If the --install-langs option is missing, all\n\
|
|
|
50f89d |
locales are installed. The colon separated list\n\
|
|
|
50f89d |
can contain any strings matching the beginning of\n\
|
|
|
50f89d |
locale names.\n\
|
|
|
50f89d |
If a string does not contain a \"_\", it is added.\n\
|
|
|
50f89d |
Examples:\n\
|
|
|
50f89d |
--install-langs=\"en\"\n\
|
|
|
50f89d |
installs en_US, en_US.iso88591,\n\
|
|
|
50f89d |
en_US.iso885915, en_US.utf8,\n\
|
|
|
50f89d |
en_GB ...\n\
|
|
|
50f89d |
--install-langs=\"en_US.utf8\"\n\
|
|
|
50f89d |
installs only en_US.utf8.\n\
|
|
|
50f89d |
--install-langs=\"ko\"\n\
|
|
|
50f89d |
installs ko_KR, ko_KR.euckr,\n\
|
|
|
50f89d |
ko_KR.utf8 but *not* kok_IN\n\
|
|
|
50f89d |
because \"ko\" does not contain\n\
|
|
|
50f89d |
\"_\" and it is silently added\n\
|
|
|
50f89d |
--install-langs\"ko:kok\"\n\
|
|
|
50f89d |
installs ko_KR, ko_KR.euckr,\n\
|
|
|
50f89d |
ko_KR.utf8, kok_IN, and\n\
|
|
|
50f89d |
kok_IN.utf8.\n\
|
|
|
50f89d |
--install-langs=\"POSIX\" will\n\
|
|
|
50f89d |
installs *no* locales at all\n\
|
|
|
50f89d |
because POSIX matches none of\n\
|
|
|
50f89d |
the locales. Actually, any string\n\
|
|
|
50f89d |
matching nothing will do that.\n\
|
|
|
50f89d |
POSIX and C will always be\n\
|
|
|
50f89d |
available because they are\n\
|
|
|
50f89d |
builtin.\n\
|
|
|
50f89d |
Aliases are installed as well,\n\
|
|
|
50f89d |
i.e. --install-langs=\"de\"\n\
|
|
|
50f89d |
will install not only every locale starting with\n\
|
|
|
50f89d |
\"de\" but also the aliases \"deutsch\"\n\
|
|
|
50f89d |
and and \"german\" although the latter does not\n\
|
|
|
50f89d |
start with \"de\".\n\
|
|
|
50f89d |
\n\
|
|
|
50f89d |
If the arguments TEMPLATE-FILE and ARCHIVE-FILE are not given the locations\n\
|
|
|
50f89d |
where the glibc used expects these files are used by default.\n\
|
|
|
50f89d |
");
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
int main (int argc, char *argv[])
|
|
|
50f89d |
{
|
|
|
50f89d |
char path[4096];
|
|
|
50f89d |
DIR *dirp;
|
|
|
50f89d |
struct dirent64 *d;
|
|
|
50f89d |
struct stat64 st;
|
|
|
50f89d |
char *list[16384], *primary;
|
|
|
50f89d |
char *lang;
|
|
|
50f89d |
int install_langs_count = 0;
|
|
|
50f89d |
int i;
|
|
|
50f89d |
char *install_langs_arg, *ila_start;
|
|
|
50f89d |
char **install_langs_list = NULL;
|
|
|
50f89d |
unsigned int cnt = 0;
|
|
|
50f89d |
struct locarhandle tmpl_ah;
|
|
|
50f89d |
char *new_locar_fname = NULL;
|
|
|
50f89d |
size_t loc_path_len = strlen (loc_path);
|
|
|
50f89d |
|
|
|
50f89d |
while (1)
|
|
|
50f89d |
{
|
|
|
50f89d |
int c;
|
|
|
50f89d |
|
|
|
50f89d |
static struct option long_options[] =
|
|
|
50f89d |
{
|
|
|
50f89d |
{"help", no_argument, 0, 'h'},
|
|
|
50f89d |
{"verbose", no_argument, 0, 'v'},
|
|
|
50f89d |
{"install-langs", required_argument, 0, 'l'},
|
|
|
50f89d |
{0, 0, 0, 0}
|
|
|
50f89d |
};
|
|
|
50f89d |
/* getopt_long stores the option index here. */
|
|
|
50f89d |
int option_index = 0;
|
|
|
50f89d |
|
|
|
50f89d |
c = getopt_long (argc, argv, "vhl:",
|
|
|
50f89d |
long_options, &option_index);
|
|
|
50f89d |
|
|
|
50f89d |
/* Detect the end of the options. */
|
|
|
50f89d |
if (c == -1)
|
|
|
50f89d |
break;
|
|
|
50f89d |
|
|
|
50f89d |
switch (c)
|
|
|
50f89d |
{
|
|
|
50f89d |
case 0:
|
|
|
50f89d |
printf ("unknown option %s", long_options[option_index].name);
|
|
|
50f89d |
if (optarg)
|
|
|
50f89d |
printf (" with arg %s", optarg);
|
|
|
50f89d |
printf ("\n");
|
|
|
50f89d |
usage ();
|
|
|
50f89d |
exit (1);
|
|
|
50f89d |
|
|
|
50f89d |
case 'v':
|
|
|
50f89d |
verbose = 1;
|
|
|
50f89d |
be_quiet = 0;
|
|
|
50f89d |
break;
|
|
|
50f89d |
|
|
|
50f89d |
case 'h':
|
|
|
50f89d |
usage ();
|
|
|
50f89d |
exit (0);
|
|
|
50f89d |
|
|
|
50f89d |
case 'l':
|
|
|
50f89d |
install_langs_arg = ila_start = strdup (optarg);
|
|
|
50f89d |
/* If the argument to --install-lang is "all", do
|
|
|
50f89d |
not limit the list of languages to install and install
|
|
|
50f89d |
them all. We do not support installing a single locale
|
|
|
50f89d |
called "all". */
|
|
|
50f89d |
#define MAGIC_INSTALL_ALL "all"
|
|
|
50f89d |
if (install_langs_arg != NULL
|
|
|
50f89d |
&& install_langs_arg[0] != '\0'
|
|
|
50f89d |
&& !(strncmp(install_langs_arg, MAGIC_INSTALL_ALL,
|
|
|
50f89d |
strlen(MAGIC_INSTALL_ALL)) == 0
|
|
|
50f89d |
&& strlen (install_langs_arg) == 3))
|
|
|
50f89d |
{
|
|
|
50f89d |
/* Count the number of languages we will install. */
|
|
|
50f89d |
while (true)
|
|
|
50f89d |
{
|
|
|
50f89d |
lang = strtok(install_langs_arg, ":;,");
|
|
|
50f89d |
if (lang == NULL)
|
|
|
50f89d |
break;
|
|
|
50f89d |
install_langs_count++;
|
|
|
50f89d |
install_langs_arg = NULL;
|
|
|
50f89d |
}
|
|
|
50f89d |
free (ila_start);
|
|
|
50f89d |
|
|
|
50f89d |
/* Reject an entire string made up of delimiters. */
|
|
|
50f89d |
if (install_langs_count == 0)
|
|
|
50f89d |
break;
|
|
|
50f89d |
|
|
|
50f89d |
/* Copy the list. */
|
|
|
50f89d |
install_langs_list = (char **)xmalloc (sizeof(char *) * install_langs_count);
|
|
|
50f89d |
install_langs_arg = ila_start = strdup (optarg);
|
|
|
50f89d |
install_langs_count = 0;
|
|
|
50f89d |
while (true)
|
|
|
50f89d |
{
|
|
|
50f89d |
lang = strtok(install_langs_arg, ":;,");
|
|
|
50f89d |
if (lang == NULL)
|
|
|
50f89d |
break;
|
|
|
50f89d |
install_langs_list[install_langs_count] = lang;
|
|
|
50f89d |
install_langs_count++;
|
|
|
50f89d |
install_langs_arg = NULL;
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
break;
|
|
|
50f89d |
|
|
|
50f89d |
case '?':
|
|
|
50f89d |
/* getopt_long already printed an error message. */
|
|
|
50f89d |
usage ();
|
|
|
50f89d |
exit (0);
|
|
|
50f89d |
|
|
|
50f89d |
default:
|
|
|
50f89d |
abort ();
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
tmpl_ah.fname = NULL;
|
|
|
50f89d |
if (optind < argc)
|
|
|
50f89d |
tmpl_ah.fname = argv[optind];
|
|
|
50f89d |
if (optind + 1 < argc)
|
|
|
50f89d |
new_locar_fname = argv[optind + 1];
|
|
|
50f89d |
if (verbose)
|
|
|
50f89d |
{
|
|
|
50f89d |
if (tmpl_ah.fname)
|
|
|
50f89d |
printf("input archive file specified on command line: %s\n",
|
|
|
50f89d |
tmpl_ah.fname);
|
|
|
50f89d |
else
|
|
|
50f89d |
printf("using default input archive file.\n");
|
|
|
50f89d |
if (new_locar_fname)
|
|
|
50f89d |
printf("output archive file specified on command line: %s\n",
|
|
|
50f89d |
new_locar_fname);
|
|
|
50f89d |
else
|
|
|
50f89d |
printf("using default output archive file.\n");
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
dirp = opendir (loc_path);
|
|
|
50f89d |
if (dirp == NULL)
|
|
|
50f89d |
error (EXIT_FAILURE, errno, "cannot open directory \"%s\"", loc_path);
|
|
|
50f89d |
|
|
|
50f89d |
open_tmpl_archive (&tmpl_ah);
|
|
|
50f89d |
|
|
|
50f89d |
if (new_locar_fname)
|
|
|
50f89d |
unlink (new_locar_fname);
|
|
|
50f89d |
else
|
|
|
50f89d |
unlink (locar_file);
|
|
|
50f89d |
primary = getenv ("LC_ALL");
|
|
|
50f89d |
if (primary == NULL)
|
|
|
50f89d |
primary = getenv ("LANG");
|
|
|
50f89d |
if (primary != NULL)
|
|
|
50f89d |
{
|
|
|
50f89d |
if (strncmp (primary, "ja", 2) != 0
|
|
|
50f89d |
&& strncmp (primary, "ko", 2) != 0
|
|
|
50f89d |
&& strncmp (primary, "zh", 2) != 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
char *ptr = malloc (strlen (primary) + strlen (".utf8") + 1), *p, *q;
|
|
|
50f89d |
/* This leads to invalid locales sometimes:
|
|
|
50f89d |
de_DE.iso885915@euro -> de_DE.utf8@euro */
|
|
|
50f89d |
if (ptr != NULL)
|
|
|
50f89d |
{
|
|
|
50f89d |
p = ptr;
|
|
|
50f89d |
q = primary;
|
|
|
50f89d |
while (*q && *q != '.' && *q != '@')
|
|
|
50f89d |
*p++ = *q++;
|
|
|
50f89d |
if (*q == '.')
|
|
|
50f89d |
while (*q && *q != '@')
|
|
|
50f89d |
q++;
|
|
|
50f89d |
p = stpcpy (p, ".utf8");
|
|
|
50f89d |
strcpy (p, q);
|
|
|
50f89d |
primary = ptr;
|
|
|
50f89d |
}
|
|
|
50f89d |
else
|
|
|
50f89d |
primary = NULL;
|
|
|
50f89d |
}
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
memcpy (path, loc_path, loc_path_len);
|
|
|
50f89d |
|
|
|
50f89d |
while ((d = readdir64 (dirp)) != NULL)
|
|
|
50f89d |
{
|
|
|
50f89d |
if (strcmp (d->d_name, ".") == 0 || strcmp (d->d_name, "..") == 0)
|
|
|
50f89d |
continue;
|
|
|
50f89d |
if (strchr (d->d_name, '_') == NULL)
|
|
|
50f89d |
continue;
|
|
|
50f89d |
|
|
|
50f89d |
size_t d_name_len = strlen (d->d_name);
|
|
|
50f89d |
if (loc_path_len + d_name_len + 1 > sizeof (path))
|
|
|
50f89d |
{
|
|
|
50f89d |
error (0, 0, "too long filename \"%s\"", d->d_name);
|
|
|
50f89d |
continue;
|
|
|
50f89d |
}
|
|
|
50f89d |
|
|
|
50f89d |
memcpy (path + loc_path_len, d->d_name, d_name_len + 1);
|
|
|
50f89d |
if (stat64 (path, &st) < 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
error (0, errno, "cannot stat \"%s\"", path);
|
|
|
50f89d |
continue;
|
|
|
50f89d |
}
|
|
|
50f89d |
if (! S_ISDIR (st.st_mode))
|
|
|
50f89d |
continue;
|
|
|
50f89d |
if (cnt == 16384)
|
|
|
50f89d |
{
|
|
|
50f89d |
error (0, 0, "too many directories in \"%s\"", loc_path);
|
|
|
50f89d |
break;
|
|
|
50f89d |
}
|
|
|
50f89d |
list[cnt] = strdup (path);
|
|
|
50f89d |
if (list[cnt] == NULL)
|
|
|
50f89d |
{
|
|
|
50f89d |
error (0, errno, "cannot add file to list \"%s\"", path);
|
|
|
50f89d |
continue;
|
|
|
50f89d |
}
|
|
|
50f89d |
if (primary != NULL && cnt > 0 && strcmp (primary, d->d_name) == 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
char *p = list[0];
|
|
|
50f89d |
list[0] = list[cnt];
|
|
|
50f89d |
list[cnt] = p;
|
|
|
50f89d |
}
|
|
|
50f89d |
cnt++;
|
|
|
50f89d |
}
|
|
|
50f89d |
closedir (dirp);
|
|
|
50f89d |
/* Store the archive to the file specified as the second argument on the
|
|
|
50f89d |
command line or the default locale archive. */
|
|
|
50f89d |
fill_archive (&tmpl_ah, new_locar_fname,
|
|
|
50f89d |
install_langs_count, install_langs_list,
|
|
|
50f89d |
cnt, list, primary);
|
|
|
50f89d |
close_archive (&tmpl_ah);
|
|
|
50f89d |
truncate (tmpl_file, 0);
|
|
|
50f89d |
if (install_langs_count > 0)
|
|
|
50f89d |
{
|
|
|
50f89d |
free (ila_start);
|
|
|
50f89d |
free (install_langs_list);
|
|
|
50f89d |
}
|
|
|
50f89d |
char *tz_argv[] = { "/usr/sbin/tzdata-update", NULL };
|
|
|
50f89d |
execve (tz_argv[0], (char *const *)tz_argv, (char *const *)&tz_argv[1]);
|
|
|
50f89d |
exit (0);
|
|
|
50f89d |
}
|