Blame SPECS/build-locale-archive.c

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