I know I believe in nothing but it is my sweet nothing.:2011年09月分

2011/09/04(Sun)

[NetBSD] nvi-1.81

2chのnviスレみたら(ひまだからな)なにやらuuencodeされた patchが貼られてたので
中見てみたら、おいら 直した件のうち、wW/eE/bBのワード単位移動が腐ってる件のpatchだった。
NetBSDのsrc/dist/nvi以下みるといろいろ直ってますお、特に正規表現周りとか。

まーなんにせよ以下のようなUnicodeのことしか考えてない修正はCodeset Independentなnvi-1.81では
とうていacceptできない *1ので以下略

#define isCJKideograph(ch)      (0x3400 <= (UCHAR_T)(ch) && (ch) < (UCHAR_T)0xa000)
#define inword(ch)              ((UCHAR_T)ch <= 255 && (isalnum(ch) || (ch) == '_') || isCJKideograph(ch))

正しくはこんなコードになりますな。

/* アンダースコアあるいは空白文字以外の印字可能かつ記号でない文字はワードの中と判定する */
#define inword(ch) ((ch) == L'_' || (iswgraph((wint_t)ch) && !iswpunct((wint_t)ch)))

まぁ実際はこれにmultibyte supportを含めるか否かのcompile optionもあるので
現物はis*とisw*、intとwint_tを切り替えできるようなマクロが入ってるのだけど。

元々のコードは

#define inword(ch) (isalnum((UCHAR_T)ch) || (ch) == '_')

という、CJKどころかUS-ASCII以外はワードじゃねーヒャッハーな実装。

*1:といつつ本家でもunicode hardwired wchar_tに依存したアレなコード混入してたけど

2011/09/06(Tue)

[NetBSD] nvi-1.81 の問題点(その1)

新たな作業をする前に、何回かに分けて今のnviのイケてない点をあげつらって
寝てる間に小人さんが直してくれないかなーと期待してみるテスト(ぉぃ

@Berkeley DBにつよくたくましく依存している

Berkeley DBとはKVS(キーバリュストア、イオン系列店) *1の一実装で、その中でもRECNOというDB形は

  • テキストファイルの行番号を key
  • 行データそのものを value

として扱うものです、nviはこのRECNOを利用して様々な処理を行っているのですが、このことがかーなーり困った問題を生んでいます
(まぁ感のいい人ならこの時点でピーンとくるでしょう)。

まずRECNOの内部実装を考えてみましょ、「1行」をここからここまでと切り出すためには

  • バイト列を走査して
  • 改行文字(列)を探す

という処理が必要です、RECNOではこの改行文字については

  • デフォルトは0xa(いわゆるLFつーか"\n")
  • 別の文字に変更するにはRECNOINFO構造体のbvalを変更する

ちゅーインタフェースを持っています、以下ヘッダファイル。

/* Structure used to pass parameters to the record routines. */
typedef struct {
	...
	uint8_t	bval;	/* delimiting byte (variable-length records) */
	...
} RECNOINFO;  

改行文字ってーのはこんなチラシの裏好き好んで読んでる人は当然ご存知とは思いますが
dos2unix/unix2dosなんてコマンドがあることからお判りの通り、まず環境によって異なります。

  • 0xa (いわゆるLFとか"\n")のみ … Unixなど
  • 0xd + 0xa(いわゆるCRLFとか"\r\n") … DOSなど

んで上記の通りDOSなんかでは改行は2byteなのですけども、先刻ご覧の通りRECNOのインタフェースでは
残念至極なことにuint8_tとして定義されてるので、恐悦至極ながら1byte 以上のより大きい改行文字(列)はサポートできんのですな。
助けてーfopen("b")~といってもそれじゃ改行文字は変更できないし、そもそもRECNOの中の人はmmap(2)なので以下略

どうです、これはひどい問題でしょ?といってもこれだけだとWindozeプギャーm9(^д^)で片付けてしまいかねないのですが
よーく考えよう。もういっちょ重大な問題が裏に隠れています、それは何かとゆーと。

つまり

  • 改行文字はふつーは文字コードに依存しない
  • でも改行文字のバイト列で表現したものは当然ながら文字コードに依存する

なのですよな、例えばみんな大好きUTF-16/32なんかだと改行文字がLFであっても

  • UTF-16BE 0xa 0x0
  • UTF-16LE 0x0 0xa
  • UTF-32BE 0x0 0x0 0xa 0x0
  • UTF-32LE 0x0 0x0 0x0 0xa

と、2~4byteあるわけです、つまりRECNOに強く依存したnviはUTF-16/32のサポートなどがまず不可能ちゅーことを意味します。

この問題は現時点のBerkeley DBバージョン5でも 解決されていないですし、また将来のバージョンで対応されたとしても
ライセンスの縛りがきつくなったバージョン1.85以降をNetBSDにインポートすることは極めて困難です *2

まぁ解決法はRECNOを

  • テキストファイルの文字コードを明示的に指定可能とする
  • 改行文字でなく改行文字列を指定可能とする

とかなんですが、破壊的インタフェース変更になるゆえ Berkeley DB を fork するってー話になるわけで
非常にめんどくさいし、本家がOracleちゅーことを考えると最悪のケースであの最強法務部門と以下略。
まーぶっちゃけBerkeley DBなんぞ捨てて、もっと軽量なRECNO作るんでしょうな(お待ちしております)。

世間一般の人だとインタフェースを破壊せずBOM使って対象がUTF-16/32なら暗黙的に内部でUTF-8に変換するんでしょうが
世の中のstateful encodingにおいてはエスケープシーケンスをともなった改行コードは別の意味を持つ場合もあるので
そういう実装にも対応することを考えちゃうようなパラノイアの私にはちょっとそんな実装恥ずかしくて無理です(ぉ

あと別件ですがRECNOがらみなので。
nvi-1.81はメッセージカタログにまでRECNO使ってるせいで、時々余計なエラーメッセージでたりするし
文言に plural とか u?intN_t format string なんかが必要になった時のことを考えると、これもさっさとgettext化すべきですな。
餅は餅屋。

*1:最近はKVSちゅーと分散KVSを指すことがおおいっす、memcachedとかredisとかいろいろありまんな。
複合キーのどー考えてもRDBMSを採用した方が楽な事案にRedisを採用してしまい、複合キーをTSV形式にして無理矢理単独キー化して回避したら
実はRedisはプロトコル上isblank()な文字を問答無用でkey/valueの境界と扱うため、set/get時にエラーは起きないがデータは破壊という事象が発生し
この対策にURIEncodeする知恵もなくShift_JISの2byte目は1byte目と被るという知識も無くただ闇雲にデリミタを試行錯誤して
簡単なテストだけして本番リリースたらもっと盛大に壊れたデータが数TBほど完成し、この時点で納期ギリギリで心が折れた実装者が失踪し
かわりにお前が直せとActivePerlとメモ帳ひとつで監禁されたりすると、半年チラシの裏が更新されなくなったりするわけです。

*2:そもそもnvi-1.81ではdb3以外はサポートしてなかったのをわざわざdb185でも動くよう改造しているくらいですしおすし。

2011/09/10(Sat)

[FreeBSD][NetBSD][nvi] GSoC Multibyte Encoding Support in Nvi

前回のネタ書いた後知ったんだけど、FreeBSDのGSoCで似たようなことやってる人いるみたい、 ここ
いやー後は若い人に任せて、俺は最近仕事のストレスでIYHした数々のオモチャで遊んでていい?

と思ったんだけど、 これ見ると結局nvi-1.79に1.81系の修正を持ち込んでるだけな希ガス。
EXFってのは先日問題だーといったBerkeley DBをwrapした感じのファイル管理情報用構造体ですな。
んで編集中のラインバッファがc_lp(実際にはEXFでなく画面描画用のSCR構造体にある)であり
これをwchar_t化したぜっ!てことみたいですが、これnvi-1.81ですでにやってるしね。
API名もINT2FILE/FILE2INTと名前も同じなので、学生さんが自分で考えてやったことじゃないっぽい。

んで私が指摘したよなCharacter Encoding SchemeレイヤでUS-ASCIIと互換性のない文字コード(UTF-16/32とか)への対策を
DB1/iconv層に手を入れることで解決したぜ!ということが書いてあるんですが、どの辺で対策したのかよーわからんです。

common/encoding.cのlook_utf16()とか関係してるっぽいけど、githubのアレなインタフェースで脳挫傷したので以下略。
まぁDBに入ってるファイル情報をfileencoding使わずにlocale encodingにしてるのかなーとか仮説。

ただそれやると情報落ち発生するので個人的にはアレ、現時点でのnvi-1.81はfileencodingとinputencodingがあって
これを一致させれば情報落ちは発生しない(ただしnvi-m17nのようにdisplayencodingがなくwcurses/locale依存でそのは化ける)
わけでして(まぁlocaleとは異なるinputencodingってinputmethodが困るような気もするけど)。

それとNetBSD-currentでnvi-1.81はすでに本家にフィードバックせずに徹底的に手がはいってて、
差分はもはやpkgsrcへ反映するのも心が折れるほどの、forkしたといって過言でない量が溜まっておるのですが
こっちは全く中見てないようですな、まぁFはCSI wchar_tなんだけども、GB18030なんかでwchar_tがnegativeにならんよう
localesrcを無理矢理いじったりしてるから問題が出にくいからもあるんだろけど。

あとgtagsサポートなんかのnvi独自機能をばっさり切るぜーゆうとるようですが、それならnviやめて
Caldera(今のSCO)が4-clause BSDLで放流した美流上位オリジナルの traditional-viをベースに
i18nやり直した方がいいんじゃねーのと思うアテクシ、nviもしょせんニセです。
いわゆる商用UNIX(Solarisとか)はこれを自分たちで国際化してつかってますな。

それと文字コード自動判定にfile(1)使うって、Fのfile(1)ってそんな機能まであるのかいな。
Nだと8bitがたってるファイルならeucJPだろーがWindows31-Jだろーが

$ echo あいうえお >hoge.txt
$ file hoge.txt
hoge.txt: ISO-8859 text

と、ISO-8859-*という答えが返ってくるはずなんですが…

まぁ結論からゆーとFreeBSDのことなのでどうでもよろしい(ぉ