玩式草子─ソフトウェアとたわむれる日々

第18回「温故知新」

この連載でも紹介してきたように、現在、手元ではPlamo Linuxの64ビット化作業を進めています。hot plugやネットワーク回りなど、調整しなければならない部分は多数残っているものの、年末年始の集中作業の結果、とりあえずこの原稿を64ビット版Emacs上で書ける程度には環境が整いました。

先に紹介したように、Core i7などx86-64機能に対応したCPUでは、32ビット用のバイナリを互換モードでそのまま動かすことができるので、32ビット用と64ビット用のバイナリが混在した環境で運用することも可能です。しかし、ディストリビューションの作成といった立場からすると、64ビット版は全て64ビット用のパッケージで揃えなければ格好がつかないので、一部の設定ファイルやスクリプト類を除き、既存のパッケージを全てビルドし直しています。

全パッケージの再ビルドというのは面倒な作業ではあるものの、ライブラリへの依存関係なども全て作り直すことになるので、メンテナンスレベルの更新では手を出しにくい、基盤的なソフトウェアのバージョンアップなどを行うことができます。

Plamoの場合、人手不足を口実に壊れてないものは直さないという立場でやっているので、セキュリティフィックス等の重要な更新が無い限り、⁠枯れた」ソフトウェアのバージョンアップは滞りがちです。今回の全面的な再ビルドは、そのようなソフトウェアの埃を払ういい機会ですが、実際にやってみたら使い慣れているはずのソフトウェアの新機能に戸惑うことがありました。今回はそのような事例を紹介してみましょう。

groffの更新

groffは、UNIX上で開発されたroffと呼ばれる組版ソフトをフリーソフトウェアとして再実装したソフトウェアです。

UNIX/Linux上で動く組版ソフトウェアとしてはDonald Knuth氏が開発したTeXが広く知られていますが、roffはTeXよりも古く、UNIXの開発元であるAT&Tベル研究所で開発された由緒あるソフトウェアです。roffはTeX同様のマークアップ方式の組版ソフトウェアで、1つのソースコードから画面表示用や印刷用などさまざまな形式の出力を得ることができます。

組版ソフトとは言うものの、pLaTeX等と比べると使い勝手はよくないので、最近ではわざわざroffを使って文書を書く人はいないでしょうが、UNIXのマニュアルページ(manページ)は伝統的にroffの書式で書くことになっており、現在のLinuxでもmanページはroff形式で書かれているので、表示するためにはGNU版のroffであるgroffコマンドが必要になります。

Plamo Linuxにもgroffのパッケージはありますが、manページの整形くらいにしか使わないので、Plamo-4.73まではgroff-1.17.2を使い続けていました。一方、groffの開発元であるGNUプロジェクトのサイトを見ると、groffの最新版は1.21になっています。そこでさっそく最新版をビルドして入れ替えてみましたが、日本語のmanページが表示できません。

$ man fd
/usr/bin/groff: can't find `DESC' file
/usr/bin/groff:fatal error: invalid device `nippon'

groffは組版ソフトという特徴上、文字コードの扱いが重要になるため、従来は1バイト文字しか扱えないgroffに日本語を通すようなパッチを当てて使っていたのですが、調べてみると、groff-1.19以降ではgroff自身がUTF-8形式の文字コードを受けつけ、自身で日本語も含めたマルチバイト文字を処理できるようになったため、従来の日本語用パッチは廃止されたようです。

ざっとイジった感じでは、出力先をUTF-8(-Tutf8)に指定すれば日本語も整形してくれるようですが、行の折り返しや1バイト文字とマルチバイト文字の組み合わせの間隔調整などがうまく行かずに、ずいぶん間延びした表示になってしまうようです。

$ cat /usr/share/man/ja_JP.eucJP/man1/fd.1 | groff -DeucJP -Tutf8 | nkf -e
<standard input>:40: warning [p 1, 0.5i]: cannot adjust line
<standard input>:44: warning [p 1, 1.0i]: cannot adjust line
...
<standard input>:673: warning [p 7, 0.3i]: cannot adjust line                                       
fd - ファイル・ディレクトリ管理ツール [ ] [ ] [ [ ]]                                              
[    ]   [   ]   は、   UNIX   汎用のテキスト端末用に考えられた、                                  
ファイルやディレクトリの管理ツールです。 PC/AT 互換機と  PC‐98x1                                  
専用に作られている同名ユーティリティのクローンを目指しています。                                   
実際、機能的には上位互換となっています。                        
....

これらの不具合は将来のバージョンでは解決していくとは思うものの、日本語のmanページがきちんと表示できなくなるとgroffを更新する意味がないので、今回の更新は日本語パッチが用意されている最終バージョンである1.18.1.1にとどめておくことにしました。

謎のESCコード

ところが、groff-1.18.1.1では日本語manページも表示できるものの、表示結果はESCコードが多数混じった見苦しいものになってしまいます図1⁠。

図1 ESCが混じった日本語manページ
図1 ESCが混じった日本語manページ

安定環境のマシンにログインして、同じmanページをgroff-1.17.2で見ると正しく表示されます。

おかしいなぁ……、と思って両者の違いを見るために、それぞれの出力結果をファイルに書き出してEmacsで読み込んでみると、groff-1.17.2図2の下側のターミナル)では強調文字が^Hを使って表現されている(たとえば「名^H^H名称^H^H称⁠⁠)のに対して、groff-1.18.1図2の上側のターミナル)では強調文字がESCシーケンスを使って表現されている(たとえば「^[[1m名称^[[0m⁠⁠)ようです。

図2 groff-1.17.2と1.18.1.1の出力の比較
図2 groff-1.17.2と1.18.1.1の出力の比較

強調や背景色などの文字飾りをESCシーケンスを使って実現するのはコンソールでは常套手段ですが、lessはそれらのESCシーケンスを文字飾りとは認識せず、ESCシーケンスをそのまま出力してしまうようです。一方、moreだとESCシーケンスを文字飾りとして表示するので、PAGER='more'とすればmanページは読めるようになりますが、スクロールバックや検索といったlessの便利な機能に欠けるmoreでは何かと不便です。

文字飾りの表示方法を古い形式にするような機能は無いのだろうか。。とgroff-1.18.1.1に付属のドキュメント類を眺めていたら、NEWSファイルに以下のような記載がありました。

VERSION 1.18
============

***************************************************************************
*                                                                         *
*  PLEASE READ THE CHANGES BELOW REGARDING GROTTY, GROFF'S TTY FRONTEND.  *
*                                                                         *
***************************************************************************

(VERSION 1.18
 GROFFのTTY用フロントエンドであるGROTTYについて以下の変更を読んでください)

Troff
-----

o Color support has been added to troff and pic (and to the device drivers
  grops, grodvi, grotty, and grohtml -- other preprocessors and drivers will follow)
 ...

(Troff: troffとpic(とデバイスドライバであるgrops、grodvi、grottyとgrohtml--他のプリプロセッサと
 ドライバも順次対応の予定)にカラーサポート機能が追加されました。)

  Outputting color can be disabled in troff and groff with the option -c
  (it is always disabled in compatibility mode).  See the section on grotty
  for the GROFF_NO_SGR environment variable also.

(カラー出力は troff と groff に -c オプションを与えることで抑制できます(互換モードでは常に抑制されます)。
 grottyのセクションのGROFF_NO_SGR環境変数についても合わせて見ること。)

なるほど、GROFF_NO_SGRという環境変数があるのか、と思ってこの環境変数をセットしてみたところ、画面がESCコードまみれになる状況は改善しましたが、強調部分には何か変な文字が表示されています図3⁠。

図3 GROFF_NO_SGR環境変数を設定すると強調部分が変
図3 GROFF_NO_SGR環境変数を設定すると強調部分が変

おかしいなぁ……、と思って、出力結果をファイルに落しEmacsで開いてみると、2バイト文字を強調する場合、本来は^Hを2度出して2バイト分戻らないといけない(名^H^H名称^H^H称)のに、1度しか出していない(名^H名称^H称)ことがわかりました。これだと文字コードが途中で壊れておかしな表示になってしまうのでしょう。

これは明らかにバグですが、さすがにこのためにgroffのデバッグをするのも大変なので、何か別の方法が無いかしらん……と、しばらくドキュメントやmanページを調べてみました。

発見! SGRモード

先にNEWSファイルに紹介されていたTTY端末用のGROTTYの日本語manページを読んでみたけど、特にSGRなる機能についての記述は見当りません……と、そこで気がついたのが、このページが日本語なことです! 先にビルドしたgroff-1.18.1.1には英語版のmanページしかなく、参照している日本語のmanページは別途翻訳プロジェクトから配布されているgroff-1.17用のままでした。

そこでexport LANG=Cとして英語版のgrottyのmanページを見ると、SGRについての解説がしっかり記述されていました。

By default, grotty emits SGR escape sequences (from ISO 6429, also
called ANSI  color  escapes)  to  change  text  attributes  (bold,
italic,  colors).   This makes it possible to have eight different
background and foreground colors; additionally,  bold  and  italic
attributes can be used at the same time (by using the BI font).

(デフォルトでは、grottyはSGRエスケープシーケンス(ISO6429、あるいはANSIカラーエスケープ
 由来)を使ってテキストの属性(太字、イタリック、カラー)を変更します。この機能を使えば、
 8つの異なる背景色と前景色を利用でき、合わせて太字やイタリックといった属性も
 (BIフォントを使うことで)同時に利用することができます)

For SGR support, it is necessary to use the -R option  of  less(1)
to disable the interpretation of grotty's old output format.  Con-
sequently, all programs which use less as the pager  program  have
to  pass  this option to it.  For man(1) in particular, either add
-R to the $PAGER environment variable, e.g.

       PAGER="/usr/bin/less -R"
       export PAGER

or use the -P option of man to set the pager  executable  and  its
options,  or  modify  the  configuration  file of man in a similar
fashion.

(SGR機能を使うには、less(1)に-Rオプションを指定して、grottyの古い形式の出力を
 解釈させないようにする必要があります。結果として、lessをページャに使っている
 全てのプログラムがこのオプションを指定する必要があります。たとえば、man(1)の場合、
 $PAGER環境変数に下記のように-Rオプションを追加するか、manコマンドの-Pオプションで
 指定するページャにオプションを指定するか、あるいはmanの設定ファイルに同様の
 オプションを指定する必要があります。)

なるほど、これだ、ということで、さっそくGROFF_NO_SGR環境変数を無効にして、PAGER環境変数を'less -R'と指定してみると、日本語manページが正しく表示できるようになりました。

図4 日本語manページが正常に戻った
図4 日本語manページが正常に戻った

こんな機能があったのか、と思って、改めてlessの側のmanページを調べると、-r-Rの2つのオプションが説明してありました。

 -r or --raw-control-chars
        Causes "raw" control characters to be displayed.  The default is to display con-
        trol characters using the caret notation; for example, a control-A  (octal  001)
        is  displayed  as  "^A".   Warning: when the -r option is used, less cannot keep
        track of the actual appearance of the screen (since  this  depends  on  how  the
        screen responds to each type of control character).  Thus, various display prob-
        lems may result, such as long lines being split in the wrong place.

 (-r あるいは --raw-control-chars
      「生の」コントロールキャラクタをそのまま表示する。デフォルトでは、コントロールキャラクタは
      キャレット(^)モード(たとえば control-A(8進で001)は^A)で表示する。
      注意:-r オプションを使った場合、less は画面の実際の表示に追従することができなくなる
      (各コントロールキャラクタにスクリーンがどう反応するかに依存するため)。そのため、いくつかの
      ディスプレイでは、長い行が正しく分割されないなどの問題が生じる恐れがある。 )

 -R or --RAW-CONTROL-CHARS
        Like -r, but only ANSI "color"  escape  sequences  are  output  in  "raw"  form.
        Unlike  -r,  the  screen appearance is maintained correctly in most cases.  ANSI
        "color" escape sequences are sequences of the form:
        
             ESC [ ... m
         
        where the "..." is zero or more color specification characters For  the  purpose
        of  keeping  track of screen appearance, ANSI color escape sequences are assumed
        to not move the cursor.  You can make less think that characters other than  "m"
        can  end ANSI color escape sequences by setting the environment variable LESSAN-
        SIENDCHARS to the list of characters which can end a color escape sequence.  And
        you  can make less think that characters other than the standard ones may appear
        between the ESC and the m by setting the environment  variable  LESSANSIMIDCHARS
        to the list of characters which can appear.

 (-R あるいは --RAW-CONTROL-CHARS
        -r に似ているが、ANSIの「カラー」エスケープシーケンスのみを「生の」状態で表示する。
        -r とは異なり、ほとんどの場合で画面表示は正しく保たれるだろう。ANSIの「カラー」エスケープ
        シーケンスは以下のような形式を取る

        ここで "..." には0か複数の色指定用の文字が入る。画面の状況を保持するため、ANSIカラー
        エスケープシーケンスはカーソルを動かさないと想定している。環境変数LESSANSIENDCHARSに
        指定することで、ANSIのカラーエスケープシーケンスの終端を示すのにm以外の文字を使うことも
        可能である。また、出現するであろうキャラクタのリストをLESSANSIMIDCHARS環境変数に登録することで、
        ESCとmの間に標準的な文字以外の文字を含めることも可能である。)

なるほど、lessの側でもエスケープシーケンスは理解していて、むしろそれらが画面表示に悪影響を及ぼすことを防ぐために^[に変換してから表示するようになっていたようです。そして-Rはその変換を抑制するオプションでした。

個人的には、groffはmanページ整形用の「枯れた」ソフトと考えていましたが、実際に調べてみると、現在でもさまざまな新機能が追加され、定期的に新しいバージョンがリリースされているようです。また、lessも昔からずいぶんお世話になっているソフトなものの、-Rや-rというオプションは使ったことがなかったので、あらためてlessのソースコードに付属のNEWSファイルを調べたところ、-Rオプションは1999年にリリースされたless-346で採用された、かなり以前からある機能だったようです。

昔から使っているソフトウェアは、決まった使い方しかしていないことが多いのですが、64ビット化のために棚下しして埃を払ってみると「へぇ、こんなこともできたんだ」と気づくことがあって、まさに「古きを温(たず)ねて、新しきを知る」といった心境です。

おすすめ記事

記事・ニュース一覧