isXXXXX
数日前に降った雨で出来た、水たまりに、玉虫とおぼしき虫が、ぽとりと落ちた。 足が何本もあって、もつれて落ちたのか、それとも暑いので飛び込みたかったのかは 定かではない。
水溜まりの中を気持ちよさそうに、泳いでいる。この虫って、泳げたっけかなあ? 初めてみたよ、泳ぐ所を。暫くすると疲れたのか、足と言うか手と言うか不明な のの動きを止めて、沈んでいった。また、暫くすると、思い出したように泳ぎ始める。 見ていて、飽きない。
数時間後に行ってみたら、水溜まりの中をうろうろして、出たがっているようだった。 だけど、同じ所をぐるぐる回っているだけで、さっぱり上陸口を見つけられないでいる。 そのうち、エネルギーが無くなって、水死してしまうのだろうか?
出口はすぐそこなのに、なかなか辿り付けない虫君を見て、ふと、私達も、同じなの かと、思ってしまったよ。
isXXXXX って、どうなってるの?
素朴な疑問です。だって、gaucheつながりで、ふと思ったのです。
まず、簡単なCを書いて、gdbで追いかけてみます。
[sakae@nil ~/tmp]$ cat t.c #include <stdio.h> #include <ctype.h> main(){ ispunct('%'); } [sakae@nil ~/tmp]$ cc -g t.c
一応、FreeBSD 7.2 で、やります。場合によっては、深い所まで潜って行く必要が あるかも知れないからです。Linuxだと、ソースを集めてくるのがきっと大変でしょうから。。
[sakae@nil ~/tmp]$ gdb a.out GNU gdb 6.1.1 [FreeBSD] (gdb) b main Breakpoint 1 at 0x8048430: file t.c, line 4. (gdb) run Starting program: /usr/home/sakae/tmp/a.out Breakpoint 1, main () at t.c:4 4 main(){ (gdb) s main () at t.c:5 5 ispunct('%'); (gdb) s __sbistype (_c=37, _f=8192) at _ctype.h:123 123 return (!!__sbmaskrune(_c, _f)); (gdb) p _c $1 = 37 (gdb) p _f $2 = 8192
私は、ispunctを呼んだ積りなのに、sbmaskrune と言う、不思議な関数に飛び込んで しまいました。細かい事は気にせずに、もう少し先に行ってみます。
(gdb) s __sbmaskrune (_c=37, _f=8192) at _ctype.h:110 110 return (_c < 0 || _c >= __mb_sb_limit) ? 0 : (gdb) l 105 } 106 107 static __inline int 108 __sbmaskrune(__ct_rune_t _c, unsigned long _f) 109 { 110 return (_c < 0 || _c >= __mb_sb_limit) ? 0 : 111 _CurrentRuneLocale->__runetype[_c] & _f; 112 } (gdb) p __mb_sb_limit $3 = 256
範囲を調べて、評価する式を決めてますね。次は、111行目が実行されるはず。 どんな値が返るか、先周りして調べてみます。
(gdb) p _CurrentRuneLocale->__runetype[_c] & _f $4 = 8192
Lisp風に言うと、trueですか。この式は、テーブルを引いているんですね。テーブルが どうなっているか、調べてみます。
(gdb) set print pretty on (gdb) p/x _CurrentRuneLocale->__runetype $5 = {0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x24200, 0x4200, 0x4200, 0x4200, 0x4200, 0x200 <repeats 18 times>, 0x64000, 0x42800 <repeats 15 times>, 0x50c00, 0x50c01, 0x50c02, 0x50c03, 0x50c04, 0x50c05, 0x50c06, 0x50c07, 0x50c08, 0x50c09, 0x42800, 0x42800, 0x42800, 0x42800, 0x42800, 0x42800, 0x42800, 0x5890a, 0x5890b, 0x5890c, 0x5890d, 0x5890e, 0x5890f, 0x48900 <repeats 20 times>, 0x42800, 0x42800, 0x42800, 0x42800, 0x42800, 0x42800, 0x5190a, 0x5190b, 0x5190c, 0x5190d, 0x5190e, 0x5190f, 0x41900 <repeats 20 times>, 0x42800, 0x42800, 0x42800, 0x42800, 0x200, 0x0 <repeats 128 times>} (gdb) finish Run till exit from #0 __sbmaskrune (_c=37, _f=8192) at _ctype.h:110 0x08048478 in __sbistype (_c=37, _f=8192) at _ctype.h:123 123 return (!!__sbmaskrune(_c, _f)); Value returned is $8 = 8192
なるほどね。それじゃ、意地悪く、判定したい文字として、'z'を指定してみると
(gdb) finish Run till exit from #0 __sbmaskrune (_c=122, _f=8192) at _ctype.h:110 0x08048478 in __sbistype (_c=122, _f=8192) at _ctype.h:123 123 return (!!__sbmaskrune(_c, _f)); Value returned is $1 = 0
ちゃんと、nilが返ってきてる。
ctype.h
gdbで実行してたルーチンは、ctype.hの中であった。ctype.hを直接開いて見ても いいんだけど、LoLを読んでいたら、macro-expand なんてのが、出てきていたので、 ここは、Lisp風に、t.c を、macro-expand してみる。
[sakae@nil ~/tmp]$ cc -E t.c >expand.c [sakae@nil ~/tmp]$ lv expand.c ..... static __inline int __sbmaskrune(__ct_rune_t _c, unsigned long _f) { return (_c < 0 || _c >= __mb_sb_limit) ? 0 : _CurrentRuneLocale->__runetype[_c] & _f; } static __inline int __sbistype(__ct_rune_t _c, unsigned long _f) { return (!!__sbmaskrune(_c, _f)); } ..... main(){ __sbistype(('z'), 0x00002000L); }
gdbで追っていたのは、この部分か。
ところで、runeって何物?
何だろう? 困った時のmanですね。恐ろしい世界に足を突っ込みそうになったよ。
..... 関連ファイル $PATH_LOCALE/locale/LC_CTYPE /usr/share/locale/locale/LC_CTYPE ロケール locale のバイナリ LC_CTYPE フ ァイル 関連項目 mbrune(3), setlocale(3), euc(4), utf2(4) 注 ANSI C の wchar_t 型は、 rune_t と同じです。美しさに劣る ANSI C プリミテ ィブをシステムの土台としない故意の選択であることを強調するために rune_t が選ばれました。 歴史 この関数は、 4.4BSD で初めて登場しました。 setrunelocale() 関数とその他の非 ANSI ルーン関数は、 ANSI マルチバイトと ワイドキャラクタサポートのより確実な代替案としての Bell Labs の Plan 9 に ヒントを得ました。 すべての ANSI マルチバイトとワイドキャラクタのサポート関数は、ルーン関数 を使用して作成されています。
ほぇ、FreeBSDでは、runeって言うくくりで、文字を扱っているんだ。勝手知ったる eucを紐解いてみると、isXXXXXとの繋がりが見えてきました。
EUC(4) FreeBSD カーネルインタフェースマニュアル EUC(4) 名称 euc - EUC エンコーディング 書式 ENCODING "EUC" VARIABLE len1 mask1 len2 mask2 len3 mask3 len4 mask4 mask 説明 EUC エンコーディングは、 UNIX ベースのシステムとの互換性を保つために提供 されています。 LC_TYPE ソースファイルのフォーマットに関する完全な説明を求 めるなら、 mklocale(1) を参照してください。 ..... /* * コードセット 1 */ ALPHA 'A' - 'Z' 'a' - 'z' CONTROL 0x00 - 0x1f 0x7f DIGIT '0' - '9' GRAPH 0x21 - 0x7e LOWER 'a' - 'z' PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e SPACE 0x09 - 0x0d 0x20 UPPER 'A' - 'Z' XDIGIT 'a' - 'f' 'A' - 'F' BLANK ' ' '' PRINT 0x20 - 0x7e ..... /* * コードセット 2 */ SPACE 0xa1a1 PHONOGRAM 0xa1bc SPECIAL 0xa1a2 - 0xa1fe PUNCT 0xa1a2 - 0xa1f8 /* ここに書くことはたくさんありすぎ ます */ .....
なにやら、見慣れたXXXXXが、出てきたぞ。
euc があるなら、utf8 も、あるはず。普通に、man utf8 すると、perlに行く手を 阻まれるので
man 5 utf8 UTF8(5) FreeBSD File Formats Manual UTF8(5) NAME utf8 -- UTF-8, a transformation format of ISO 10646 SYNOPSIS ENCODING "UTF-8" DESCRIPTION The UTF-8 encoding represents UCS-4 characters as a sequence of octets, using between 1 and 6 for each character. It is backwards compatible with ASCII, so 0x00-0x7f refer to the ASCII character set. The multibyte encoding of non-ASCII characters consist entirely of bytes whose high order bit is set. The actual encoding is represented by the following table: .....
ついでなので、/usr/src/share/mklocale/ にある、ja_JP.eucJP.src を、見ると LC_CTYPEの全貌が見えるよ。 このdirには、UTF-8用もあって、こちらは、Unicode 3.2 版となっていた。
ちょっと覗いてみると
/* * U+3040 - U+309F : Hiragana */ GRAPH 0x3041 - 0x3096 0x3099 - 0x309f PUNCT 0x309b 0x309c PRINT 0x3041 - 0x3096 0x3099 - 0x309f PHONOGRAM 0x3041 - 0x3096 0x309f SWIDTH0 0x3099 - 0x309a SWIDTH2 0x3041 - 0x3096 0x309b - 0x309f /* * U+30A0 - U+30FF : Katakana */ GRAPH 0x30a0 - 0x30ff PUNCT 0x30a0 0x30fb PRINT 0x30a0 - 0x30ff PHONOGRAM 0x30a1 - 0x30fa 0x30ff SWIDTH2 0x30a0 - 0x30ff
こんな定義が見つかったよ。
そろそろHaskellの海へ
お腹いっぱいモードも解消されたようなので、また、飛び込んでみますかね。 今回は、途中で溺れてしまわないよう、 CheatSheetを、浮き輪がわりに用意しました。
Cheatって良く聞くけど、意味を知らなかったので調べてみたら、カンニングとか 騙すとか、余り良いイメージ無いんですね。カンニングペーパーにしては、たっぷりと 13ページもありました。