|
|
e354a5 |
commit 92954ffa5a5662fbfde14febd7e5dcc358c85470
|
|
|
e354a5 |
Author: Carlos O'Donell <carlos@redhat.com>
|
|
|
e354a5 |
Date: Wed Jan 8 13:24:42 2020 -0500
|
|
|
e354a5 |
|
|
|
e354a5 |
localedef: Add verbose messages for failure paths.
|
|
|
e354a5 |
|
|
|
e354a5 |
During testing of localedef running in a minimal container
|
|
|
e354a5 |
there were several error cases which were hard to diagnose
|
|
|
e354a5 |
since they appeared as strerror (errno) values printed by the
|
|
|
e354a5 |
higher level functions. This change adds three new verbose
|
|
|
e354a5 |
messages for potential failure paths. The new messages give
|
|
|
e354a5 |
the user the opportunity to use -v and display additional
|
|
|
e354a5 |
information about why localedef might be failing. I found
|
|
|
e354a5 |
these messages useful myself while writing a localedef
|
|
|
e354a5 |
container test for --no-hard-links.
|
|
|
e354a5 |
|
|
|
e354a5 |
Since the changes cleanup the code that handle codeset
|
|
|
e354a5 |
normalization we add tst-localedef-path-norm which contains
|
|
|
e354a5 |
many sub-tests to verify the correct expected normalization of
|
|
|
e354a5 |
codeset strings both when installing to default paths (the
|
|
|
e354a5 |
only time normalization is enabled) and installing to absolute
|
|
|
e354a5 |
paths. During the refactoring I created at least one
|
|
|
e354a5 |
buffer-overflow which valgrind caught, but these tests did not
|
|
|
e354a5 |
catch because the exec in the container had a very clean heap
|
|
|
e354a5 |
with zero-initialized memory. However, between valgrind and
|
|
|
e354a5 |
the tests the results are clean.
|
|
|
e354a5 |
|
|
|
e354a5 |
The new tst-localedef-path-norm passes without regression on
|
|
|
e354a5 |
x86_64.
|
|
|
e354a5 |
|
|
|
e354a5 |
Change-Id: I28b9f680711ff00252a2cb15625b774cc58ecb9d
|
|
|
e354a5 |
|
|
|
e354a5 |
diff --git a/include/programs/xasprintf.h b/include/programs/xasprintf.h
|
|
|
e354a5 |
new file mode 100644
|
|
|
e354a5 |
index 0000000000000000..53193ba3837f7418
|
|
|
e354a5 |
--- /dev/null
|
|
|
e354a5 |
+++ b/include/programs/xasprintf.h
|
|
|
e354a5 |
@@ -0,0 +1,24 @@
|
|
|
e354a5 |
+/* asprintf with out of memory checking
|
|
|
e354a5 |
+ Copyright (C) 2019 Free Software Foundation, Inc.
|
|
|
e354a5 |
+ This file is part of the GNU C Library.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ This program is free software; you can redistribute it and/or modify
|
|
|
e354a5 |
+ it under the terms of the GNU General Public License as published
|
|
|
e354a5 |
+ by the Free Software Foundation; version 2 of the License, or
|
|
|
e354a5 |
+ (at your option) any later version.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ This program is distributed in the hope that it will be useful,
|
|
|
e354a5 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
e354a5 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
e354a5 |
+ GNU General Public License for more details.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ You should have received a copy of the GNU General Public License
|
|
|
e354a5 |
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+#ifndef _XASPRINTF_H
|
|
|
e354a5 |
+#define _XASPRINTF_H 1
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+extern char *xasprintf (const char *format, ...)
|
|
|
e354a5 |
+ __attribute__ ((__format__ (__printf__, 1, 2), __warn_unused_result__));
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+#endif /* xasprintf.h */
|
|
|
e354a5 |
diff --git a/locale/Makefile b/locale/Makefile
|
|
|
e354a5 |
index 23a71321b6646c49..4278350cdc7be28d 100644
|
|
|
e354a5 |
--- a/locale/Makefile
|
|
|
e354a5 |
+++ b/locale/Makefile
|
|
|
e354a5 |
@@ -28,6 +28,7 @@ routines = setlocale findlocale loadlocale loadarchive \
|
|
|
e354a5 |
localeconv nl_langinfo nl_langinfo_l mb_cur_max \
|
|
|
e354a5 |
newlocale duplocale freelocale uselocale
|
|
|
e354a5 |
tests = tst-C-locale tst-locname tst-duplocale
|
|
|
e354a5 |
+tests-container = tst-localedef-path-norm
|
|
|
e354a5 |
categories = ctype messages monetary numeric time paper name \
|
|
|
e354a5 |
address telephone measurement identification collate
|
|
|
e354a5 |
aux = $(categories:%=lc-%) $(categories:%=C-%) SYS_libc C_name \
|
|
|
e354a5 |
@@ -54,7 +55,7 @@ localedef-modules := localedef $(categories:%=ld-%) \
|
|
|
e354a5 |
localedef-aux := md5
|
|
|
e354a5 |
locale-modules := locale locale-spec
|
|
|
e354a5 |
lib-modules := charmap-dir simple-hash xmalloc xstrdup \
|
|
|
e354a5 |
- record-status
|
|
|
e354a5 |
+ record-status xasprintf
|
|
|
e354a5 |
|
|
|
e354a5 |
|
|
|
e354a5 |
GPERF = gperf
|
|
|
e354a5 |
diff --git a/locale/programs/localedef.c b/locale/programs/localedef.c
|
|
|
e354a5 |
index d718d2e9f47382bc..9a57d2cb435b25ed 100644
|
|
|
e354a5 |
--- a/locale/programs/localedef.c
|
|
|
e354a5 |
+++ b/locale/programs/localedef.c
|
|
|
e354a5 |
@@ -174,14 +174,14 @@ static struct argp argp =
|
|
|
e354a5 |
|
|
|
e354a5 |
/* Prototypes for local functions. */
|
|
|
e354a5 |
static void error_print (void);
|
|
|
e354a5 |
-static const char *construct_output_path (char *path);
|
|
|
e354a5 |
-static const char *normalize_codeset (const char *codeset, size_t name_len);
|
|
|
e354a5 |
+static char *construct_output_path (char *path);
|
|
|
e354a5 |
+static char *normalize_codeset (const char *codeset, size_t name_len);
|
|
|
e354a5 |
|
|
|
e354a5 |
|
|
|
e354a5 |
int
|
|
|
e354a5 |
main (int argc, char *argv[])
|
|
|
e354a5 |
{
|
|
|
e354a5 |
- const char *output_path;
|
|
|
e354a5 |
+ char *output_path;
|
|
|
e354a5 |
int cannot_write_why;
|
|
|
e354a5 |
struct charmap_t *charmap;
|
|
|
e354a5 |
struct localedef_t global;
|
|
|
e354a5 |
@@ -226,7 +226,8 @@ main (int argc, char *argv[])
|
|
|
e354a5 |
}
|
|
|
e354a5 |
|
|
|
e354a5 |
/* The parameter describes the output path of the constructed files.
|
|
|
e354a5 |
- If the described files cannot be written return a NULL pointer. */
|
|
|
e354a5 |
+ If the described files cannot be written return a NULL pointer.
|
|
|
e354a5 |
+ We don't free output_path because we will exit. */
|
|
|
e354a5 |
output_path = construct_output_path (argv[remaining]);
|
|
|
e354a5 |
if (output_path == NULL && ! no_archive)
|
|
|
e354a5 |
error (4, errno, _("cannot create directory for output files"));
|
|
|
e354a5 |
@@ -424,20 +425,16 @@ more_help (int key, const char *text, void *input)
|
|
|
e354a5 |
{
|
|
|
e354a5 |
case ARGP_KEY_HELP_EXTRA:
|
|
|
e354a5 |
/* We print some extra information. */
|
|
|
e354a5 |
- if (asprintf (&tp, gettext ("\
|
|
|
e354a5 |
+ tp = xasprintf (gettext ("\
|
|
|
e354a5 |
For bug reporting instructions, please see:\n\
|
|
|
e354a5 |
-%s.\n"), REPORT_BUGS_TO) < 0)
|
|
|
e354a5 |
- return NULL;
|
|
|
e354a5 |
- if (asprintf (&cp, gettext ("\
|
|
|
e354a5 |
+%s.\n"), REPORT_BUGS_TO);
|
|
|
e354a5 |
+ cp = xasprintf (gettext ("\
|
|
|
e354a5 |
System's directory for character maps : %s\n\
|
|
|
e354a5 |
repertoire maps: %s\n\
|
|
|
e354a5 |
locale path : %s\n\
|
|
|
e354a5 |
%s"),
|
|
|
e354a5 |
- CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, tp) < 0)
|
|
|
e354a5 |
- {
|
|
|
e354a5 |
- free (tp);
|
|
|
e354a5 |
- return NULL;
|
|
|
e354a5 |
- }
|
|
|
e354a5 |
+ CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, tp);
|
|
|
e354a5 |
+ free (tp);
|
|
|
e354a5 |
return cp;
|
|
|
e354a5 |
default:
|
|
|
e354a5 |
break;
|
|
|
e354a5 |
@@ -467,15 +464,13 @@ error_print (void)
|
|
|
e354a5 |
}
|
|
|
e354a5 |
|
|
|
e354a5 |
|
|
|
e354a5 |
-/* The parameter to localedef describes the output path. If it does
|
|
|
e354a5 |
- contain a '/' character it is a relative path. Otherwise it names the
|
|
|
e354a5 |
- locale this definition is for. */
|
|
|
e354a5 |
-static const char *
|
|
|
e354a5 |
+/* The parameter to localedef describes the output path. If it does contain a
|
|
|
e354a5 |
+ '/' character it is a relative path. Otherwise it names the locale this
|
|
|
e354a5 |
+ definition is for. The returned path must be freed by the caller. */
|
|
|
e354a5 |
+static char *
|
|
|
e354a5 |
construct_output_path (char *path)
|
|
|
e354a5 |
{
|
|
|
e354a5 |
- const char *normal = NULL;
|
|
|
e354a5 |
char *result;
|
|
|
e354a5 |
- char *endp;
|
|
|
e354a5 |
|
|
|
e354a5 |
if (strchr (path, '/') == NULL)
|
|
|
e354a5 |
{
|
|
|
e354a5 |
@@ -483,50 +478,44 @@ construct_output_path (char *path)
|
|
|
e354a5 |
contains a reference to the codeset. This should be
|
|
|
e354a5 |
normalized. */
|
|
|
e354a5 |
char *startp;
|
|
|
e354a5 |
+ char *endp = NULL;
|
|
|
e354a5 |
+ char *normal = NULL;
|
|
|
e354a5 |
|
|
|
e354a5 |
startp = path;
|
|
|
e354a5 |
- /* We must be prepared for finding a CEN name or a location of
|
|
|
e354a5 |
- the introducing `.' where it is not possible anymore. */
|
|
|
e354a5 |
+ /* Either we have a '@' which starts a CEN name or '.' which starts the
|
|
|
e354a5 |
+ codeset specification. The CEN name starts with '@' and may also have
|
|
|
e354a5 |
+ a codeset specification, but we do not normalize the string after '@'.
|
|
|
e354a5 |
+ If we only find the codeset specification then we normalize only the codeset
|
|
|
e354a5 |
+ specification (but not anything after a subsequent '@'). */
|
|
|
e354a5 |
while (*startp != '\0' && *startp != '@' && *startp != '.')
|
|
|
e354a5 |
++startp;
|
|
|
e354a5 |
if (*startp == '.')
|
|
|
e354a5 |
{
|
|
|
e354a5 |
/* We found a codeset specification. Now find the end. */
|
|
|
e354a5 |
endp = ++startp;
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Stop at the first '@', and don't normalize anything past that. */
|
|
|
e354a5 |
while (*endp != '\0' && *endp != '@')
|
|
|
e354a5 |
++endp;
|
|
|
e354a5 |
|
|
|
e354a5 |
if (endp > startp)
|
|
|
e354a5 |
normal = normalize_codeset (startp, endp - startp);
|
|
|
e354a5 |
}
|
|
|
e354a5 |
- else
|
|
|
e354a5 |
- /* This is to keep gcc quiet. */
|
|
|
e354a5 |
- endp = NULL;
|
|
|
e354a5 |
|
|
|
e354a5 |
- /* We put an additional '\0' at the end of the string because at
|
|
|
e354a5 |
- the end of the function we need another byte for the trailing
|
|
|
e354a5 |
- '/'. */
|
|
|
e354a5 |
- ssize_t n;
|
|
|
e354a5 |
if (normal == NULL)
|
|
|
e354a5 |
- n = asprintf (&result, "%s%s/%s%c", output_prefix ?: "",
|
|
|
e354a5 |
- COMPLOCALEDIR, path, '\0');
|
|
|
e354a5 |
+ result = xasprintf ("%s%s/%s/", output_prefix ?: "",
|
|
|
e354a5 |
+ COMPLOCALEDIR, path);
|
|
|
e354a5 |
else
|
|
|
e354a5 |
- n = asprintf (&result, "%s%s/%.*s%s%s%c",
|
|
|
e354a5 |
- output_prefix ?: "", COMPLOCALEDIR,
|
|
|
e354a5 |
- (int) (startp - path), path, normal, endp, '\0');
|
|
|
e354a5 |
-
|
|
|
e354a5 |
- if (n < 0)
|
|
|
e354a5 |
- return NULL;
|
|
|
e354a5 |
-
|
|
|
e354a5 |
- endp = result + n - 1;
|
|
|
e354a5 |
+ result = xasprintf ("%s%s/%.*s%s%s/",
|
|
|
e354a5 |
+ output_prefix ?: "", COMPLOCALEDIR,
|
|
|
e354a5 |
+ (int) (startp - path), path, normal, endp ?: "");
|
|
|
e354a5 |
+ /* Free the allocated normalized codeset name. */
|
|
|
e354a5 |
+ free (normal);
|
|
|
e354a5 |
}
|
|
|
e354a5 |
else
|
|
|
e354a5 |
{
|
|
|
e354a5 |
- /* This is a user path. Please note the additional byte in the
|
|
|
e354a5 |
- memory allocation. */
|
|
|
e354a5 |
- size_t len = strlen (path) + 1;
|
|
|
e354a5 |
- result = xmalloc (len + 1);
|
|
|
e354a5 |
- endp = mempcpy (result, path, len) - 1;
|
|
|
e354a5 |
+ /* This is a user path. */
|
|
|
e354a5 |
+ result = xasprintf ("%s/", path);
|
|
|
e354a5 |
|
|
|
e354a5 |
/* If the user specified an output path we cannot add the output
|
|
|
e354a5 |
to the archive. */
|
|
|
e354a5 |
@@ -536,25 +525,41 @@ construct_output_path (char *path)
|
|
|
e354a5 |
errno = 0;
|
|
|
e354a5 |
|
|
|
e354a5 |
if (no_archive && euidaccess (result, W_OK) == -1)
|
|
|
e354a5 |
- /* Perhaps the directory does not exist now. Try to create it. */
|
|
|
e354a5 |
- if (errno == ENOENT)
|
|
|
e354a5 |
- {
|
|
|
e354a5 |
- errno = 0;
|
|
|
e354a5 |
- if (mkdir (result, 0777) < 0)
|
|
|
e354a5 |
- return NULL;
|
|
|
e354a5 |
- }
|
|
|
e354a5 |
-
|
|
|
e354a5 |
- *endp++ = '/';
|
|
|
e354a5 |
- *endp = '\0';
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ /* Perhaps the directory does not exist now. Try to create it. */
|
|
|
e354a5 |
+ if (errno == ENOENT)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ errno = 0;
|
|
|
e354a5 |
+ if (mkdir (result, 0777) < 0)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ record_verbose (stderr,
|
|
|
e354a5 |
+ _("cannot create output path \'%s\': %s"),
|
|
|
e354a5 |
+ result, strerror (errno));
|
|
|
e354a5 |
+ free (result);
|
|
|
e354a5 |
+ return NULL;
|
|
|
e354a5 |
+ }
|
|
|
e354a5 |
+ }
|
|
|
e354a5 |
+ else
|
|
|
e354a5 |
+ record_verbose (stderr,
|
|
|
e354a5 |
+ _("no write permission to output path \'%s\': %s"),
|
|
|
e354a5 |
+ result, strerror (errno));
|
|
|
e354a5 |
+ }
|
|
|
e354a5 |
|
|
|
e354a5 |
return result;
|
|
|
e354a5 |
}
|
|
|
e354a5 |
|
|
|
e354a5 |
|
|
|
e354a5 |
-/* Normalize codeset name. There is no standard for the codeset
|
|
|
e354a5 |
- names. Normalization allows the user to use any of the common
|
|
|
e354a5 |
- names. */
|
|
|
e354a5 |
-static const char *
|
|
|
e354a5 |
+/* Normalize codeset name. There is no standard for the codeset names.
|
|
|
e354a5 |
+ Normalization allows the user to use any of the common names e.g. UTF-8,
|
|
|
e354a5 |
+ utf-8, utf8, UTF8 etc.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ We normalize using the following rules:
|
|
|
e354a5 |
+ - Remove all non-alpha-numeric characters
|
|
|
e354a5 |
+ - Lowercase all characters.
|
|
|
e354a5 |
+ - If there are only digits assume it's an ISO standard and prefix with 'iso'
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ We return the normalized string which needs to be freed by free. */
|
|
|
e354a5 |
+static char *
|
|
|
e354a5 |
normalize_codeset (const char *codeset, size_t name_len)
|
|
|
e354a5 |
{
|
|
|
e354a5 |
int len = 0;
|
|
|
e354a5 |
@@ -563,6 +568,7 @@ normalize_codeset (const char *codeset, size_t name_len)
|
|
|
e354a5 |
char *wp;
|
|
|
e354a5 |
size_t cnt;
|
|
|
e354a5 |
|
|
|
e354a5 |
+ /* Compute the length of only the alpha-numeric characters. */
|
|
|
e354a5 |
for (cnt = 0; cnt < name_len; ++cnt)
|
|
|
e354a5 |
if (isalnum (codeset[cnt]))
|
|
|
e354a5 |
{
|
|
|
e354a5 |
@@ -572,25 +578,24 @@ normalize_codeset (const char *codeset, size_t name_len)
|
|
|
e354a5 |
only_digit = 0;
|
|
|
e354a5 |
}
|
|
|
e354a5 |
|
|
|
e354a5 |
- retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
|
|
|
e354a5 |
+ /* If there were only digits we assume it's an ISO standard and we will
|
|
|
e354a5 |
+ prefix with 'iso' so include space for that. We fill in the required
|
|
|
e354a5 |
+ space from codeset up to the converted length. */
|
|
|
e354a5 |
+ wp = retval = xasprintf ("%s%.*s", only_digit ? "iso" : "", len, codeset);
|
|
|
e354a5 |
|
|
|
e354a5 |
- if (retval != NULL)
|
|
|
e354a5 |
- {
|
|
|
e354a5 |
- if (only_digit)
|
|
|
e354a5 |
- wp = stpcpy (retval, "iso");
|
|
|
e354a5 |
- else
|
|
|
e354a5 |
- wp = retval;
|
|
|
e354a5 |
-
|
|
|
e354a5 |
- for (cnt = 0; cnt < name_len; ++cnt)
|
|
|
e354a5 |
- if (isalpha (codeset[cnt]))
|
|
|
e354a5 |
- *wp++ = tolower (codeset[cnt]);
|
|
|
e354a5 |
- else if (isdigit (codeset[cnt]))
|
|
|
e354a5 |
- *wp++ = codeset[cnt];
|
|
|
e354a5 |
+ /* Skip "iso". */
|
|
|
e354a5 |
+ if (only_digit)
|
|
|
e354a5 |
+ wp += 3;
|
|
|
e354a5 |
|
|
|
e354a5 |
- *wp = '\0';
|
|
|
e354a5 |
- }
|
|
|
e354a5 |
+ /* Lowercase all characters. */
|
|
|
e354a5 |
+ for (cnt = 0; cnt < name_len; ++cnt)
|
|
|
e354a5 |
+ if (isalpha (codeset[cnt]))
|
|
|
e354a5 |
+ *wp++ = tolower (codeset[cnt]);
|
|
|
e354a5 |
+ else if (isdigit (codeset[cnt]))
|
|
|
e354a5 |
+ *wp++ = codeset[cnt];
|
|
|
e354a5 |
|
|
|
e354a5 |
- return (const char *) retval;
|
|
|
e354a5 |
+ /* Return allocated and converted name for caller to free. */
|
|
|
e354a5 |
+ return retval;
|
|
|
e354a5 |
}
|
|
|
e354a5 |
|
|
|
e354a5 |
|
|
|
e354a5 |
diff --git a/locale/programs/localedef.h b/locale/programs/localedef.h
|
|
|
e354a5 |
index 0083faceabbf3dd9..c528dbb97854dbd1 100644
|
|
|
e354a5 |
--- a/locale/programs/localedef.h
|
|
|
e354a5 |
+++ b/locale/programs/localedef.h
|
|
|
e354a5 |
@@ -122,6 +122,7 @@ extern const char *alias_file;
|
|
|
e354a5 |
|
|
|
e354a5 |
/* Prototypes for a few program-wide used functions. */
|
|
|
e354a5 |
#include <programs/xmalloc.h>
|
|
|
e354a5 |
+#include <programs/xasprintf.h>
|
|
|
e354a5 |
|
|
|
e354a5 |
|
|
|
e354a5 |
/* Mark given locale as to be read. */
|
|
|
e354a5 |
diff --git a/locale/programs/xasprintf.c b/locale/programs/xasprintf.c
|
|
|
e354a5 |
new file mode 100644
|
|
|
e354a5 |
index 0000000000000000..efc91a9c34074736
|
|
|
e354a5 |
--- /dev/null
|
|
|
e354a5 |
+++ b/locale/programs/xasprintf.c
|
|
|
e354a5 |
@@ -0,0 +1,34 @@
|
|
|
e354a5 |
+/* asprintf with out of memory checking
|
|
|
e354a5 |
+ Copyright (C) 2019 Free Software Foundation, Inc.
|
|
|
e354a5 |
+ This file is part of the GNU C Library.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ This program is free software; you can redistribute it and/or modify
|
|
|
e354a5 |
+ it under the terms of the GNU General Public License as published
|
|
|
e354a5 |
+ by the Free Software Foundation; version 2 of the License, or
|
|
|
e354a5 |
+ (at your option) any later version.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ This program is distributed in the hope that it will be useful,
|
|
|
e354a5 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
e354a5 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
e354a5 |
+ GNU General Public License for more details.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ You should have received a copy of the GNU General Public License
|
|
|
e354a5 |
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+#include <stdlib.h>
|
|
|
e354a5 |
+#include <stdio.h>
|
|
|
e354a5 |
+#include <stdarg.h>
|
|
|
e354a5 |
+#include <libintl.h>
|
|
|
e354a5 |
+#include <error.h>
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+char *
|
|
|
e354a5 |
+xasprintf (const char *format, ...)
|
|
|
e354a5 |
+{
|
|
|
e354a5 |
+ va_list ap;
|
|
|
e354a5 |
+ va_start (ap, format);
|
|
|
e354a5 |
+ char *result;
|
|
|
e354a5 |
+ if (vasprintf (&result, format, ap) < 0)
|
|
|
e354a5 |
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
|
|
|
e354a5 |
+ va_end (ap);
|
|
|
e354a5 |
+ return result;
|
|
|
e354a5 |
+}
|
|
|
e354a5 |
diff --git a/locale/tst-localedef-path-norm.c b/locale/tst-localedef-path-norm.c
|
|
|
e354a5 |
new file mode 100644
|
|
|
e354a5 |
index 0000000000000000..2ef1d26f07084c68
|
|
|
e354a5 |
--- /dev/null
|
|
|
e354a5 |
+++ b/locale/tst-localedef-path-norm.c
|
|
|
e354a5 |
@@ -0,0 +1,242 @@
|
|
|
e354a5 |
+/* Test for localedef path name handling and normalization.
|
|
|
e354a5 |
+ Copyright (C) 2019 Free Software Foundation, Inc.
|
|
|
e354a5 |
+ This file is part of the GNU C Library.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ The GNU C Library is free software; you can redistribute it and/or
|
|
|
e354a5 |
+ modify it under the terms of the GNU Lesser General Public
|
|
|
e354a5 |
+ License as published by the Free Software Foundation; either
|
|
|
e354a5 |
+ version 2.1 of the License, or (at your option) any later version.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
|
e354a5 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
e354a5 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
e354a5 |
+ Lesser General Public License for more details.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ You should have received a copy of the GNU Lesser General Public
|
|
|
e354a5 |
+ License along with the GNU C Library; if not, see
|
|
|
e354a5 |
+ <https://www.gnu.org/licenses/>. */
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+/* The test runs localedef with various named paths to test for expected
|
|
|
e354a5 |
+ behaviours dealing with codeset name normalization. That is to say that use
|
|
|
e354a5 |
+ of UTF-8, and it's variations, are normalized to utf8. Likewise that values
|
|
|
e354a5 |
+ after the @ are not normalized and left as-is. The test needs to run
|
|
|
e354a5 |
+ localedef with known input values and then check that the generated path
|
|
|
e354a5 |
+ matches the expected value after normalization. */
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+/* Note: In some cases adding -v (verbose) to localedef changes the exit
|
|
|
e354a5 |
+ status to a non-zero value because some warnings are only enabled in verbose
|
|
|
e354a5 |
+ mode. This should probably be changed so warnings are either present or not
|
|
|
e354a5 |
+ present, regardless of verbosity. POSIX requires that any warnings cause the
|
|
|
e354a5 |
+ exit status to be non-zero. */
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+#include <sys/types.h>
|
|
|
e354a5 |
+#include <sys/stat.h>
|
|
|
e354a5 |
+#include <unistd.h>
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+#include <support/capture_subprocess.h>
|
|
|
e354a5 |
+#include <support/check.h>
|
|
|
e354a5 |
+#include <support/support.h>
|
|
|
e354a5 |
+#include <support/xunistd.h>
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+/* Full path to localedef. */
|
|
|
e354a5 |
+char *prog;
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+/* Execute localedef in a subprocess. */
|
|
|
e354a5 |
+static void
|
|
|
e354a5 |
+execv_wrapper (void *args)
|
|
|
e354a5 |
+{
|
|
|
e354a5 |
+ char **argv = args;
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ execv (prog, argv);
|
|
|
e354a5 |
+ FAIL_EXIT1 ("execv: %m");
|
|
|
e354a5 |
+}
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+struct test_closure
|
|
|
e354a5 |
+{
|
|
|
e354a5 |
+ /* Arguments for running localedef. */
|
|
|
e354a5 |
+ const char *const argv[16];
|
|
|
e354a5 |
+ /* Expected directory name for compiled locale. */
|
|
|
e354a5 |
+ const char *exp;
|
|
|
e354a5 |
+ /* Expected path to compiled locale. */
|
|
|
e354a5 |
+ const char *complocaledir;
|
|
|
e354a5 |
+};
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+/* Run localedef with DATA.ARGV arguments (NULL terminated), and expect path to
|
|
|
e354a5 |
+ the compiled locale is "DATA.COMPLOCALEDIR/DATA.EXP". */
|
|
|
e354a5 |
+static void
|
|
|
e354a5 |
+run_test (struct test_closure data)
|
|
|
e354a5 |
+{
|
|
|
e354a5 |
+ const char * const *args = data.argv;
|
|
|
e354a5 |
+ const char *exp = data.exp;
|
|
|
e354a5 |
+ const char *complocaledir = data.complocaledir;
|
|
|
e354a5 |
+ struct stat64 fs;
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Expected output path. */
|
|
|
e354a5 |
+ const char *path = xasprintf ("%s/%s", complocaledir, exp);
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Run test. */
|
|
|
e354a5 |
+ struct support_capture_subprocess result;
|
|
|
e354a5 |
+ result = support_capture_subprocess (execv_wrapper, (void *)args);
|
|
|
e354a5 |
+ support_capture_subprocess_check (&result, "execv", 0, sc_allow_none);
|
|
|
e354a5 |
+ support_capture_subprocess_free (&result);
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Verify path is present and is a directory. */
|
|
|
e354a5 |
+ xstat (path, &fs);
|
|
|
e354a5 |
+ TEST_VERIFY_EXIT (S_ISDIR (fs.st_mode));
|
|
|
e354a5 |
+ printf ("info: Directory '%s' exists.\n", path);
|
|
|
e354a5 |
+}
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+static int
|
|
|
e354a5 |
+do_test (void)
|
|
|
e354a5 |
+{
|
|
|
e354a5 |
+ /* We are running as root inside the container. */
|
|
|
e354a5 |
+ prog = xasprintf ("%s/localedef", support_bindir_prefix);
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Create the needed directories:
|
|
|
e354a5 |
+ - We need the default compiled locale dir for default output.
|
|
|
e354a5 |
+ - We need an arbitrary absolute path for localedef output.
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ Note: Writing to a non-default absolute path disables any kind
|
|
|
e354a5 |
+ of path normalization since we expect the user wants the path
|
|
|
e354a5 |
+ exactly as they specified it. */
|
|
|
e354a5 |
+ xmkdirp (support_complocaledir_prefix, 0777);
|
|
|
e354a5 |
+ xmkdirp ("/output", 0777);
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* It takes ~10 seconds to serially execute 9 localedef test. We
|
|
|
e354a5 |
+ could run the compilations in parallel if we want to reduce test
|
|
|
e354a5 |
+ time. We don't want to split this out into distinct tests because
|
|
|
e354a5 |
+ it would require multiple chroots. Batching the same localedef
|
|
|
e354a5 |
+ tests saves disk space during testing. */
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 1: Expected normalization.
|
|
|
e354a5 |
+ Run localedef and expect output in /usr/lib/locale/en_US1.utf8,
|
|
|
e354a5 |
+ with normalization changing UTF-8 to utf8. */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "en_US1.UTF-8", NULL },
|
|
|
e354a5 |
+ .exp = "en_US1.utf8",
|
|
|
e354a5 |
+ .complocaledir = support_complocaledir_prefix
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 2: No normalization past '@'.
|
|
|
e354a5 |
+ Run localedef and expect output in /usr/lib/locale/en_US2.utf8@tEsT,
|
|
|
e354a5 |
+ with normalization changing UTF-8@tEsT to utf8@tEsT (everything after
|
|
|
e354a5 |
+ @ is untouched). */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "en_US2.UTF-8@tEsT", NULL },
|
|
|
e354a5 |
+ .exp = "en_US2.utf8@tEsT",
|
|
|
e354a5 |
+ .complocaledir = support_complocaledir_prefix
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 3: No normalization past '@' despite period.
|
|
|
e354a5 |
+ Run localedef and expect output in /usr/lib/locale/en_US3@tEsT.UTF-8,
|
|
|
e354a5 |
+ with normalization changing nothing (everything after @ is untouched)
|
|
|
e354a5 |
+ despite there being a period near the end. */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "en_US3@tEsT.UTF-8", NULL },
|
|
|
e354a5 |
+ .exp = "en_US3@tEsT.UTF-8",
|
|
|
e354a5 |
+ .complocaledir = support_complocaledir_prefix
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 4: Normalize numeric codeset by adding 'iso' prefix.
|
|
|
e354a5 |
+ Run localedef and expect output in /usr/lib/locale/en_US4.88591,
|
|
|
e354a5 |
+ with normalization changing 88591 to iso88591. */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "en_US4.88591", NULL },
|
|
|
e354a5 |
+ .exp = "en_US4.iso88591",
|
|
|
e354a5 |
+ .complocaledir = support_complocaledir_prefix
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 5: Don't add 'iso' prefix if first char is alpha.
|
|
|
e354a5 |
+ Run localedef and expect output in /usr/lib/locale/en_US5.a88591,
|
|
|
e354a5 |
+ with normalization changing nothing. */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "en_US5.a88591", NULL },
|
|
|
e354a5 |
+ .exp = "en_US5.a88591",
|
|
|
e354a5 |
+ .complocaledir = support_complocaledir_prefix
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 6: Don't add 'iso' prefix if last char is alpha.
|
|
|
e354a5 |
+ Run localedef and expect output in /usr/lib/locale/en_US6.88591a,
|
|
|
e354a5 |
+ with normalization changing nothing. */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "en_US6.88591a", NULL },
|
|
|
e354a5 |
+ .exp = "en_US6.88591a",
|
|
|
e354a5 |
+ .complocaledir = support_complocaledir_prefix
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 7: Don't normalize anything with an absolute path.
|
|
|
e354a5 |
+ Run localedef and expect output in /output/en_US7.UTF-8,
|
|
|
e354a5 |
+ with normalization changing nothing. */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "/output/en_US7.UTF-8", NULL },
|
|
|
e354a5 |
+ .exp = "en_US7.UTF-8",
|
|
|
e354a5 |
+ .complocaledir = "/output"
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 8: Don't normalize anything with an absolute path.
|
|
|
e354a5 |
+ Run localedef and expect output in /output/en_US8.UTF-8@tEsT,
|
|
|
e354a5 |
+ with normalization changing nothing. */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "/output/en_US8.UTF-8@tEsT", NULL },
|
|
|
e354a5 |
+ .exp = "en_US8.UTF-8@tEsT",
|
|
|
e354a5 |
+ .complocaledir = "/output"
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ /* Test 9: Don't normalize anything with an absolute path.
|
|
|
e354a5 |
+ Run localedef and expect output in /output/en_US9@tEsT.UTF-8,
|
|
|
e354a5 |
+ with normalization changing nothing. */
|
|
|
e354a5 |
+ run_test ((struct test_closure)
|
|
|
e354a5 |
+ {
|
|
|
e354a5 |
+ .argv = { prog,
|
|
|
e354a5 |
+ "--no-archive",
|
|
|
e354a5 |
+ "-i", "en_US",
|
|
|
e354a5 |
+ "-f", "UTF-8",
|
|
|
e354a5 |
+ "/output/en_US9@tEsT.UTF-8", NULL },
|
|
|
e354a5 |
+ .exp = "en_US9@tEsT.UTF-8",
|
|
|
e354a5 |
+ .complocaledir = "/output"
|
|
|
e354a5 |
+ });
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+ return 0;
|
|
|
e354a5 |
+}
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+#include <support/test-driver.c>
|
|
|
e354a5 |
diff --git a/locale/tst-localedef-path-norm.root/postclean.req b/locale/tst-localedef-path-norm.root/postclean.req
|
|
|
e354a5 |
new file mode 100644
|
|
|
e354a5 |
index 0000000000000000..e69de29bb2d1d643
|
|
|
e354a5 |
diff --git a/locale/tst-localedef-path-norm.root/tst-localedef-path-norm.script b/locale/tst-localedef-path-norm.root/tst-localedef-path-norm.script
|
|
|
e354a5 |
new file mode 100644
|
|
|
e354a5 |
index 0000000000000000..b0f016256a47f762
|
|
|
e354a5 |
--- /dev/null
|
|
|
e354a5 |
+++ b/locale/tst-localedef-path-norm.root/tst-localedef-path-norm.script
|
|
|
e354a5 |
@@ -0,0 +1,2 @@
|
|
|
e354a5 |
+# Must run localedef as root to write into default paths.
|
|
|
e354a5 |
+su
|
|
|
e354a5 |
diff --git a/support/Makefile b/support/Makefile
|
|
|
e354a5 |
index 117cfdd4f22fc405..5808a42dce87151f 100644
|
|
|
e354a5 |
--- a/support/Makefile
|
|
|
e354a5 |
+++ b/support/Makefile
|
|
|
e354a5 |
@@ -182,7 +182,8 @@ CFLAGS-support_paths.c = \
|
|
|
e354a5 |
-DLIBDIR_PATH=\"$(libdir)\" \
|
|
|
e354a5 |
-DBINDIR_PATH=\"$(bindir)\" \
|
|
|
e354a5 |
-DSBINDIR_PATH=\"$(sbindir)\" \
|
|
|
e354a5 |
- -DROOTSBINDIR_PATH=\"$(rootsbindir)\"
|
|
|
e354a5 |
+ -DROOTSBINDIR_PATH=\"$(rootsbindir)\" \
|
|
|
e354a5 |
+ -DCOMPLOCALEDIR_PATH=\"$(complocaledir)\"
|
|
|
e354a5 |
|
|
|
e354a5 |
ifeq (,$(CXX))
|
|
|
e354a5 |
LINKS_DSO_PROGRAM = links-dso-program-c
|
|
|
e354a5 |
diff --git a/support/support.h b/support/support.h
|
|
|
e354a5 |
index 121cc9e9b7c98ca6..3af87f85fe1b762d 100644
|
|
|
e354a5 |
--- a/support/support.h
|
|
|
e354a5 |
+++ b/support/support.h
|
|
|
e354a5 |
@@ -112,6 +112,8 @@ extern const char support_bindir_prefix[];
|
|
|
e354a5 |
extern const char support_sbindir_prefix[];
|
|
|
e354a5 |
/* Corresponds to the install's sbin/ directory (without prefix). */
|
|
|
e354a5 |
extern const char support_install_rootsbindir[];
|
|
|
e354a5 |
+/* Corresponds to the install's compiled locale directory. */
|
|
|
e354a5 |
+extern const char support_complocaledir_prefix[];
|
|
|
e354a5 |
|
|
|
e354a5 |
extern ssize_t support_copy_file_range (int, off64_t *, int, off64_t *,
|
|
|
e354a5 |
size_t, unsigned int);
|
|
|
e354a5 |
diff --git a/support/support_paths.c b/support/support_paths.c
|
|
|
e354a5 |
index eb2390227433aa70..6b15fae0f0173b1e 100644
|
|
|
e354a5 |
--- a/support/support_paths.c
|
|
|
e354a5 |
+++ b/support/support_paths.c
|
|
|
e354a5 |
@@ -78,3 +78,10 @@ const char support_install_rootsbindir[] = ROOTSBINDIR_PATH;
|
|
|
e354a5 |
#else
|
|
|
e354a5 |
# error please -DROOTSBINDIR_PATH=something in the Makefile
|
|
|
e354a5 |
#endif
|
|
|
e354a5 |
+
|
|
|
e354a5 |
+#ifdef COMPLOCALEDIR_PATH
|
|
|
e354a5 |
+/* Corresponds to the install's compiled locale directory. */
|
|
|
e354a5 |
+const char support_complocaledir_prefix[] = COMPLOCALEDIR_PATH;
|
|
|
e354a5 |
+#else
|
|
|
e354a5 |
+# error please -DCOMPLOCALEDIR_PATH=something in the Makefile
|
|
|
e354a5 |
+#endif
|