Linuxの端末サブシステムの行指向編集レイヤーを文字幅を考慮するように改造してみた
何を改造したって?
ライン・ディシプリン1と呼ばれる、通常 xterm のような端末エミュレー ターと、コマンドプロセスの中間にあって、ユーザーの文字入力をバッファリ ングし、打ち間違いを BackSpace (あるいは Delete)キーで削除することを可 能にしている部分です。
端末のフラグ
端末には行編集やキーボードによるシグナル生成(^C など)の挙動を変更する ための様々なフラグが用意されています。これは stty コマンドで確認、変更 することができます。
例えば、文字入力は常にバッファリングされているわけではなくて、Emacs や bash などの高度な編集機能を有するプログラムは、端末を raw モードなどに 設定することで一文字ごとに入力を受け取っています。
iutf8フラグ
Linux では、このようなフラグの中に iutf8 というものがあります。これは 入力をマルチバイトエンコーディングである UTF-8 と仮定して、行指向編集 を助けるものです。
このフラグが設定されていないと例えばマルチバイト文字の「à」は C3
A0
という2バイトですので、二度 BackSpace キーを押さなければ消すことが
できず、またその時、端末へは2文字消すように制御文字の並びが送られます
から、カーソルは2マス戻ってしまいます。画面上の表示と実際のデータに食
い違いが埋まれてしまうわけです。
iutf8 フラグは、これを一度の BackSpace キーで削除することができるよう にするものです。
Linux は UTF8 を解する。すばらしい! でも…
Linux は UTF8 の1文字を表わすバイト列を認識すると、これを消去したとき は一律に「1マス後退」の命令を端末に送ります。
例に上げた à のようなラテン文字の場合は、グリフが端末上で1マスを占める のでこれで良いのですが、東アジアの人々はいわゆる全角文字も使っており、 これらは2マスを占めます。これを消す為にはカーソルの後退は一度では足り ないのです。
従って、直前に打った全角文字を削除すると、データの上では1文字消えても 表示上はその文字の半ばまでしかカーソルが戻りません。以下の動画は4文字 削除しても、画面上では2文字しか消えていない様子です。
文字幅を知る必要がある
このように、Linux は UTF8 文字は認識できるのですが、これの幅を常に 1 マスだと考えています。どうにかして彼にこの世に全角文字のあることを教え てやらなくてはなりません!
Unicode では個々の文字に、全角か半角かという属性が定義されており、これ を参照すれば文字幅を判定することができます。(実は話はそう簡単ではなく て、全角とも半角ともつかない文字があってややこしいのですが…2)
幸運にも、すでに Markus Kuhn 氏による wcwidth.c というコード が公開されており、これを使えば簡単です。(このコードは、ご存知 w3m テキ ストウェブブラウザでも使われています)
Linux カーネルに組込む
Linux ソースツリーの drivers/tty/n_tty.c がライン・ディシプリンを実装 していますから、これを変更します。
同じディレクトリに wcwidth.c を置いてやって、#include "wcwidth.c"
と
しました。ちょことちょこと両者を貼り合わせるコードを追加してやって、で
きあがりです。
BackSpace 一発で全角文字が削除できるようになりました。