The Man Who Fell From The Wrong Side Of The Sky:2010年1月分

2010/1/1(Fri)

今日

@

あけことよろ

@

昨日の夜は部分月蝕きれいでしたね、望遠鏡欲しくなっちゃったい。

[NetBSD] libedit wide-character support

@

だと、またこれは災害に近いpatchを勝手にレビューして勝手にOKしたもんだ栗金団。
今メールと修正patch書いてる最中だけど、ぱっとみてこれぐらいは指摘してくれよホンと。

1. don't write UTF-8 locale dependent ``cheat'' code in locale independent libedit,
such as enc_width(), utf8_islead() and so on, completely meaningless.

2. cast wchar_t -> wint_t in the argument of isw* function.
consider sizeof(wchar_t) < sizeof(wint_t) case (for integer promotion).

3. kill evil __STDC_ISO_10646__ usage.
if you want kick some encodings not compatible with ASCII(like EBCDIC),
use __STDC_MB_MIGHT_NEQ_WC__ instead.
see http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_321.htm

4. library function must not use thread unsafe version of mb/wc conversion,
like mbtowc, wctomb and so on. use mbrtowc/wcrtomb instead.
this broke compatibility with some old apllication uses of mbtowc/wctomb.

とくに1番目なんてあんたらMDなコードがMIに入り込んだらどんだけギャーギャー騒ぐのかと。

いつものやつおいときますね。

「UTF-8!出た!UTF-8出た!得意技!UTF-8出た!UTF-8!これ!UTF-8出たよ〜〜!」 俺は限界だと思った。

2010/1/2(Sat)

今日

@

新年初仕事は以前このチラシの裏にも書いた__deregister_frame_info crash問題( その1その2)について
lib/39986にanalyzeを投げますた。まぁ 脊髄反射 → 放置プレイ といういつものパターンに陥りそうですがw

しかもも、これ解決してうれしいのがpkgsrc/inputmethod/scimのgtkimmmmmmmmmmoduleくらいなんだよな。
正気なプログラマ(=オッサン)ならCからC++をdlopen(3)しようなんて死亡フラグしないし
するなら例外を使わずに RTLD_LAZYRTLD_LOCALするか、アプリケーション自体を-lgcc_sつけるし。
まぁ正直dlopen(3)なんか使ってるGTK2+がうんこであって、 汝Motifを愛せよと思うのですが *1

しかもももそっちは obacheさんscim-bridgeの方を使うように
すでに対策(pkgsrc/inputmethod/scim-bridge)してくれてるので誰も困らないし。

しかももももも SCIMってHTTP 500だし、もういいんじゃないかなーとも思うんだけどねぇ *2*3

*1:俺?Athena Widgetばんじゃーい
*2:ashieさん引き継ぐ可能性もあるみたいですけど
*3:まあ未だにkinput2使ってるお前はなんなのといわれると返す言葉も無いなw

2010/1/10(Sun)

[FreeBSD] environ(7)

先日のエントリで、FreeBSD の unsetenv(3)をあんまりいじめないで(バリバリ)!
という記事をそのうち書くと宣言したので、そもそもなんであんなに他の
*BSD とは異なる実装に書き直す必要があったのかというお話。

@

まず第壱に memory leak 問題すな、setenv(3) は環境変数の為に heap memory を割り当てますが
これは environ(7) の 仕様を見ていただくと判ると思いますが

extern char **environ;

と宣言する事でユーザがこれを改変してしまう事が可能なのですよね、ですので単純に

#include <stdlib.h>

extern char **environ;

int
main(void)
{
	setenv("FOO", "bar", 1);
	environ = malloc(1024 * sizeof(char *));
	...
}

と上書きしただけで古い *BSD の libc 実装では、setenv(3) が内部で確保した heap がまんま
memory leak してしまうのです、なんというおおらかな時代の遺物。

この問題は OpenBSDNetBSDでは、およそ5年前に対応がされていますが
FreeBSD ではその後もしばらくは放置されとりました。

千奈美に、環境変数をgetenv(3)を介さずに直接参照するには上の方法以外に

int
main(int argc, char *argv[], char **envp)
{
	char **p;
	for (p = envp; *p; ++p)
		printf("%s\n", *p);
	return 0;
}

と main 3番目の引数を使う方法があったりしますが、standard ではないことに注意。

話を戻して、沙良にもういっちょ setenv(3) で上書きを選択した場合、常に新規に heap を確保し直すので

	setenv("FOO", "bar", 1);
	setenv("FOO", "buzz", 1);

というようなコードを実行すると、environ[n] もさっくり leak する問題もあります。

なんでこれ realloc(3) しないの?という素朴な疑問が沸きますが、これは environ[n] が
本当に heap 上に存在し realloc(3) 可能かどうかは、environ(7) が自由に弄れてしまうという
仕様の都合上まったく保証できないんですよね、例えば

static char s[1024];

strlcpy(s, "FOO=bar", sizeof(s));
environ[0] = s;

というコードもたうぜんありうるわけでして。

こっちの leak 問題については、まだ何ら NetBSDOpenBSDは対策してません。

 82         if ((c = __findenv(name, &offset)) != NULL) {
 83                 if (!rewrite)
 84                         goto good;
 85                 if (strlen(c) >= l_value)       /* old larger; copy over */
 86                         goto copy;
 87         } else {                                        /* create new slot */
...
106         if ((c = malloc(size + l_value + 2)) == NULL)
107                 goto bad;
108         environ[offset] = c;
109         (void)memcpy(c, name, size);
110         c += size;
111         *c++ = '=';
112 copy:
113         (void)memcpy(c, value, l_value + 1);

82行目の __findenv() によるチェックで既に環境変数が存在する場合、106行目で新規に malloc(3) して置き換えます。
ちなみに上のコードは NetBSD の実装ですが、前回セットした値と長さが一致するか短い場合には
再利用しているのですが、でもこれ c がもし書込不可領域に存在したら?の ケアレス航空ですな。
よって OpenBSD では85〜86行目がコメントアウトされとりやす:D

@

第弐に SUSv3 への追従です、putenv(3) について 仕様では

the string pointed to by string shall become part of the environment,
so altering the string shall change the environment.

とあり、putenv(3) の引数として渡した文字列を後から変更した場合、getenv(3) の結果に反映されるとあります。

$ cat >putenv_test.c
#include <assert.h>
int
main(void)
{
	static char s[1024];

	strlcpy(s, "FOO=bar", sizeof(s));
	putenv(s);
	strlcpy(s, "FOO=buzz", sizeof(s));
	assert(!strcmp("buzz", getenv("FOO")));
}
^D

うーむなんというデンジャラスな仕様…
すでに CERT Secure Coding Standardの記事にもなってるけど、ここはもっと突っ込んで

使うなボケ

で良かったと思うんだけどな…しかも RATIONAL には

The standard developers noted that putenv() is the only function available to add
to the environment without permitting memory leaks.

と、leak しないのは putenv(3) だけ!とか使用を推奨しとるように読めるのがどうにもアレ。
TOG先生の次回作にご期待ください。

putenv(3) は HISTORY によると 4.3BSD-Renoよりとあるのですが、この実装では
単に setenv(3) の wrapper 関数で、渡された "FOO=bar" はコピーされた上で environ(7) にセットされます。

34         if (!(p = strdup(str)))
35                 return(1);
...
41         rval = setenv(p, equal + 1, 1);

よってその実装を受け継ぐ NetBSD/OpenBSD 上では、先ほどのコード例は

$ make putenv_test
$ ./putenv_test
assertion "!strcmp("buzz", getenv("FOO"))" failed: file "test.c", line 10, function "main"
Abort (core dumped)

と SUSv3 通りには動いてくれません、ヒャッハー!標準化モヒカン狩りまくり。

しかしやっぱこれ SUSv3 がウンコだと思うんだよね、おそらく SysV 系の putenv(3)の実装に *1
これに依存したアプリがあったとかで、 RPGじゃなくて XPG つまり X/Open Portability Guide を作る際
こんなキナ臭い仕様まで明文化してしまったんじゃねーのと。
先日の dirname(3) なんかでは SysV と *BSD の最大公約数を取ってたのに…

@

第参は「unsetenv(3)は本当にちゃんと環境変数を根こそぎお掃除してるのか?」問題すね。
以下の姑コードを実行してみましょう。

$ cat >unsetenv_test.c
#include <stdlib.h>
int
main() {
        extern char **environ;
        char *save;

        setenv("FOO", "bar", 1);
        save = environ[0];
        environ[0] = NULL;
        unsetenv("FOO");
        environ[0] = save;
        printf("%s\n", getenv("FOO"));
}
^D

こいつをNetBSD/OpenBSDそしてglibc2で実行すると

$ make unsetenv_test
$ ./unsetenv_test
bar

あらいやだ、きれいになってないじゃないの。ちょっとMIT子さん(以下ry

これは environ(7) の終端チェックを NULL か否かでしかやってないからです、NetBSD の実装だと

123        for (p = environ; (c = *p) != NULL; ++p)
124                if (strncmp(c, name, len) == 0 && c[len] == '=') {

んな感じ。まぁこれ烏賊にもヤバそうに見えますが、冷静に考えるとこれ攻撃には使い道ないと思うけどね。
これを悪用できる状況ならもっと別の攻撃した方が早いという良くあるお話。

でも OpenSolaris や、FreeBSD ではこんな細工をものともせず綺麗にお掃除してくれます。

$ make unsetenv_test
$ ./unsetenv_test

なんという吸引力、だいそんだいそん!
この commit logを参照のこと。

@

ということで、この3つの問題に果敢に取り組んで地雷を踏んだらサヨウナラしちゃったのが
FreeBSD-SA-09:16.rtld ですな、おそらくOpenSolaris の libc 実装が同じようなことをやっとるので
インスパイアされちゃったんだろうけど…
要は「今動いてるものは(なるべく)触るな、なぜなら今動いてるのは奇跡だから」といういつものお話。
(SUSv3対応しないとはいってないけどもっと清朝にねというアヘン戦争!)

@

んで後日談、FreeBSD は今回の exploit で unsetenv(3) を一端は他の *BSD と同様に、environ(7) に
不正な値が入っていた場合でもスルーして処理を続行するように 変更したのを
なぜかまた 元に戻してるのよね、どうしてそうなるのかなかな。

これおそらく仕様を勘違いしてると思うんだよな。

    The unsetenv() function shall fail if:

    [EINVAL]
        The name argument is a null pointer, points to an empty string,
        or points to a string containing an '=' character.

こいつを

(引数として渡された) name が NULL とか空文字とか '=' を含む不正な文字の場合、EINVAL を返す

environ(7) で保持している環境変数の name が〜

と読み間違えてるんじゃないの?疑惑、まぁNetBSDじゃないからいいけど(ぉ

*1:ググルさんでは当時の SVR4 のマヌアルがヒットしないので、アマゾンでポチりましたのでまた後日…

2010/1/14(Thu)

今日

@

UNIX System V プログラマ・リファレンス・マニュアル 第2版 リリース 3.0届いたお。

結論からいうと 先日のputenv(3)ネタ

string が指す文字列は環境の一部となりますので、文字列を変えると環境も変わることになります。

の一文があるので、SVR3(1986)の時点で存在してたちうこと。
よって4.3BSD-Reno(1990)の実装が手抜きのパチモノだったということげ。

しかしこうなると こっちも買って差分をチェックしたくなりますな…貧乏だから買わないけど。

2010/1/15(Fri)

[NetBSD] editline(3)

@ libeditとは?

NetBSD libeditGNU readline互換ライブラリで、以下のような機能を提供します。

  • (キーバインドが)vi互換でなければ生きて行けない
  • emacs互換は生きる資格がない(ぉ
  • 老化に伴う健忘対策としてコマンドラインを記憶する機能
  • Tabキーによる補完機能、無駄に叩かれたTabキーの発する静電気で東京都が一晩(ry

NetBSDではsh(1)にmail(1)、そしてftp(1)などが使用しています。

@ libeditの現状

このライブラリはつい最近まで国際化されておらず、multibyteの扱いに難があったのですが
GNU readline 6.0でmultibyte supportが入ったことにインスパイヤされたのか
去年の暮れに UTF-8サポートしたよという知らせが。

これはへんたい喜ばしいことなんですが、いろいろと実装に問題のあるコードでして。
国際化はUTF-8にまかせろー(バリバリ)」ときたら「 やめて!」と返すしかありませんやね。

私もざっと問題点を 指摘したメールを投げまして、commitしたえらい人と 面接しまして

「特技はI5N(イオナズン)とありますが?」
「いいえ。I18Nです。」
「I18Nとは何のことですか?」
「国際化です。」

(以下略)

という流れになり、私が書き直すことになりましてん。

ぼちぼちコード読みはじめたとこなんですが、どうせならその過程でもネタにしてみようかね、と。
いきなり全部の解説は無理なので、ディアゴス○ティーニ方式で。

2010/1/16(Sat)

[NetBSD] libedit I18N化への道(その1)

タイトルはインスパイヤ・ザ・NeXTじゃなくてNEWSちうことで :D

@ まずは変更点の確認を

まず手始めに、今回の commit logより read.cの監査から第1回はスタートすることにしましょう。

rev1.52 と rev1.53 の 差分を眺めてみると

  • char → Charへの置き換え
  • isprint → Isprint 〃
  • read_char() の大幅な書き直し

といった作業が行われていることがわかります。

@ バイト(char)指向APIからワイド文字(wchar_t)指向APIへの置換

Char および Isprint は今回cvs(1) addされた chartype.hで定義されており

#ifdef WIDECHAR

#define Char			wchar_t
#define Isprint(x)  iswprint(x)

#else /* NARROW */

#define Char			char
#define Isprint(x)  isprint((unsigned char)x)

#endif

と定義されていて

  • -DWIDECHARでビルドされたときはワイド文字(wchar_t)指向API
  • それ以外はバイト(char)指向APIを使う

という、ソレナンテMicrosoft Visual C++の TCHARマクロのようなもの。
これはもう 痴漢置換レベルの作業です、楽勝ですな。詳細についてはまた次回以降で。

@ 低水準APIと国際化対応(高水準APIに置き換え可能?)

次はread_char()なのですが…

312 private int
313 read_char(EditLine *el, Char *cp)
314 {
315         ssize_t num_read;
316         int tried = 0;
317         char cbuf[MB_LEN_MAX];
318         int cbp = 0;
319         int bytes = 0;
320
321  again:
322         el->el_signal->sig_no = 0;
323         while ((num_read = read(el->el_infd, cbuf + cbp, 1)) == -1) {
324                 if (el->el_signal->sig_no == SIGCONT) {
...
327                         goto again;
328                 }
329                 if (!tried && read__fixio(el->el_infd, errno) == 0)
...
335         }
336
337 #ifdef WIDECHAR
338         if (el->el_flags & CHARSET_IS_UTF8) {
339                 if (!utf8_islead((unsigned char)cbuf[0]))
340                         goto again; /* discard the byte we read and try again */
342                 if ((bytes = ct_mbtowc(cp, cbuf, cbp)) == -1) {
343                         ct_mbtowc_reset;
...
348                         goto again;
349                 }
350         } else  /* we don't support other multibyte charsets */
351 #endif
352                 *cp = (unsigned char)cbuf[0];
...

これはひどい!両生類のクソをかき集めた価値も無いコードです。

we don't support other multibyte charsets

weって誰っすか、 cargo cult?

それでは早速コードの解説。

  • (323行目) ファイル記述子el_infdからread(2)し、MB_LEN_MAXのサイズなバッファcbufに書き込み
  • (342行目) 成功した場合にはmbtowc(3)でワイド文字に変換を試み、失敗したらやり直し

というコードです。

このコードが通常のファイルを扱うi18nプログラミングであるならば、迷うことなくread(2)のような
低水準API(システムコール)の使用は止めましょう、ぐちゃぐちゃ書いてバグ仕込んでる暇があったら

	wchar_t wc;

	wc = fgetwc(el->el_infile);
	if (wc == WEOF) {
		*cp = 0;
		return -1;
	}
	*cp = wc;

	return 1;

と高水準API(ライブラリ)であるfgetwc(3)使えばおk。

…なのですが今回はちょっと特殊な処理が間に挟まっています、本当に高水準APIに置き換えても大丈夫でしょうか?

@ SIGCONTってなーにー?

まずは324行目で、SIGCONTがセットされているかをチェックしてる様子、さてこれは何ぞ。
shell上で実行中のアプリはCtrl+Zを送ることで停止し、fg <ジョブID>で復帰しますが
この復帰をアプリにお知らせするシグナルがSIGCONTです、確認してみまひょ。

ksh$ cat > test_sigcont.c
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>

static sigjmp_buf stop_jmp;

static void
stop_handler(int signo)
{
	siglongjmp(stop_jmp, signo);
}

int
main(void)
{
	struct sigaction act;
	int ret;
	char ch;

	act.sa_handler = &stop_handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	sigaction(SIGCONT, &act, NULL);
	ret = sigsetjmp(stop_jmp, 1);
	if (ret == SIGCONT)
		puts("catch SIGCONT.");

	puts("waiting input.");
	read(0, &ch, 1);

	return 0;
}
^D

ファイル記述子0、つまり標準入力から1byte読み込んで終了するだけの簡単なお仕事。
コンパイルして実行してみます。

ksh$ make test_sigcont
cc -O2   -o test_sigcont test_sigcont.c
ksh$ ./test_sigcont
waiting input.

この状態でCtrl+Zを入力し、そしてfgで復帰させてみましょう。

^Z[1] Stopped               ./test_sigcont
ksh$ fg %1
catch SIGCONT.
waiting input.
a
ksh$

確かにSIGCONTシグナルが送られていますね。

どうやらこのコードはread(2)に失敗した場合、アプリが復帰したことを検知したら
リトライする必要があるようです、また次回以降に理由を調べてみることにしましょう。

@ × ブクロキング ○ブロッキング (ふー、びっくりした)。

次に329行目、read__fixio()という関数でerrnoを元にゴニョっているようです。
そのゴーニョゴーニョ魚の子の内容ですが、ifdefの嵐なのでコードは貼りませんがこんな感じ。

  • EAGAIN(諦めるなよ!)あるいはEWOULDBLOCK(はい今君ブロックされた!)エラーの場合
    ファイルはO_NDELAY(O_NONBLOCK)を指定して非ブロックモードで開かれてると判断しfcntl(2)でO_NDELAYを解除
    あるいはioctl(2)でFIONBIOを指定しブロックモードに変更、成功すれば0を返す
  • EINTR(シグナルで中断されたよ!)の場合
    アキラメロンで-1を返す

この非ブロッキングモードの使用例は、CERT Secure Coding Standardの FIO32-C.
「通常ファイルに固有の操作をデバイスファイルに対して行わない」あたりをどぞ。

@ さて、結論は?

うーんどうやらここはread(2)のままにして置かないと良くないことが起きそうな予感。
それよりもなにも、この場所以外でもread(2)を使いまくりなことがネックになってきます。

常識の範囲と思われるので詳しく説明しませんが、入出力において低水準(intなファイル記述子を引数にとる)と
高水準(FILE構造体を引数にとる)の両者のAPIをまじぇまじぇすることは禁物です、後者はstdio.hで定義される
BUFSIZ定数分バッファリングしますので、混在させると予期しない結果になる恐れあり。

少なくともバッファリングだけは、setbuf(3)やsetvbuf(3)によって無効にすることも可能です。

	FILE *fp;

	setbuf(fp, NULL); /* バッファを無効 */

しかし、fgetwc(3)が内部で使用するmbrtowc(3)の引数として渡すmbstate_tだけは
FILE構造体にセットしたり逆にこれをぶっこ抜く標準的な手段は今のところ用意されていません。
# これやっぱC1Xにはset_mbstate_fgetwc/set_mbstate_fputwcとかゆーAPI必要だと思うんですが…

さすがに全てを高水準APIに置き換えていくのは、国際化対応ってレベルジャネーゾ。
また「今動いてるものはなるべく触らない(なぜなら今動いてるのは奇跡だから)」ルール
を思い出してください、元のコードをなるべく尊重した形で作業していく、これ大事。

@ 次回予告

かなりの文章量になってしまったので今回はここまでにして、実際のread.cの書き直しは次回に譲り
またI18Nコードにおけるチート行為についてその問題点を検証する予定、disるぜ〜glibc2〜超disるぜ〜

2010/1/18(Mon)

今日

@

Power Of Dreams reunionとかマジか、日本に来るかな(たぶん無理

あとは Five Thirtyのreunionと再発キボン。

2010/1/19(Tue)

[NetBSD] libedit I18N への道(その2)

先日の続きです

@ 出た!UTF-8!得意技!

先日監査した read.cでは、CHARSET_IS_UTF8というフラグが立っていた場合のみ国際化機能を有効にしていました。

337 #ifdef WIDECHAR
338         if (el->el_flags & CHARSET_IS_UTF8) {
339                 if (!utf8_islead((unsigned char)cbuf[0]))

このフラグは el.cの中でセットされています。

 88 #ifdef WIDECHAR
 89         if ((locale = setlocale(LC_CTYPE, NULL)) != NULL){
 90                 if (strcasestr(locale, ".UTF-8") != NULL)
 91                         el->el_flags |= CHARSET_IS_UTF8;
 92         }
 93 #endif

setlocale(3)で現在のLC_CTYPEカテゴリの設定値を取得し、".[Uu][Tt][Ff]-8"という文字列が含まれれば
Editline構造体elのel_flagsにCHARSET_IS_UTF8フラグのビットを立てるという処理です。

なぜUTF-8だけ特別扱い!という抗議は後回しにして、このコードそのものにも問題がある事を指摘しておきましょう。
それは「現在のlocaleの文字コード(CODESET)が何かを調べるために、locale nameの一部を切り出す」という行為
これそのものがマテガイ(馬刀貝、蟶貝とも。二枚貝綱マテガイ科の1種)であるということです。

@ それは実相寺いぞんです!

setlocale(3)に指定するlocale nameは

  • デフォルト値として"C"あるいは"POSIX"
  • 環境変数LANG/LC_*から取得するために""を指定
  • 現在の設定値を返すためにNULLを指定

以外はすべて実装依存であることは以前もこの チラシの裏で書きました。

X/Open Portability Guide(XPG)では

	language [_territory] [.codeset] [@modifier]

という形式で言語以外はすべて省略可です、これよりのちの標準化の動きとしては
LI18NUX Locale Name Guidelineがありまして、こちらではCODESETは省略不可となったのですが
現状Linux/glibc2はXPGとの後方互換性を保つために、CODESETも省略可能のままです。
省略されてしまったら文字コードなんて拾えませんがな。

ただし省略した場合はen_USならISO8859-1、ja_JPならeucJPなどの、XPG時代のdense encodingとなるケースがほとんどです。
UTF-8 localeを使いたい場合は、明示的ににCODESETを指定する必要がありますので、Linux/glibc2ならうまくいくかもね。
# でもcanonical nameであるLANG=ja_JP.utf8だとマッチしないのだよね :P

@ Before Unicode, After Unicode

Unicodeもすでに 1/4世紀を経ています(Unixに至ってはもう40年…)、Unicode生誕以降にコンピュータ/情報処理が
伝来した言語+地域圏においては、この広大な21bitの幅を持つsparse encodingであるUTF-8がデフォルトなことが多いのです。

一例としてあげるならglibc2のaa_ET(Afar - Ethiopia) localeがあります。

	setlocale(LC_ALL, "aa_ET");

この場合、UTF-8なlocaleであるにもかかわらず、先ほどのCHARSET_IS_UTF8フラグは有効にはなりません。
libeditに祝福されたUTF-8のはずなのに、国際化機能による恩恵を受けることが出来ないわけです。

@ 文字コードを取得するだけであれば

上記のコードをもっとマシなものに書き改めるのであれば、nl_langinfo(3)を使うかでしょう。

#include <langinfo.h>
...
	char *codeset = nl_langinfo(CODESET);
	if (!strcmp(codeset, "UTF-8"))
		el->el_flags |= CHARSET_IS_UTF8;


これであれば、aa_ETだろうがen_US.UTF-8だろうが等しくlocaleの文字コード名を返します。

しかし困ったことに(?)これも返されるCODESETは実装依存です。文字列は"UTF-8"かもしれませんし
"utf8"かもしれません。もしかすると"Unicode Transformation Format 8"である可能性だってあるのです。
ですのでこのコードが予期したとおりに動くとは限りません、よって残念ながらsetlocale(LC_ALL, NULL)の戻り値も
nl_langinfo(CODESET)の戻り値もどちらもopaque(不透明)として扱うのが正しい姿勢でしょう。

@ イコール・ランゲージ・オポチュニティ

そもそもなぜUTF-8だけを特別扱い(神聖視?笑)する必要があるのでしょうか?
本来国際化機能というものは、全ての言語・地域の人間が等しく恩恵を受ける機会を得られるものでなければなりませんし
またその通りに実装されているはずです(足りない点は大いにありますが、例えばmulti-locale機能など)。

これに対して「Unicode/UTF-8は言語も地域も限定しない」と熱心なUnicodeのファンはいうでしょう。
しかし多くの国においてLegacy Encoding(日本ならeucJP、Shift_JISなど)が存在し
そもそも(その2)POSIX localeはそれらのことも考慮して設計されたものであるにもかかわらず
Unicode/UTF-8以外は足切りされて何の恩恵が受けられないというのはおかしな話です。

@ 次回

すいません、今日は力尽きました。

  • Multilingual Processing Environment
  • CodeSet Indepent
  • UTF-8によるチート行為の実例
  • read.cの書き換え

は次回に持ち越しということで。

まぁそもそもちゃんと何書くか決めないで行き当たりばったりのお筆先とか自動書記だからね(ぉ

2010/1/24(Sun)

週末

@

散財してきましあ、 SONY SAL70300G買ったよわーい。
新品同様中古品保証書残あり、決算処分大安売りで定価の半分だたので
IYHしてしまいましたとさ、もやし食って生き残るしかねー。

@

先週はちょっとlibmとかlibpthreadとかで遊んでたので、libeditの記事は白いワニのままです。
今週は本気出す…はず。

2010/1/26(Tue)

今日

@

obsdfbsdi18nだと、 7年前もあったな、んなの。

そいやFreeBSDのGSoC bsdiconvとやらってどうなったんだっけ?
なんかlocale dbも今のCSI捨ててUTF-8にするとかいう記事も読んだ気がするけど。