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