From 91764bd9ec690d4b8a886c0a3a104aac12d340d2 Mon Sep 17 00:00:00 2001 From: Carlos O'Donell Date: Thu, 5 Mar 2015 16:05:43 -0500 Subject: [PATCH] Resolves: #156477 - Support installing only those locales specified by the RPM macro %%_install_langs (#156477). --- build-locale-archive.c | 237 ++++++++++++++++++++++++++++++++++++++--- glibc.spec | 14 ++- 2 files changed, 232 insertions(+), 19 deletions(-) diff --git a/build-locale-archive.c b/build-locale-archive.c index 187c7e5..a506876 100644 --- a/build-locale-archive.c +++ b/build-locale-archive.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ const char *alias_file = DATADIR "/locale/locale.alias"; const char *locar_file = PREFIX "/lib/locale/locale-archive"; const char *tmpl_file = PREFIX "/lib/locale/locale-archive.tmpl"; const char *loc_path = PREFIX "/lib/locale/"; +/* Flags set by `--verbose` option. */ int be_quiet = 1; int verbose = 0; int max_locarchive_open_retry = 10; @@ -122,7 +124,7 @@ open_tmpl_archive (struct locarhandle *ah) ah->mmaped = (head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent)); if (ah->mmaped > (unsigned long) st.st_size) - error (EXIT_FAILURE, 0, "locale archite template file truncated"); + error (EXIT_FAILURE, 0, "locale archive template file truncated"); ah->mmaped = st.st_size; ah->reserved = st.st_size; @@ -257,7 +259,9 @@ compute_data (struct locarhandle *ah, struct nameent *name, size_t sumused, static int fill_archive (struct locarhandle *tmpl_ah, - const char *fname, size_t nlist, char *list[], + const char *fname, + size_t install_langs_count, char *install_langs_list[], + size_t nlist, char *list[], const char *primary) { struct locarhandle ah; @@ -288,11 +292,41 @@ fill_archive (struct locarhandle *tmpl_ah, for (cnt = used = 0; cnt < head->namehash_size; ++cnt) if (namehashtab[cnt].locrec_offset != 0) { + char * name; + int i; assert (used < head->namehash_used); - names[used].name = tmpl_ah->addr + namehashtab[cnt].name_offset; - names[used++].locrec - = (struct locrecent *) ((char *) tmpl_ah->addr + - namehashtab[cnt].locrec_offset); + name = tmpl_ah->addr + namehashtab[cnt].name_offset; + if (install_langs_count == 0) + { + /* Always intstall the entry. */ + names[used].name = name; + names[used++].locrec + = (struct locrecent *) ((char *) tmpl_ah->addr + + namehashtab[cnt].locrec_offset); + } + else + { + /* Only install the entry if the user asked for it via + --install-langs. */ + for (i = 0; i < install_langs_count; i++) + { + /* Add one for "_" and one for the null terminator. */ + size_t len = strlen (install_langs_list[i]) + 2; + char *install_lang = (char *)xmalloc (len); + strncpy (install_lang, install_langs_list[i], len - 2); + install_lang[len - 1] = '\0'; + if (strchr (install_lang, '_') == NULL) + strcat (install_lang, "_"); + if (strncmp (name, install_lang, strlen (install_lang)) == 0) + { + names[used].name = name; + names[used++].locrec + = (struct locrecent *) ((char *)tmpl_ah->addr + + namehashtab[cnt].locrec_offset); + } + free (install_lang); + } + } } /* Sort the names. */ @@ -542,6 +576,62 @@ fill_archive (struct locarhandle *tmpl_ah, return result; } +void usage() +{ + printf ("\ +Usage: build-locale-archive [OPTION]... [TEMPLATE-FILE] [ARCHIVE-FILE]\n\ + Builds a locale archive from a template file.\n\ + Options:\n\ + -h, --help Print this usage message.\n\ + -v, --verbose Verbose execution.\n\ + -l, --install-langs=LIST Only include locales given in LIST into the \n\ + locale archive. LIST is a colon separated list\n\ + of locale prefixes, for example \"de:en:ja\".\n\ + The special argument \"all\" means to install\n\ + all languages and it must be present by itself.\n\ + If \"all\" is present with any other language it\n\ + will be treated as the name of a locale.\n\ + If the --install-langs option is missing, all\n\ + locales are installed. The colon separated list\n\ + can contain any strings matching the beginning of\n\ + locale names.\n\ + If a string does not contain a \"_\", it is added.\n\ + Examples:\n\ + --install-langs=\"en\"\n\ + installs en_US, en_US.iso88591,\n\ + en_US.iso885915, en_US.utf8,\n\ + en_GB ...\n\ + --install-langs=\"en_US.utf8\"\n\ + installs only en_US.utf8.\n\ + --install-langs=\"ko\"\n\ + installs ko_KR, ko_KR.euckr,\n\ + ko_KR.utf8 but *not* kok_IN\n\ + because \"ko\" does not contain\n\ + \"_\" and it is silently added\n\ + --install-langs\"ko:kok\"\n\ + installs ko_KR, ko_KR.euckr,\n\ + ko_KR.utf8, kok_IN, and\n\ + kok_IN.utf8.\n\ + --install-langs=\"POSIX\" will\n\ + installs *no* locales at all\n\ + because POSIX matches none of\n\ + the locales. Actually, any string\n\ + matching nothing will do that.\n\ + POSIX and C will always be\n\ + available because they are\n\ + builtin.\n\ + Aliases are installed as well,\n\ + i.e. --install-langs=\"de\"\n\ + will install not only every locale starting with\n\ + \"de\" but also the aliases \"deutsch\"\n\ + and and \"german\" although the latter does not\n\ + start with \"de\".\n\ +\n\ + If the arguments TEMPLATE-FILE and ARCHIVE-FILE are not given the locations\n\ + where the glibc used expects these files are used by default.\n\ +"); +} + int main (int argc, char *argv[]) { char path[4096]; @@ -549,22 +639,133 @@ int main (int argc, char *argv[]) struct dirent64 *d; struct stat64 st; char *list[16384], *primary; + char *lang; + int install_langs_count = 0; + int i; + char *install_langs_arg, *ila_start; + char **install_langs_list; unsigned int cnt = 0; struct locarhandle tmpl_ah; + char *new_locar_fname = NULL; size_t loc_path_len = strlen (loc_path); + while (1) + { + int c; + + static struct option long_options[] = + { + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {"install-langs", required_argument, 0, 'l'}, + {0, 0, 0, 0} + }; + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long (argc, argv, "vhl:", + long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("unknown option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + usage (); + exit (1); + + case 'v': + verbose = 1; + be_quiet = 0; + break; + + case 'h': + usage (); + exit (0); + + case 'l': + install_langs_arg = ila_start = strdup (optarg); + /* If the argument to --install-lang is "all", do + not limit the list of languages to install and install + them all. We do not support installing a single locale + called "all". */ +#define MAGIC_INSTALL_ALL "all" + if (install_langs_arg != NULL + && install_langs_arg[0] != '\0' + && !(strncmp(install_langs_arg, MAGIC_INSTALL_ALL, + strlen(MAGIC_INSTALL_ALL)) == 0 + && strlen (install_langs_arg) == 3)) + { + /* Count the number of languages we will install. */ + while (true) + { + lang = strtok(install_langs_arg, ":;,"); + if (lang == NULL) + break; + install_langs_count++; + install_langs_arg = NULL; + } + free (ila_start); + /* Copy the list. */ + install_langs_list = (char **)xmalloc (sizeof(char *) * install_langs_count); + install_langs_arg = ila_start = strdup (optarg); + install_langs_count = 0; + while (true) + { + lang = strtok(install_langs_arg, ":;,"); + if (lang == NULL) + break; + install_langs_list[install_langs_count] = lang; + install_langs_count++; + install_langs_arg = NULL; + } + } + break; + + case '?': + /* getopt_long already printed an error message. */ + usage (); + exit (0); + + default: + abort (); + } + } + tmpl_ah.fname = NULL; + if (optind < argc) + tmpl_ah.fname = argv[optind]; + if (optind + 1 < argc) + new_locar_fname = argv[optind + 1]; + if (verbose) + { + if (tmpl_ah.fname) + printf("input archive file specified on command line: %s\n", + tmpl_ah.fname); + else + printf("using default input archive file.\n"); + if (new_locar_fname) + printf("output archive file specified on command line: %s\n", + new_locar_fname); + else + printf("using default output archive file.\n"); + } + dirp = opendir (loc_path); if (dirp == NULL) error (EXIT_FAILURE, errno, "cannot open directory \"%s\"", loc_path); - /* Use the template file as specified on the command line. */ - tmpl_ah.fname = NULL; - if (argc > 1) - tmpl_ah.fname = argv[1]; - open_tmpl_archive (&tmpl_ah); - unlink (locar_file); + if (new_locar_fname) + unlink (new_locar_fname); + else + unlink (locar_file); primary = getenv ("LC_ALL"); if (primary == NULL) primary = getenv ("LANG"); @@ -575,7 +776,8 @@ int main (int argc, char *argv[]) && strncmp (primary, "zh", 2) != 0) { char *ptr = malloc (strlen (primary) + strlen (".utf8") + 1), *p, *q; - + /* This leads to invalid locales sometimes: + de_DE.iso885915@euro -> de_DE.utf8@euro */ if (ptr != NULL) { p = ptr; @@ -640,9 +842,16 @@ int main (int argc, char *argv[]) closedir (dirp); /* Store the archive to the file specified as the second argument on the command line or the default locale archive. */ - fill_archive (&tmpl_ah, argc > 2 ? argv[2] : NULL, cnt, list, primary); + fill_archive (&tmpl_ah, new_locar_fname, + install_langs_count, install_langs_list, + cnt, list, primary); close_archive (&tmpl_ah); truncate (tmpl_file, 0); + if (install_langs_count > 0) + { + free (ila_start); + free (install_langs_list); + } char *tz_argv[] = { "/usr/sbin/tzdata-update", NULL }; execve (tz_argv[0], (char *const *)tz_argv, (char *const *)&tz_argv[1]); exit (0); diff --git a/glibc.spec b/glibc.spec index 9a8d664..7095268 100644 --- a/glibc.spec +++ b/glibc.spec @@ -1,6 +1,6 @@ %define glibcsrcdir glibc-2.21-104-gbdf1ff0 %define glibcversion 2.21.90 -%define glibcrelease 4%{?dist} +%define glibcrelease 5%{?dist} # Pre-release tarballs are pulled in from git using a command that is # effectively: # @@ -1579,22 +1579,22 @@ end %postun -p /sbin/ldconfig -%triggerin common -p -- glibc +%triggerin common -e -p -- glibc if posix.stat("%{_prefix}/lib/locale/locale-archive.tmpl", "size") > 0 then pid = posix.fork() if pid == 0 then - posix.exec("%{_prefix}/sbin/build-locale-archive") + posix.exec("%{_prefix}/sbin/build-locale-archive", "--install-langs", "%%{_install_langs}") elseif pid > 0 then posix.wait(pid) end end -%post common -p +%post common -e -p if posix.access("/etc/ld.so.cache") then if posix.stat("%{_prefix}/lib/locale/locale-archive.tmpl", "size") > 0 then pid = posix.fork() if pid == 0 then - posix.exec("%{_prefix}/sbin/build-locale-archive") + posix.exec("%{_prefix}/sbin/build-locale-archive", "--install-langs", "%%{_install_langs}") elseif pid > 0 then posix.wait(pid) end @@ -1751,6 +1751,10 @@ rm -f *.filelist* %endif %changelog +* Tue Mar 3 2015 Mike Fabian - 2.21.90-5 +- Support installing only those locales specified by the RPM macro + %%_install_langs (#156477). + * Mon Feb 23 2015 Siddhesh Poyarekar - 2.21.90-4 - Auto-sync with upstream master.