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ページもありました。