bca718
Adds --install-langs support build-locale-archive.
bca718
bca718
commit 0457f649e3fe6299efe384da13dfc923bbe65707
bca718
Author: Carlos O'Donell <carlos@systemhalted.org>
bca718
Date:   Thu Sep 17 12:24:49 2015 -0400
bca718
bca718
[snip]
bca718
    - Fix --install-langs bug which causes SIGABRT (#1262040).
bca718
bca718
commit 91764bd9ec690d4b8a886c0a3a104aac12d340d2
bca718
Author: Carlos O'Donell <carlos@systemhalted.org>
bca718
Date:   Thu Mar 5 16:05:43 2015 -0500
bca718
bca718
    Resolves: #156477
bca718
    
bca718
    - Support installing only those locales specified by the RPM macro
bca718
      %%_install_langs (#156477).
bca718
bca718
Index: glibc-2.17-c758a686/releng/build-locale-archive.c
bca718
===================================================================
bca718
--- glibc-2.17-c758a686.orig/releng/build-locale-archive.c
bca718
+++ glibc-2.17-c758a686/releng/build-locale-archive.c
bca718
@@ -8,6 +8,7 @@
bca718
 #include <stdbool.h>
bca718
 #include <stdio.h>
bca718
 #include <stdlib.h>
bca718
+#include <getopt.h>
bca718
 #include <string.h>
bca718
 #include <sys/mman.h>
bca718
 #include <sys/stat.h>
bca718
@@ -21,6 +22,7 @@ const char *alias_file = DATADIR "/local
bca718
 const char *locar_file = PREFIX "/lib/locale/locale-archive";
bca718
 const char *tmpl_file = PREFIX "/lib/locale/locale-archive.tmpl";
bca718
 const char *loc_path = PREFIX "/lib/locale/";
bca718
+/* Flags set by `--verbose` option.  */
bca718
 int be_quiet = 1;
bca718
 int verbose = 0;
bca718
 int max_locarchive_open_retry = 10;
bca718
@@ -96,7 +98,7 @@ open_tmpl_archive (struct locarhandle *a
bca718
   struct stat64 st;
bca718
   int fd;
bca718
   struct locarhead head;
bca718
-  const char *archivefname = tmpl_file;
bca718
+  const char *archivefname = ah->fname == NULL ? tmpl_file : ah->fname;
bca718
 
bca718
   /* Open the archive.  We must have exclusive write access.  */
bca718
   fd = open64 (archivefname, O_RDONLY);
bca718
@@ -116,7 +118,7 @@ open_tmpl_archive (struct locarhandle *a
bca718
   ah->mmaped = (head.sumhash_offset
bca718
 		+ head.sumhash_size * sizeof (struct sumhashent));
bca718
   if (ah->mmaped > (unsigned long) st.st_size)
bca718
-    error (EXIT_FAILURE, 0, "locale archite template file truncated");
bca718
+    error (EXIT_FAILURE, 0, "locale archive template file truncated");
bca718
   ah->mmaped = st.st_size;
bca718
   ah->reserved = st.st_size;
bca718
 
bca718
@@ -250,7 +252,10 @@ compute_data (struct locarhandle *ah, st
bca718
 }
bca718
 
bca718
 static int
bca718
-fill_archive (struct locarhandle *tmpl_ah, size_t nlist, char *list[],
bca718
+fill_archive (struct locarhandle *tmpl_ah,
bca718
+	      const char *fname,
bca718
+	      size_t install_langs_count, char *install_langs_list[],
bca718
+	      size_t nlist, char *list[],
bca718
 	      const char *primary)
bca718
 {
bca718
   struct locarhandle ah;
bca718
@@ -281,11 +286,40 @@ fill_archive (struct locarhandle *tmpl_a
bca718
   for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
bca718
     if (namehashtab[cnt].locrec_offset != 0)
bca718
       {
bca718
+	char * name;
bca718
+	int i;
bca718
 	assert (used < head->namehash_used);
bca718
-	names[used].name = tmpl_ah->addr + namehashtab[cnt].name_offset;
bca718
-	names[used++].locrec
bca718
-	  = (struct locrecent *) ((char *) tmpl_ah->addr +
bca718
-				  namehashtab[cnt].locrec_offset);
bca718
+        name = tmpl_ah->addr + namehashtab[cnt].name_offset;
bca718
+        if (install_langs_count == 0)
bca718
+          {
bca718
+	    /* Always intstall the entry.  */
bca718
+            names[used].name = name;
bca718
+            names[used++].locrec
bca718
+                = (struct locrecent *) ((char *) tmpl_ah->addr +
bca718
+                                        namehashtab[cnt].locrec_offset);
bca718
+          }
bca718
+        else
bca718
+          {
bca718
+	    /* Only install the entry if the user asked for it via
bca718
+	       --install-langs.  */
bca718
+            for (i = 0; i < install_langs_count; i++)
bca718
+              {
bca718
+		/* Add one for "_" and one for the null terminator.  */
bca718
+		size_t len = strlen (install_langs_list[i]) + 2;
bca718
+		char *install_lang = (char *)xmalloc (len);
bca718
+                strcpy (install_lang, install_langs_list[i]);
bca718
+                if (strchr (install_lang, '_') == NULL)
bca718
+                  strcat (install_lang, "_");
bca718
+                if (strncmp (name, install_lang, strlen (install_lang)) == 0)
bca718
+                  {
bca718
+                    names[used].name = name;
bca718
+                    names[used++].locrec
bca718
+		      = (struct locrecent *) ((char *)tmpl_ah->addr
bca718
+					      + namehashtab[cnt].locrec_offset);
bca718
+                  }
bca718
+		free (install_lang);
bca718
+              }
bca718
+          }
bca718
       }
bca718
 
bca718
   /* Sort the names.  */
bca718
@@ -304,6 +338,9 @@ fill_archive (struct locarhandle *tmpl_a
bca718
 
bca718
   /* Open the archive.  This call never returns if we cannot
bca718
      successfully open the archive.  */
bca718
+  ah.fname = NULL;
bca718
+  if (fname != NULL)
bca718
+    ah.fname = fname;
bca718
   open_archive (&ah, false);
bca718
 
bca718
   if (primary != NULL)
bca718
@@ -532,24 +569,197 @@ fill_archive (struct locarhandle *tmpl_a
bca718
   return result;
bca718
 }
bca718
 
bca718
-int main ()
bca718
+void usage()
bca718
+{
bca718
+  printf ("\
bca718
+Usage: build-locale-archive [OPTION]... [TEMPLATE-FILE] [ARCHIVE-FILE]\n\
bca718
+ Builds a locale archive from a template file.\n\
bca718
+ Options:\n\
bca718
+  -h, --help                 Print this usage message.\n\
bca718
+  -v, --verbose              Verbose execution.\n\
bca718
+  -l, --install-langs=LIST   Only include locales given in LIST into the \n\
bca718
+                             locale archive.  LIST is a colon separated list\n\
bca718
+                             of locale prefixes, for example \"de:en:ja\".\n\
bca718
+                             The special argument \"all\" means to install\n\
bca718
+                             all languages and it must be present by itself.\n\
bca718
+                             If \"all\" is present with any other language it\n\
bca718
+                             will be treated as the name of a locale.\n\
bca718
+                             If the --install-langs option is missing, all\n\
bca718
+                             locales are installed. The colon separated list\n\
bca718
+                             can contain any strings matching the beginning of\n\
bca718
+                             locale names.\n\
bca718
+                             If a string does not contain a \"_\", it is added.\n\
bca718
+                             Examples:\n\
bca718
+                               --install-langs=\"en\"\n\
bca718
+                                 installs en_US, en_US.iso88591,\n\
bca718
+                                 en_US.iso885915, en_US.utf8,\n\
bca718
+                                 en_GB ...\n\
bca718
+                               --install-langs=\"en_US.utf8\"\n\
bca718
+                                 installs only en_US.utf8.\n\
bca718
+                               --install-langs=\"ko\"\n\
bca718
+                                 installs ko_KR, ko_KR.euckr,\n\
bca718
+                                 ko_KR.utf8 but *not* kok_IN\n\
bca718
+                                 because \"ko\" does not contain\n\
bca718
+                                 \"_\" and it is silently added\n\
bca718
+                               --install-langs\"ko:kok\"\n\
bca718
+                                 installs ko_KR, ko_KR.euckr,\n\
bca718
+                                 ko_KR.utf8, kok_IN, and\n\
bca718
+                                 kok_IN.utf8.\n\
bca718
+                               --install-langs=\"POSIX\" will\n\
bca718
+                                 installs *no* locales at all\n\
bca718
+                                 because POSIX matches none of\n\
bca718
+                                 the locales. Actually, any string\n\
bca718
+                                 matching nothing will do that.\n\
bca718
+                                 POSIX and C will always be\n\
bca718
+                                 available because they are\n\
bca718
+                                 builtin.\n\
bca718
+                             Aliases are installed as well,\n\
bca718
+                             i.e. --install-langs=\"de\"\n\
bca718
+                             will install not only every locale starting with\n\
bca718
+                             \"de\" but also the aliases \"deutsch\"\n\
bca718
+                             and and \"german\" although the latter does not\n\
bca718
+                             start with \"de\".\n\
bca718
+\n\
bca718
+  If the arguments TEMPLATE-FILE and ARCHIVE-FILE are not given the locations\n\
bca718
+  where the glibc used expects these files are used by default.\n\
bca718
+");
bca718
+}
bca718
+
bca718
+int main (int argc, char *argv[])
bca718
 {
bca718
   char path[4096];
bca718
   DIR *dirp;
bca718
   struct dirent64 *d;
bca718
   struct stat64 st;
bca718
   char *list[16384], *primary;
bca718
+  char *lang;
bca718
+  int install_langs_count = 0;
bca718
+  int i;
bca718
+  char *install_langs_arg, *ila_start;
bca718
+  char **install_langs_list;
bca718
   unsigned int cnt = 0;
bca718
   struct locarhandle tmpl_ah;
bca718
+  char *new_locar_fname = NULL;
bca718
   size_t loc_path_len = strlen (loc_path);
bca718
 
bca718
+  while (1)
bca718
+    {
bca718
+      int c;
bca718
+
bca718
+      static struct option long_options[] =
bca718
+        {
bca718
+            {"help",            no_argument,       0, 'h'},
bca718
+            {"verbose",         no_argument,       0, 'v'},
bca718
+            {"install-langs",   required_argument, 0, 'l'},
bca718
+            {0, 0, 0, 0}
bca718
+        };
bca718
+      /* getopt_long stores the option index here. */
bca718
+      int option_index = 0;
bca718
+
bca718
+      c = getopt_long (argc, argv, "vhl:",
bca718
+                       long_options, &option_index);
bca718
+
bca718
+      /* Detect the end of the options. */
bca718
+      if (c == -1)
bca718
+        break;
bca718
+
bca718
+      switch (c)
bca718
+        {
bca718
+        case 0:
bca718
+          printf ("unknown option %s", long_options[option_index].name);
bca718
+          if (optarg)
bca718
+            printf (" with arg %s", optarg);
bca718
+          printf ("\n");
bca718
+          usage ();
bca718
+          exit (1);
bca718
+
bca718
+        case 'v':
bca718
+          verbose = 1;
bca718
+          be_quiet = 0;
bca718
+          break;
bca718
+
bca718
+        case 'h':
bca718
+          usage ();
bca718
+          exit (0);
bca718
+
bca718
+        case 'l':
bca718
+          install_langs_arg = ila_start = strdup (optarg);
bca718
+          /* If the argument to --install-lang is "all", do
bca718
+             not limit the list of languages to install and install
bca718
+             them all.  We do not support installing a single locale
bca718
+	     called "all".  */
bca718
+#define MAGIC_INSTALL_ALL "all"
bca718
+          if (install_langs_arg != NULL
bca718
+	      && install_langs_arg[0] != '\0'
bca718
+	      && !(strncmp(install_langs_arg, MAGIC_INSTALL_ALL,
bca718
+			   strlen(MAGIC_INSTALL_ALL)) == 0
bca718
+		   && strlen (install_langs_arg) == 3))
bca718
+            {
bca718
+	      /* Count the number of languages we will install.  */
bca718
+              while (true)
bca718
+                {
bca718
+                  lang = strtok(install_langs_arg, ":;,");
bca718
+                  if (lang == NULL)
bca718
+                    break;
bca718
+                  install_langs_count++;
bca718
+                  install_langs_arg = NULL;
bca718
+                }
bca718
+	      free (ila_start);
bca718
+	      /* Copy the list.  */
bca718
+	      install_langs_list = (char **)xmalloc (sizeof(char *) * install_langs_count);
bca718
+	      install_langs_arg = ila_start = strdup (optarg);
bca718
+	      install_langs_count = 0;
bca718
+	      while (true)
bca718
+                {
bca718
+                  lang = strtok(install_langs_arg, ":;,");
bca718
+                  if (lang == NULL)
bca718
+                    break;
bca718
+                  install_langs_list[install_langs_count] = lang;
bca718
+		  install_langs_count++;
bca718
+                  install_langs_arg = NULL;
bca718
+                }
bca718
+            }
bca718
+          break;
bca718
+
bca718
+        case '?':
bca718
+          /* getopt_long already printed an error message. */
bca718
+          usage ();
bca718
+          exit (0);
bca718
+
bca718
+        default:
bca718
+          abort ();
bca718
+        }
bca718
+    }
bca718
+  tmpl_ah.fname = NULL;
bca718
+  if (optind < argc)
bca718
+    tmpl_ah.fname = argv[optind];
bca718
+  if (optind + 1 < argc)
bca718
+    new_locar_fname = argv[optind + 1];
bca718
+  if (verbose)
bca718
+    {
bca718
+      if (tmpl_ah.fname)
bca718
+        printf("input archive file specified on command line: %s\n",
bca718
+               tmpl_ah.fname);
bca718
+      else
bca718
+        printf("using default input archive file.\n");
bca718
+      if (new_locar_fname)
bca718
+        printf("output archive file specified on command line: %s\n",
bca718
+               new_locar_fname);
bca718
+      else
bca718
+        printf("using default output archive file.\n");
bca718
+    }
bca718
+
bca718
   dirp = opendir (loc_path);
bca718
   if (dirp == NULL)
bca718
     error (EXIT_FAILURE, errno, "cannot open directory \"%s\"", loc_path);
bca718
 
bca718
   open_tmpl_archive (&tmpl_ah);
bca718
 
bca718
-  unlink (locar_file);
bca718
+  if (new_locar_fname)
bca718
+    unlink (new_locar_fname);
bca718
+  else
bca718
+    unlink (locar_file);
bca718
+
bca718
   primary = getenv ("LC_ALL");
bca718
   if (primary == NULL)
bca718
     primary = getenv ("LANG");
bca718
@@ -560,7 +770,8 @@ int main ()
bca718
 	  && strncmp (primary, "zh", 2) != 0)
bca718
 	{
bca718
 	  char *ptr = malloc (strlen (primary) + strlen (".utf8") + 1), *p, *q;
bca718
-
bca718
+	  /* This leads to invalid locales sometimes:
bca718
+	     de_DE.iso885915@euro -> de_DE.utf8@euro */
bca718
 	  if (ptr != NULL)
bca718
 	    {
bca718
 	      p = ptr;
bca718
@@ -623,10 +834,19 @@ int main ()
bca718
       cnt++;
bca718
     }
bca718
   closedir (dirp);
bca718
-  fill_archive (&tmpl_ah, cnt, list, primary);
bca718
+  /* Store the archive to the file specified as the second argument on the
bca718
+     command line or the default locale archive.  */
bca718
+  fill_archive (&tmpl_ah, new_locar_fname,
bca718
+                install_langs_count, install_langs_list,
bca718
+                cnt, list, primary);
bca718
   close_archive (&tmpl_ah);
bca718
   truncate (tmpl_file, 0);
bca718
-  char *argv[] = { "/usr/sbin/tzdata-update", NULL };
bca718
-  execve (argv[0], (char *const *)argv, (char *const *)&argv[1]);
bca718
+  if (install_langs_count > 0)
bca718
+    {
bca718
+      free (ila_start);
bca718
+      free (install_langs_list);
bca718
+    }
bca718
+  char *tz_argv[] = { "/usr/sbin/tzdata-update", NULL };
bca718
+  execve (tz_argv[0], (char *const *)tz_argv, (char *const *)&tz_argv[1]);
bca718
   exit (0);
bca718
 }