w3mの開発について

1999/2/18
1999/3/8改訂
伊藤 彰則
aito@fw.ipsj.or.jp

はじめに

w3mは,WWWに対応したページャ/ブラウザで,テキストベースで動く. w3mに最も近いアプリケーションとして,有名なテキストベースブラウザ Lynxがある.しかし,w3mには Lynxにないいくつかの特徴がある.例えば, などだ.もちろん,Lynx は優れたブラウザで,w3mにない多くの機能を 持っている.Lynxにあってw3mにない機能は,例えば などなど.Lynx には豊富なドキュメントもあり,一方w3mにはほとんどまともな ドキュメントがない.ドキュメントは今後の課題だ.

というわけで,w3mは既存のブラウザ(Netscapeはもちろん,Lynxも)を代替 するものではない.それではw3mは何のためにあるのか? それは,日常的に「ちょっと」 web を使うためだ.専用線で接続された環境で, 「ちょっとwebを見に行きたい」とき,Netscapeを立ちあげるのはイライラする. Lynxも立ちあがるのにちょっと間がある. その点,w3mは一瞬で立ちあがり,マシンにほとんど負担をかけない. そこで情報を見て,もっと詳細に見たいときに,はじめて他のブラウザを使う のだ.もっとも私の場合,ほとんどはw3mだけで十分なのだが.

w3mの誕生

w3m の前身は,fm というページャ(moreやlessの親戚)だった.fmが書かれたのは1991年以前 (記録していなかったので正確な日付はわからない)で,当時まだWWWは 一般的ではなかった(存在しなかったかも).その当時「ブラウザ」といえば,lessなどの ファイルを見るツールのことを指していた.

fm は,当時私が書いていた 研究用のプログラムをデバッグするために書いたものだ.プログラムの状態 をトレースするため,プログラムの内部状態を延々とファイルにダンプし, それを見ながらデバッグをしていた.ある時点での内部状態を1行にプリント していたため,そのファイルは1行が数百文字あった.それをmoreやlessで 見ると,行が折り返されるため,何が何だかわからなくなってしまうのだった. そこで私は,行を折り返さないページャであるfmを書いた.物理的な1行は 画面の上でも1行で,画面からはみ出した部分を見るには,画面全体をずらす という設計にした.当時私は80x24の画面を使っていたので,fm はデバッグ にとても役立った.

そのうち,私もWWWの存在を知って使いはじめた.当時使っていたブラウザは, XMosaic と Chimera だった.特に Chimera は軽いので愛用していた. 興味があったので HTML と HTTP の勉強をしてみたが,案外簡単なので, これなら自分でもブラウザが書けるのではないかと思った.当時のHTTPは GOPHERプロトコルに毛が生えた程度で,非常に簡単なものだった.また, HTML は 2.0 で,行の折り返しと箇条書きがほとんど全てだった. そこで,fm にちょっと手を入れて,WWWブラウザを作ってみた.これがw3mだった. ちなみに,w3m は WWW-wo-Miru (日本語だ)の略で,fm (File-wo-Miru)に 倣った.最初に w3m を書いたのは,1995年初頭だったと思う.

w3mの没落と再生

それ以来,ずっと私は w3m を「ページャ」として使っていた.ファイルや 電子メール,マニュアルなどを読むときに,lessの代わりにしていたのだ. w3mでwebを見ることも時々あったが,その後 w3m で正常に見られないページが 多くなった(その多くはtableを使っていた)こともあって,webブラウザと してはほとんど使わなくなっていた.一度 table のレンダリングを検討 したことがあったが,難しいので放ってあった.

もう一度 w3m に手を入れる気になったのは,1998年のことだ.動機は2つあった. その当時,私は客員研究員としてボストン大学に滞在しており,多少時間に余裕があった ことが一つ.もう一つは,研究日誌を HTML で書いていて,結果をどうしても表に したくなったためだ.それまでは表を <pre>..</pre>で書いていたのだが, plain textで表を作るのがわずらわしくて仕方なかった.とうとう我慢できなくなって <table>タグを使ったが,そうすると今度は Netscape を使わないと日誌が 見られなくなってしまった.そこで,w3m で table の レンダリングができるようにしようと試みた.

私としては,それほど複雑でない表を見ることができれば十分だった.ところが, 半端にtableに対応した結果,画面のレイアウトにtableを使っているページの 表示がぐちゃぐちゃになってしまった.結局,「表が見られて」「その他のページ もそこそこに見られる」ようにするためには,tableの表示が完璧に近くなければ ならないのだった.茨の道だ.

結局,結構時間がかかったが,何とか 実用になるものができたと思う.table の実装に気をよくして,次に form を実装 した.これで,w3mはほぼ実用になるブラウザとして生まれ変わったのだ.

w3mでのtableのレンダリングアルゴリズム

HTMLのtableのレンダリングは結構難しい.LaTeX の tabular のように, 「表の各列の幅を指定するか,さもなければ必要な最大の幅を取る」 というのなら話は簡単なのだが,HTMLのtableは「画面に適当に収まるように」 列の幅を設定して,表の内容を適当に折りかえさなければならない. 幅の決定をいいかげんにすると,非常に表が見づらくなってしまう. また,tableは入れ子にできるので,それが話を一層ややこしくしている. そこで,w3mでは次のようなアルゴリズムで幅を決定している.
  1. まず,各列の内容の最大幅と最小幅を求める.最大幅というのは, もしいくらでも広い幅が取れたとしたら,最大何桁になるかというもの だ.一般的には,<BR>や<P>で区切られた段落の長さになる. 最小幅は,それより列の幅が狭いと内容が詰められないという限界の幅 である.表の内容が日本語だけの場合には最小幅は常に2であり, internationalization という単語が含まれていれば最小幅は20 である.また,表の中に<pre>..</pre>があった場合, その一行の長さの最大値が最小幅になる.
  2. もし,WIDTH属性で列の幅が指定してあれば,列の幅をその値で固定 する.ただし,その幅が最小幅よりも小さければ,列の幅を最小幅で固定する.
  3. 列の最大幅(または固定幅)を合計して,画面の幅よりも広いかどうかを調べる. もし合計が画面に収まるなら,その値を各列の幅として使う.
  4. もし合計が画面に収まらなければ,次のようにして幅を決定する.
    1. 画面の幅から,幅が固定された列の幅の合計を引く.これを W とする.
    2. 幅が固定されていない列に対して,各列の最大幅の対数に比例して W を配分する.
    3. もし配分された幅が最小幅よりも小さければ,その列の幅を最小幅で固定し, 幅の配分をやり直す.
幅の配分を最大幅の対数に比例させているが,これでいいのかどうか検討を要する. ただし,最大幅そのものに比例させると悲惨なことになる.table を画面レイアウト に使っていた場合,ある列に長い文章があると,その列が画面の幅のほとんどを使って しまうからだ.対数じゃなくて n 乗根でもいいかもしれない.

上記のアルゴリズムでは,画面の幅が既知であることが前提になっている.ところが, これでは困る場合がある.どういう場合かというと,表が入れ子になっている場合だ. 外側の表の列幅がわからないと内側の表がレンダリングできないが,内側の表を レンダリングしてみないと外側の表の幅が決定できないという矛盾に陥る.WIDTH属性 が指定してあれば問題はないのだが,そうでない場合には,結局 「内側の表の幅は,外側の表の幅の0.8倍」で決め打ちしてしまうことにした. ほとんどの場合はこれで問題ないが,ある表の中に表を入れ子にして2つ並べると, 外側の表が必ず画面をはみだしてしまうようになった.もし厳密にこれを画面に収め ようとすると,一旦レンダリングして全体の幅を調べたあと,幅を設定しなおして もう一度レンダリングするという過程を収束するまで繰り返さなければならない. Netscapeは,多分これをやっているのだろう.

利用したライブラリ

w3m は, Boehm GC というライブラリを利用している.これは私が書いたものではないが, コンパイル時の便宜を考えて配布パッケージに含めている.

# Boehm GC は、w3m-0.4.2 以降のパッケージには含まれていません。

なお,libwww は使っていない.

Boehm GCは,Cから使えるガベージコレクタだ.table を実装したあたりにこれを 使いはじめたのだが,非常に快適だった.GCなしでは,w3mにtableやformを実装 する根性が私にあったかどうか疑わしい.Boehm GCの利用については,「 Boehm GCを使おう」という文章を書いたので,それも見ていただけると良い と思う.

beta-990304より前のバージョンでは, LIBFTPと いうライブラリを使っていた. libftp を使った理由は,FTPプロトコルが HTTP に比べて面倒だったためだ. しかし,ライセンスの問題がありそうだということなので,同等の関数(のサブセット) を自前で書いてしまった.

ちなみに,w3mはUNIXの正規表現ライブラリと curses ライブラリを使っていない. どちらも自前の関数群だ.これらを自前で用意した理由は,fmを書いた当時, 日本語の通るまともでフリーな正規表現とcursesのライブラリがなかったためだ. 現在ではどちらも存在するし,他のライブラリを使った方が速そうなのだが, 面倒なので現在までこの実装を引きずっている.

今後の予定

...ない.w3mは軽快さが売りなので,あまり機能を満載してしまうとw3m独自の 良さが失われるからだ.とはいっても,まだバグが多いので,それらのfixは していきたいと思っている.