2008/09/12(Fri)
○[NetBSD] tech-userlevel
そもそもNetBSDのC localeはISO646だからなぁ。
ソースをそれ以外の文字コードで書くっちゅうのは微妙だNE。
コメントだけUTF-8、リテラルはISO646にしとくってもチェックが手間になるし。
Cの規格としてはJISX0310の5.2.1あたりを読めば判るとおり、文字集合については
- ソース文字集合(source character set)
- 実行時文字集合(execution character set)
の2つが定められており、gccでは
- --input-charset
- --exec-charset
ちゅうオプションがすでに実装されてたり。
動作としては
$ gcc --input-charset=eucJP --exec-charset=UTF-8 hoge.c
ちゅうのは
$ iconv -f eucJP -t UTF-8 | gcc -xc -
というようなイメージね、つまり文字(列)リテラルもeucJP -> UTF-8変換が行われる。
でもこの実行時文字集合を指定可能、ちゅうgccの--exec-charsetオプションは
ワイド文字(列)リテラル(つまりL接頭語)というものと喰いあわせが悪いのよね。
ほかほかごはんとアフラトキシンB1くらいのノリで。
ワイド文字(列)リテラルについては、JIS X3010の6.4.4~6.4.5を読むと
処理系定義のその時点のロケールにおけるmbtowc/mbstowcs関数によって定義
とある、この一文を読むと「その時点」ってのはどこ?ちゅー疑問が涌くのだけど
まぁこれは直前に
翻訳フェーズ(7)において,
(中略)
結果の多バイト文字の並びは, 静的記憶域期間をもち,
(以下略)
とあるので、静的記憶域期間(=static)つまりプログラムの開始直前ですやね。
POSIX localeにおいては実行時のlocaleの切替はsetlocale(3)で行うので
ユーザが明示的に静的記憶域期間のロケールをきりかえることは出来ません。
つまり「この時点のロケール」ちゅうのはシステムのデフォルトなのであって
NetBSDの場合はC locale=ISO646であるわけのなのですよ。
つまりgcc --exec-charsetなんてコンパイルする人が自由に変更可能だとよろしくないわけで。
この問題に加えて、gccは(中略)なのでwchar_tは常にUCS4と仮定して
ワイド文字(列)リテラルをコンパイルしてくれやがります。
$ cat >test.c
#include <wchar.h>
int
main(void)
{
printf("%#x\n", L'あ');
}
^D
$ gcc --input-charset=euc-jp --exec-charset=euc-jp -o test test.c
$ ./test
0x3042
$ gcc --input-charset=euc-jp --exec-charset=utf-8 -o test test.c
$ ./test
0x3042
実行時文字集合がeucJPだろうがUTF-8だろうが、どっちもU+3042ですやね。
以上のことを考えた上で、下のソース中のワイド文字列リテラルは
- どう解釈されるか
- 本当はどう動くのが正しいか
考えてみませう。
$ cat > test.c
#include <locale.h>
int
main(void)
{
setlocale(LC_ALL, "ja_JP.eucJP");
wprintf(L"あいうえお");
}
いやー本当に、C言語って、orzっですね。さいなら、さいなら。
つかEBCDICとか考えると、L'\0'以外は全て移植性(以下略
ちなみにMSVCの場合デフォルトロケールがCP932の上に、wchar_t=UCS2だから
ほとんどの人が意識していないと思われる。
まぁふつーgettext(3)なりcatgets(3)なり使ってi18n化するから問題ないのだけど。
んで、更にUnicodeリテラル、つまりU接頭語なんてものを導入すると
wchar_t != UCS4な実装だと(以下略
↑とは全く関係ない理由でragge氏がUTF-8にしよーよといってて全俺が泣いた。
ソースのコメントのi18n/m17n、javadocとかdoxygenのようなドキュメント生成まで考えると
もうplain text以外でやれ、という世界だよな。
まぁもうすぐ皆ピンク色の光線照射されてテレパシー使えるようになるから。
○[NetBSD] Citrus iconv API
__iconv_{get,free}_list()は使ってるのですか、了解しました。
iconv -l相当もlibc interfaceには無いのがアレですな。
PortableにiconvのサポートするCES名一覧を取得するコードを書こうとすると
厳密にはsystem("iconv -l")しかないという(いちおうlスイッチはSUSv3以降で標準化されたので)。
まぁでも出力はunspecified formatなのでどっちみち解析するのに困るし
Solarisのマニュアルにはシェルスクリプト等で使うんじゃねぇゴルアとまで
書いてあったりするのだけれども。
そもそもiconv自体OO的にはevilなわけで、非推奨でまったくもって正しい希ガス。
//TRANSLITをサポートするなら WindowsBestFitもやらんとならんのかいな。
個人的には//NFCとか//NFDなんかは欲しいけど、//とかスイッチ化する気はないし
iconvの仕事かぁ?といわれると返す言葉がない。
突然思い出したTODO、CP936の0x80<->U+20AC問題、csmapperの方は
CP936EURO/UCS mapper_zone 0x80-0x80:+0x202C
UCS/CP936EURO mapper_zone 0x20AC-0x20AC:-0x202C
GB2312:CP936/UCS mapper_parallel CP936EURO/UCS,GB2312EXT/UCS,GB2312/UCS
UCS/GB2312:CP936 mapper_parallel UCS/CP936EURO,UCS/GB2312EXT,UCS/GB2312
とかにすりゃいいのだけど、citrus_gbk2k.c側が0x80をEILSEQにしちまうので直す。
あーこれもsambaとかことを考えるとpullup必要だすな。
ところでCP54936ってGB18030のaliasにしてもおkなのだろか。
CP54932はCP936のスーパーセット、EURO記号が0x80(CP936) vs 0xA2E2(GB18030)と
異なる問題があるのだけども、なんかこれworkaround入ってたりするのかな。