setlocaleで "POSIX"を指定するときの注意

Glibcに限った話なんですが、setlocaleで "POSIX"を指定しても
現在の localeは "POSIX"になりません。"C"になります。これは
内部的に同じものを意味するので大して問題になるわけではないん
ですが、現在の localeの文字列を比較するような場合は注意が
必要です。

検証

#include <stdio.h>
#include <locale.h>

int main(void)
{
    char *s;

    setlocale(LC_MESSAGES, "POSIX");

    s = setlocale(LC_MESSAGES, NULL);
    printf("current locale '%s'\n", s);

    return 0;
}

結果は以下のようになります。

current locale 'C'

もちろん Perlでも同じです。

#!perl
use strict;
use warnings;

use POSIX qw/LC_ALL setlocale/;

setlocale(LC_ALL, 'POSIX');
my $lc = setlocale(LC_ALL);
print "current locale '$lc'\n";

BSDMacの libcの実装だとこうならず、戻り値に"POSIX"が得られます。

実装

詳しくは読んでないですが、おそらく問題となるのは下記の
部分でしょう(glibc/locale/setlocale.cより抜粋)

      /* All the categories use the same name.  */
      if (strcmp (newnames[0], _nl_C_name) == 0
          || strcmp (newnames[0], _nl_POSIX_name) == 0)
        return (char *) _nl_C_name;

指定した名前が _nl_C_name(="C")か _nl_POSIX_name(="POSIX")なら
_nl_C_nameを返すようになっています。これが localeとしてセット
されることになるので "POSIX"を指定しても実際には "C"がセット
されることになってしまいます。


POSIXでは setlocaleは指定した文字列を返すことにもなっていないので
これはバグではなく、glibcの仕様ということになるかと思います。
"POSIX"以外でも同じようなことがある可能性は十分にあるので、
戻り値の比較を行う場合は気をつけてください。

おわりに

https://github.com/hideo55/p5-autolocale


autolocale 0.03が Linuxでテストをパスしなくなったのは
これが原因ではないかと思います。