Hi,
I have a question about us catopen(3) implementation: currently we
bypass locale settings (that could be setted from setlocale(3)) to
directly go reading user locale settings from environment.
So a program that try to set specific locale (using setlocale(LC_ALL,
"C") for example), will not use the "C" locale for LC_MESSAGES (when
using catopen).
The current code is:
--- from lib/libc/nls/catopen.c ---
// in _catopen(const char *name, int oflag)
lang = NULL;
if (oflag & NL_CAT_LOCALE) {
lang = getenv("LC_ALL");
if (lang == NULL)
lang = getenv("LC_MESSAGES");
}
if (lang == NULL)
lang = getenv("LANG");
if (lang == NULL)
lang = NLS_DEFAULT_LANG;
if (strcmp(lang, "POSIX") == 0)
lang = NLS_DEFAULT_LANG;
---
The POSIX specification is not very clear, as it speak about
"setting of LC_MESSAGES" and "LANG environment variable".
> This default may be affected by the setting of LC_MESSAGES if the
> value of oflag is NL_CAT_LOCALE, or the LANG environment variable if
> oflag is 0.
Others implementations checked use LC_MESSAGES from locale setting
(instead of value from environment), and fallback to getenv:
- NetBSD
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/nls/catopen.c?rev=1.33&content-type=text/x-cvsweb-markup
if (oflag == NL_CAT_LOCALE) {
lang = loc->part_name[LC_MESSAGES];
}
- FreeBSD
https://svnweb.freebsd.org/base/head/lib/libc/nls/msgcat.c?revision=278530&view=markup
if (type == NL_CAT_LOCALE)
lang = setlocale(LC_MESSAGES, NULL);
else
lang = getenv("LANG");
- DragonFlyBSD (same code as FreeBSD)
http://gitweb.dragonflybsd.org/dragonfly.git/blob/HEAD:/lib/libc/nls/msgcat.c
(same code as FreeBSD)
- Linux
http://sourceware.org/git/?p=glibc.git;a=blob;f=catgets/catgets.c;h=cf93d56c5407ed737cdb8975b77a8b1e5aa26903;hb=HEAD
if (flag == NL_CAT_LOCALE)
/* Use the current locale setting for LC_MESSAGES. */
env_var = setlocale (LC_MESSAGES, NULL);
else
/* Use the LANG environment variable. */
env_var = getenv ("LANG");
The following patch change the behaviour of catopen(3). It will have
impact on program that use catopen(3) (or related functions like
strerror(3)) and don't initialise locale settings using
setlocale(LC_ALL, ""), as the default value for locale (without
initialisation) is "C".
One example in base is kill program:
- don't use setlocale
- use err(3) function (which use strerror, which use catopen)
Generic example:
$ cat catopen-test.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/errno.h>
#include <locale.h>
int main() {
//setlocale(LC_ALL, "");
printf("EPERM = %s\n", strerror(EPERM));
return (EXIT_SUCCESS);
}
Old behaviour:
$ env -i LC_ALL=fr_FR.UTF-8 ./catopen-test
EPERM = Opération non autorisée
New behaviour:
$ env -i LC_ALL=fr_FR.UTF-8 ./catopen-test
EPERM = Operation not permitted
Same program with setlocale initialisation:
$ env -i LC_ALL=fr_FR.UTF-8 ./catopen-test
EPERM = Opération non autorisée
If this change is desirable, I will propose patchs for programs in base
in order to call setlocale(LC_ALL, "") at program initilisation.
Comments, ideas ?
--
Sébastien Marie
Index: nls/catopen.c
===================================================================
RCS file: /cvs/src/lib/libc/nls/catopen.c,v
retrieving revision 1.16
diff -u -p -r1.16 catopen.c
--- nls/catopen.c 16 Jan 2015 16:48:51 -0000 1.16
+++ nls/catopen.c 12 Jun 2015 06:37:22 -0000
@@ -39,6 +39,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <nl_types.h>
+#include <locale.h>
#define NLS_DEFAULT_PATH
"/usr/share/nls/%L/%N.cat:/usr/share/nls/%l.%c/%N.cat:/usr/share/nls/%l/%N.cat"
#define NLS_DEFAULT_LANG "C"
@@ -68,9 +69,7 @@ _catopen(const char *name, int oflag)
lang = NULL;
if (oflag & NL_CAT_LOCALE) {
- lang = getenv("LC_ALL");
- if (lang == NULL)
- lang = getenv("LC_MESSAGES");
+ lang = setlocale(LC_MESSAGES, NULL);
}
if (lang == NULL)
lang = getenv("LANG");