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");

Reply via email to