文房清玩の最近のブログ記事

SyntaxHighlighter 3.0

|

プログラム・コードを Web ページに掲載する際,コードを pre タグでマークアップするのが一般的ではないだろうか。SyntaxHighlighter を使うと,C/C++ や Java などの予約語をカラー表示したり,行番号を付加したり,重要な行を強調したりできる。これを使用しているプログラマのブログを目にするのは稀ではない。私も使ってみることにした。

いま現在の SyntaxHighlighter 最新バージョンは 3.0.83。ダウンロード・ページから syntaxhighlighter_3.1.0.83.zip を取得して,ローカル計算機で解凍する。解凍してできたディレクトリ下にある compass, scripts, styles, src のディレクトリを Web ドキュメントルート下にコピーすれば,使い始めることができる。ここでは DocumentRoot/sh にインストールしたものとする。

使い方を簡単に説明すると以下のとおりである。SyntaxHighlighter - Installation に書かれているので,きちんとした仕様が知りたい方はそちらを参照のこと。

  1. プログラム・コードを掲載したい HTML(以下「対象ページ」)のヘッダ部に shCore.jsshCore.css を読み込むよう指定する。
  2. 出力様式のタイプがテーマ Theme として管理されており,好みのものを選択して,そのスタイルシートを対象ページに指定する。一覧は SyntaxHighlighter - Themes にある。とくにこだわりがなければ標準 Theme: shThemeDefault.css を指定しておけばよい。私は Eclipse Theme について表示色等カスタマイズしたものを shThemeSite.css として保存して使うことにした。
  3. 対象ページで掲載したいプログラム・コードの言語に応じて SyntaxHighlighter の JavaScript を選択し,それらを対象ページに指定する。JavaScript の一覧は SyntaxHighlighter - Bundled Brushes にある。

上記までで対象ページのヘッダ部は以下のようなものとなる。ここではシェルスクリプト,XML(HTML 含む),Java,C/C++,Perl,JavaScript を扱うものとした。これは SyntaxHighlighter の出力例でもある。

  <link type="text/css" rel="stylesheet" href="/sh/styles/shCore.css" />
  <link type="text/css" rel="stylesheet" href="/sh/styles/shThemeDefault.css" />
  <script type="text/javascript" src="/sh/scripts/shCore.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushBash.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushXml.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushJava.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushCpp.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushPerl.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushJScript.js"></script>

プログラム・コードは pre タグでマークアップする。class オプションに SyntaxHighlighter のコンフィギュレーションを指定する。扱う言語種別,行番号表示や開始番号,タブのサイズ,ハイライトすべき行番号,ツールバー表示有無,等である。SyntaxHighlighter - Configuration に指定可能なオプションの説明がある。以下は,Perl 言語コード,開始行番号 1,タブ・サイズ 4,ハイライト行なし,の指定である。title に題名を設定できる。

<pre class="brush: perl; first-line: 1; tab-size: 4; highlight: null;" 
     title="xlstocsv.pl">

行番号を出力したくない場合,gutter: false; を指定する。個別に行をハイライトしたいなら,highlight: [2, 4, 6]; のようにすればよい。プログラム・コードのマークアップが済んだら,最後に以下の JavaScript コードを書いておく。おまじないのようなものである。同一ページ内にいくつも SyntaxHighlighter 使用 pre タグを書いていても,最後に一度だけ発行すればよい。

<script type="text/javascript">SyntaxHighlighter.all()</script>

昨日作成した Excel 一括 CSV 出力の Perl コードは,以下のように表示される。

#!/usr/bin/perl -w
# -*- coding: utf-8; mode: cperl; -*-
# Excel to CSV
# $Id: xlstocsv.pl 12 2012-02-05 16:57:06Z isao $
use strict;
use utf8;
use Spreadsheet::ParseExcel;
use Spreadsheet::ParseExcel::FmtJapan;
binmode STDOUT, ":utf8"; binmode STDERR, ":utf8";
($#ARGV < 0) && die "Usage: $0 xls-file\n";
my $fnm = $ARGV[0]; # Excel file name
my $ct = 0;         # 出力行数
# Excel object
my $xls = Spreadsheet::ParseExcel->new();
my $fmt = Spreadsheet::ParseExcel::FmtJapan->new(); # 日本語あり
my $bko = $xls->parse($fnm, $fmt); # Excel Book object
my @wsa = $bko->worksheets();      # Worksheet object 配列
utf8::decode($fnm);
# ワークシート毎の処理
foreach my $ws (@wsa) {
    my $wsnm  = $ws->{"Name"};
    my $mxrow = $ws->{"MaxRow"};
    my $mxcol = $ws->{"MaxCol"};
    print STDERR "*** $0 $fnm - sheet: $wsnm start.\n";
    if ((defined $mxrow) && (defined $mxcol)) {
        # 行毎の処理
        for (my $row = 0; $row <= $mxrow; $row++) {
            my $line = "";
            # 列毎の処理
            for (my $col = 0; $col <= $mxcol; $col++) {
                # セル値をセット
                my $cell = $ws->get_cell($row, $col);
                $line .= $cell->value() if (defined $cell);
                $line .= ",";
            }
            print "$line\n";
            $ct++;
        }
    }
}
print STDERR "*** $0 from $fnm wrote $ct lines\n";

ただし,オリジナルの shBrushPerl.js では # 文字があるとそれ以降がすべてコメントとみなされてしまうので,$#ARGV など配列末添字番号変数の表示がおかしくなるので,上掲では少し訂正したものを用いた。53 行目の正規表現の先頭に [^\$] を追加し,以下のとおりに訂正した。

this.regexList = [
	{ regex: new RegExp('[^\$]#[^!].*$', 'gm'),	css: 'comments' },
	{ regex: new RegExp('^\\s*#!.*$', 'gm'),	css: 'preprocessor' },

ちょっと使ってみて制約があった。XML のタグのように < > で囲まれたテキストがあるとき,開始タグに対して終了タグがないと,終了タグが最後に強制的に付加されてしまう仕様のようである。空要素タグ < /> の場合も例外ではない。たとえば C/C++ の #include <stdio.h> の行をそのまま記述すると,終了タグを探しても当然ないので,ソースコード表示の最後に </stdio.h> なるゴミが出てしまうのである。私はまだ SyntaxHighlighter を使い込んでいないのでなんとも判断できないが,< > については,これまでどおり実体参照指定(&lt; &gt;)で記述した方がよさそうである。

misima 漢詩・詩語集拡充

|

misima 漢詩作成支援の詩語検索機能で参照する詩語データベースを大幅に拡充した。ここのところ,この漢詩プログラム改造に取り組んでいた延長で,詩語のデータベース拡充のための元ネタがないか探していたら,今月 2 日,ある方から個人的に集めた詩語集をいただいた。入手した Microsoft Excel 形式のファイルを CSV に変換して行数を確認すると,なんと 8 万行もある。長年の労苦が偲ばれる素晴らしい成果をいただいた次第である。これは早速データベースにして,提供してくれた方に使ってもらわないとバチが当たる。昨夜と今日のお休み一日かけて,これに取り組んだのである。

いくつものシートに分けられた Excel データを CSV データに変換するのに,「Excel 一括 CSV」を使わせてもらった。妻の Windows 7 で変換したのち,Mac OS X にコピーして,ここからいくつかプログラムを書いて既存のデータベースにマージした。主な作業手順は以下のとおり。

  • 文字コードを UTF-8 に変換(nkf 使用)。
  • CSV データをフォーマット編集(新規作成ツール)
  • 平仄付加(既存ツール)
  • 詩語を旧字体変換して増幅(既存ツール)
  • 新旧両字体で検索してもヒットするよう韻字カラム調整(新規作成ツール)
  • 重複排除(既存ツール)
  • SQLite3 import
  • FreeBSD サーバ Tomcat 環境への辞書・プログラムのデプロイ(配備・インストール)
  • 読み,意味,出典も詩語検索の結果に出力する処理(JavaScript)も追加した。旧字体変換を入れたので,韻字の検索で新旧字体のどちらを指定してもヒットするようデータ構造,クエリを変えた。例えば,韻字が「仏」・平仄「○-●」の検索で,「銅佛」が出て来るようにした。
     

    20120204-sigo.png

    これまでの詩語データベースは,ネットで漁った『唐詩選』の漢詩データ 470 首程度を Perl HTML::TreeBuilder パーサで処理して二字,三字の詩語に分解し,この結果得た約 4000 語程度しか蓄積してなかった。これがいまや二字 33601 語,三字 53657 語,計 87258 語にまで拡張された。まだまだ使い勝手としては疑問が残るけれども,詩語ブラウザとしてはそれなりの規模が備わったのではないかと思う。

    今回作業で使用した「Excel 一括 CSV」ツールでは,残念ながら JIS 第三・第四水準の漢字が文字化けしてしまった。データベースにはこれらは含めなかった。これらを反映するのが次なる課題である。

    misima 漢詩作成支援・平仄音韻分析・詩語検索ツールに,旧字体変換機能と漢字検索機能を追加した。ただし,このツールは友人向けの限定公開であり,ユーザ ID,パスワードを入力しないとアクセスできないようになっている。

    今回の追加機能は,先日,自分で漢詩を書く際に本ツールを使ってみて,足りないと思った機能である。そのときは手元の仮想端末から SQL を叩いて漢字データベースを検索して,欲しい平仄・韻をもつ漢字を探す,などしていた。せっかくだから,これらを Web 上で出来るようにした。旧字体変換はすでにある misima 旧字・旧仮名遣い変換サーブレットを呼び出すようにしただけである。漢字検索機能は,詩語検索とまったく同じ方式である(「DWR with Java: misima 漢詩詩語検索」を参照)。DWR(Dynamic Web Remoting)ライブラリをベースに,Java Beans クラス(KanjiBean.java)と,漢字 DB を検索してそのクラス配列にデータをストアするメソッド(KanjiTable.java)の二本の短い Java プログラム,非同期通信ライブラリ DWR とユーザインタフェースを制御する一本の JavaScript(kanji.js),検索用 HTML(kanjisearch.html)をごそごそと書いた。DWR のおかげで面倒な Java サーブレットのコーディング,非同期通信 Ajax JavaScript のブラウザ依存コーディングから解放され,たったこれだけの一日作業で機能追加が出来た。以下,追加機能の使い方を簡単にメモしておく。

    メインの画面(図 1.)に「漢字検索」と「旧字体」のボタンがある。
     

    20120128-kansi-1.jpg

    図 1. メイン画面

    分析対象漢詩テキストを入力し,「旧字体」のボタンをクリックすると,入力した内容が旧字体に置き換えられる。図 2. はその実行前後を示している。


    20120128-kansi-2.jpg

    図 2. 旧字体変換

    「漢字検索」右横の「開く」ボタンをクリックすると,漢字検索用の別ウィンドウがオープンする(図 3.)。
     

    20120128-kansi-3.jpg

    図 3. 漢字検索ウィンドウ

    ここで「漢字」,「韻字」,「韻目」で検索が可能である。ある漢字の平仄,韻目を知りたいとき,「漢字」のテキストエリアに文字を指定する。複数指定することができる。「韻字」のエリアに漢字を入力して検索すると,当該漢字と同じ韻目の漢字の一覧が得られる。押韻文字を考えるときに役立つはずである。「韻目」は,韻目を指定して該当する漢字の一覧を検索するためのものである。韻目は「上平聲一東」のような複雑なものでありかつシステム内部において特殊な形式で管理している。この形式でユーザが入力するのは困難なので,韻目テキストエリアにカーソルが位置づけられるとメニューがポップアップし,ここに一覧された平水韻 106 項目から求める韻目を選択することにより,検索条件が設定されるようになっている。図 4. に韻目検索のポップアップメニューを示す。
     

    20120128-kansi-4.jpg

    図 4. 韻目ポップアップメニュー

    「漢字」,「韻字」,「韻目」の条件は1回の検索ではどれかひとつしか指定できない。複雑な条件で検索するために「SQL」条件を設けてある。検索キー:漢字 ji; 平仄 hs; 字韻1 in1; 字韻2 in2; 音読 yo; 訓読 yk; 備考 bk を使って,SQL where 句のなかに書くクエリ条件を記述する。select * from KANJITBL where query condition; の SQL 文のうち query condition 部分のみをテキストエリアに記述する。最後の ;(クエリ終了を示すセミコロン)も書いてはならない。これはエキスパート向けであり,基本的には使う場面はないと思う。

    検索結果には,漢字,平仄,韻目,備考が出力される。備考欄には,音読み,訓などの付加情報があったりなかったりする。出力例を図 5. に示す。
     

    20120128-kansi-5.jpg

    図 5. 漢字検索結果例

    ここで,平仄欄の は平字, は仄字, は平仄両韻字(意味によって平仄が違う)を示している。韻目欄の,例えば「hs:07:陽」というのは下平聲七陽を示している。hk: 上平聲,hs: 下平聲,sj: 仄上聲,sk: 仄去聲,sn: 仄入聲であり,コロンで区切られた数字と漢字一文字で韻目を表している。

    これで,漢詩の平仄・音韻規則チェック,詩語の検索,漢字の平仄・韻目検索が揃った。これからは詩語データベースの充実を図り,漢詩作成の有益なツールになるようにしたいものである。
     

    20120128-kansi-6.jpg

    図 6. misima 漢詩作成支援・平仄音韻分析・詩語検索・漢字検索

    再生装置の組替

    |

    リビングに設置した再生装置のうちプリアンプが壊れた。アナログプレーヤ用のフォノアンプの右の音が出なくなってしまった。年末に大掃除をしなかったツケか。Technics 製 70A というプリアンプで,1970 年代に製造されたオンボロ。娘の部屋に設置した ONKYO Integra A-2001 という,これも 1980 年代後半の年代物のデジタル・プリメインアンプを代替機とすることにした。これは優れた MC フォノアンプも搭載している ONKYO のかつての高級機で,会社に入ったころに秋葉原の中古ショップで見つけて購入したんである。娘の言うには,iPod があれば,こんなシロモノは邪魔になるだけだからどこかにもって行って。これを機に掃除がてら再生装置の組替をした。

    プリメインをリビングに移設するとパワーアンプ YAMAHA 製 B-2x が余る。娘の部屋のアナログプレーヤ TRIO(現 JVCケンウッド)製 KP-F605 Mk-II,スピーカ YAMAHA 製 NS-10M も無意味になる。仕方なく,パワーアンプとスピーカは,書斎にもって行くことにした。アナログプレーヤは,壊れた 70A プリアンプともども,処分するしかなさそうである。

    昔のオーディオ・アンプはとにかく重い。B-2x も Integra A-2001 も 30kg くらいの重量がある。いまの私には,これを 1F リビングと 3F ロフト書斎との間で持ち運びするなんてとてもできない。ちょうど大学の冬休みで家にいた息子に,運搬をすべてやらせた。面倒極まりない接続作業だけは自分でやるしかなかった。

    アンプを ONKYO Integra A-2001 に替えて,リビングでアナログレコードも掛けられるようになった。YAMAHA NS-1200 という木目調の美しいスピーカとの組合せ。オーケストラがよく鳴ってくれるんである。屋根裏の書斎では,YAMAHA C-2x プリアンプ,同 B-6 パワーアンプ,同 NS-1 Classic スピーカ,Technics 製 SL-01 アナログプレーヤ,DENON 製 DCD-1650AR CD プレーヤというコンポーネントを使っている。これに B-2x パワーアンプと NS-10M スピーカを追加して,気分でパワーアンプとスピーカを切替えて音楽を楽しめるようになった。

    私は高校のころから懐の許す限りこだわりをもって再生装置を選んで来た。決してオーディオ・マニアというわけではないが(なんとなれば,そこまで金持ちではない),YAMAHA 製が好きであった。就職して少し金銭に余裕ができて,いまの装置を揃えることができた。結婚してからはもうこだわりがなくなってしまい,いまだに古い装置でもっぱら古楽と近現代音楽,時折クラシック,ジャズ,和洋ポップスを聴いている。壊れない限り新しいものを求めることはない。

    特に YAMAHA NS-10M は,私が貯めたお小遣いで高校二年のときに買った初めてのスピーカで,もう 32 年鳴らし続けていることになるけれども,ウーファにカビが張り付いたいまも,モニタスピーカらしい締まりのある音響を昔と変わりなく再生してくれる。私の私財で最も古く,大阪,札幌,蒲田,川崎と私の住まいすべてを渡り歩き,かつ現在もバリバリに鳴ってくれる。YAMAHA NS-1 Classic は艶やかで一枚上手だけれども,一方 NS-10M も C-2x,B-6 で鳴らすとパンチがあって飽きが来ない。もうここまで来ると,長年連れ添った思い入れ以外の何ものでもない。

    バッハのゴールドベルク変奏曲 BWV 988 を聴いた。ただし,ドミトリ・シトコヴェツキイ編曲による弦楽三重奏版。「グレン・グールドを偲んで」とある。ドミトリ・シトコヴェツキイのヴァイオリン,ジェラール・コセのヴィオラ,ミシャ・マイスキイのチェロによる極上のアンサンブル。アリア歌い出しの艶やかさ,懐かしさは弦楽室内楽ならでは。私はこの CD をオーディオチェック用に使っている。音のチェックに向いているというより,「あ,これこれ」のわが定番的一枚であるからに過ぎない。
     

     

    20120106-audio1.jpg

    ONKYO Integra A-2001 (上), Technics 70A

    20120106-audio2.jpg

    カビまくりの YAMAHA NS-10M スピーカ

    20120106-audio3.jpg

    YAMAHA C-2x プリアンプ (上), 同 B-2x パワーアンプ

    20120106-audio4.jpg

    屋根裏部屋にパワーアンプとスピーカを増設

    Apache IP denial configuration

    |

    Apache のアクセスログを時折チェックすると,様々なアタックがあることに気付く。公開 Web サーバを運用しているとこれはもうしようがない。GET //dbadmin/scripts/setup.php HTTP/1.1 などの要求(世に言う php アタック)が頻発していて,すべて 404 レスポンス(document not found)で弾かれている。404 で帰ってくれるならよいのだが,隠れたセキュリティホールを突かれてサーバが乗っ取られないとも限らない。面倒だが,これら前科のあるヤクザ・ホストの IP アドレスを収拾し,こいつらのネットワークから来る HTTP 要求はすべて拒否することにした。

    Apache には特定の IP アドレス,ネットワークからの要求を拒絶する機能が備わっている。あるドキュメントに対して deny from IP | CIDR を指定すればよい。ヤクザ・ホストの IP アドレスをログから抽出しここに指定しておけば,Apache は 403 レスポンス(forbidden)を返し,アクセスを拒絶してくれる。今回は IP アドレスそのものではなく,こいつらヤクザが属しているネットワーク全体からの要求を拒絶するように CIDR(ネットワークアドレス/マスクビット)を指定することにした。暴力団排除条例が施行されたことだし,「関係者」ともども皆お断りというわけだ。今日は,その設定変更レシピの備忘録を示しておく。ここで % は UNIX コマンドライン tcsh プロンプトを示している。

    1. 存在しないドキュメント参照記録の抽出
    2. まず 404 の履歴を Apache のアクセスログから抽出する。user, webserver は,それぞれ Web サーバへのログイン ID,ホスト名である。リモートシェルを使って Mac OS からオペレーションしている。
      % rsh -l user webserver grep -e ' 404 ' /var/log/httpd-access.log > 404list.txt
      
    3. アタックの特定
    4. 404list.txt をテキストエディタで開いて内容を確認し,明らかにアタックだと思われるアクセス(.cgi, .dll, .php の拡張子をもつ,いかがわしいプログラムを実行するもの)を特定し,それ以外の行を削除して格納する。

    5. IP アドレス・リストの作成
    6. 編集済のログデータ 404list.txt から IP アドレスだけを抽出・編集し,重複を排除し,対象リスト 404ip.txt を作成する。
      % cat 404list.txt | cut -f 1 -d ' ' | grep -e '[0-9]' | sort | uniq > 404ip.txt
      
      私の場合,上記 1〜3 のオペレーションは以下のワンライナーでよさそうであった(→印は,次の行と一続きで入力することを示す)。
      % rsh -l user webserver grep -e ' 404 ' /var/log/httpd-access.log  | \
      grep -v 'GET .*\.html ' | grep -e 'bbs\.html\+\|\/manager\|\.php\|\.dll\|\.asp\|\.pl\|→
      GET http\|\/user\/\|\/muieblackcat\|blackhats\|++++++*\|GET \/\/\|GET \/crossdomain\|→
      GET \/idle\/\|GET \/appConf' | \
      cut -f 1 -d ' ' | grep -e '[0-9]' | sort | uniq > 404ip.txt
      
    7. Whois 情報から Apache denial list の作成
    8. 404ip.txt 中の IP アドレスで whois を参照し,このアドレスが属するネットワーク CIDR や国コードを取得する。国コードはなくてもよいが,いったいどの国にヤクザが多いのか興味はありませんか? この処理は UNIX の whois コマンドを IP アドレス毎にひとつひとつ叩いて CIDR を調べ,Apache の定義文を作成すればよいけれども,IP の数が多いとやってられない。ネットワークの IP アドレス範囲から CIDR を計算しなければならない場合もあり,面倒だ。そこで私は,これらを全部自動的に行う Perl コード ipwhois.pl を書いた。ipwhois.pl コードを記事の最後に掲げておく。このプログラムは IP アドレス・リストを標準入力から読みとって,IP アドレス毎に whois を照会し,当該 IP の国コードと CIDR を取得あるいは計算し,指定出力ファイルに Apache の拒絶定義文(deny from CIDR)の形式で出力するとともに,標準エラー出力に国コード毎の集計データを出力する。CIDR が得られなかったら IP アドレス単体指定を行うものとしている。さて,私の自宅サーバログに基づく実行結果は以下のとおり。標準エラーの全出力を掲げておきます。ヤクザ IP・CIDR の一覧でもありますので,お使いいただいても構いません。
      % ipwhois.pl -o denylist < 404ip.txt 
      113.59.33.9     CN      113.59.0.0/17
      113.84.73.25    CN      113.64.0.0/11
      119.254.72.218  CN      119.254.0.0/15
      121.14.129.100  CN      121.8.0.0/13
      121.141.172.40  KR      121.128.0.0/11
      121.166.70.252  KR      121.160.0.0/11
      140.112.41.58   TW      140.112.0.0/16
      173.162.102.20  US      173.162.64.0/18
      173.245.70.17   US      173.245.70.0/24
      175.177.100.182 JP      175.177.100.0/24
      182.20.206.193  JP      182.20.128.0/17
      184.107.108.37  CA      184.107.108.0/24
      188.127.230.124 RU      188.127.224.0/20
      188.143.233.14  RU      188.143.232.0/23
      188.143.233.160 RU      188.143.232.0/23
      188.143.233.34  RU      188.143.232.0/23
      188.163.105.52  UA      188.163.0.0/17
      188.163.65.234  UA      188.163.0.0/17
      188.163.67.245  UA      188.163.0.0/17
      188.163.67.254  UA      188.163.0.0/17
      188.40.106.11   DE      188.40.0.0/16
      188.40.53.213   DE      188.40.0.0/16
      190.144.175.133 CO      190.144.175.133 (CIDR 190.144/14 not available)
      193.105.210.42  UA      193.105.210.0/24
      195.68.223.44   UA      195.68.222.0/23
      202.19.227.40   JP      202.19.227.40   (CIDR 2 not available)
      203.142.24.17   SG      203.142.24.0/24
      203.180.234.16  JP      203.180.234.0/24
      203.238.185.121 KR      203.238.176.0/20
      204.111.110.29  US      204.111.0.0/16
      204.9.123.122   US      204.9.120.0/21
      208.78.244.107  US      208.78.240.0/21
      209.144.30.98   US      209.144.30.0/24
      209.172.57.77   CA      209.172.32.0/19
      209.190.38.98   US      209.190.38.96/29
      209.236.115.222 US      209.236.112.0/20
      210.165.234.248 JP      210.165.128.0/17
      210.253.108.17  JP      210.253.96.0/19
      211.144.82.8    CN      211.144.64.0/19
      211.233.71.243  KR      211.233.64.0/20
      211.62.35.217   KR      211.62.32.0/21
      211.68.122.12   CN      211.68.120.0/22
      211.72.32.244   TW      211.72.0.0/17
      212.138.82.23   SA      212.138.64.0/18
      212.54.226.117  IT      212.54.224.0/19
      216.171.171.163 US      216.171.160.0/20
      219.136.241.165 CN      219.136.241.165 (CIDR 6 not available)
      221.117.61.178  JP      221.117.61.176/29
      222.186.43.119  CN      222.184.0.0/13
      31.128.142.199  RU      31.128.128.0/19
      31.184.236.13   UA      31.184.236.0/24
      31.184.236.33   UA      31.184.236.0/24
      31.184.236.34   UA      31.184.236.0/24
      31.184.236.36   UA      31.184.236.0/24
      31.184.238.71   RU      31.184.238.0/24
      46.109.177.176  LV      46.109.0.0/16
      46.116.43.68    IL      46.116.0.0/15
      46.21.154.66    US      46.21.144.0/20
      58.191.154.41   JP      58.191.154.40/29
      58.215.12.42    CN      58.208.0.0/12
      59.108.108.100  CN      59.108.0.0/16
      61.187.206.148  CN      61.187.206.148  (CIDR 4 not available)
      61.190.172.2    CN      61.190.0.0/16
      64.124.203.73   US      64.124.0.0/15
      66.87.2.132     US      66.87.0.0/16
      68.15.157.99    US      68.0.0.0/12
      68.58.54.214    US      68.32.0.0/11
      69.133.106.156  US      69.132.0.0/14
      69.26.37.39     US      69.26.32.0/19
      69.73.163.187   US      69.73.128.0/18
      72.29.84.41     US      72.29.64.0/19
      72.44.90.84     US      72.44.80.0/20
      74.217.148.71   US      74.217.0.0/16
      74.217.148.72   US      74.217.0.0/16
      77.207.110.229  FR      77.192.0.0/12
      77.247.181.165  NL      77.247.176.0/21
      77.52.112.197   UA      77.52.64.0/18
      77.92.224.110   GE      77.92.224.0/24
      77.92.233.198   GE      77.92.233.0/24
      81.170.143.96   SE      81.170.128.0/17
      81.25.46.137    BY      81.25.32.0/20
      82.193.117.23   UA      82.193.96.0/19
      85.192.15.82    RU      85.192.0.0/20
      85.88.195.35    IT      85.88.192.0/19
      86.101.224.233  HU      86.101.0.0/16
      86.127.192.137  RO      86.120.0.0/13
      88.40.179.242   IT      88.40.0.0/15
      89.107.227.210  TR      89.107.227.0/24
      89.252.58.228   UA      89.252.0.0/18
      89.28.124.238   MD      89.28.0.0/17
      89.97.190.170   IT      89.97.0.0/16
      91.135.235.139  GB      91.135.224.0/20
      91.207.6.154    UA      91.207.6.0/24
      91.207.6.174    UA      91.207.6.0/24
      91.207.6.82     UA      91.207.6.0/24
      91.214.45.239   LU      91.214.44.0/22
      91.224.160.126  NL      91.224.160.0/23
      91.224.160.127  NL      91.224.160.0/23
      91.224.161.127  NL      91.224.160.0/23
      91.226.165.164  RU      91.226.164.0/22
      92.113.10.162   UA      92.113.0.0/18
      93.182.36.82    RU      93.182.32.0/20
      93.84.116.216   BY      93.84.0.0/15
      94.24.152.61    RU      94.24.128.0/17
      Country list:
      GB = 1; GE = 2; NL = 4; TW = 2; LV = 1; BY = 2; SA = 1; KR = 5; RU = 10; MD = 1;
       TR = 1; JP = 8; CA = 2; SE = 1; CN = 12; DE = 2; LU = 1; CO = 1; RO = 1; SG = 1
      ; IT = 4; UA = 17; US = 21; IL = 1; HU = 1; FR = 1; 
      
    9. Apache denial list 重複削除
    10. ipwhois.pl が出力した denial list denylist から CIDR が重複するものを一つに纏める。
      % cat denylist | sort | uniq > denylist.txt
      
    11. Apache httpd.conf への反映
    12. 完成した denial list denylist.txt を Apache のコンフィグレーション(httpd.conf あるいは,これからインクルードされる定義ファイル)中のドキュメントルート Directory ディレクティブのところに,テキストエディタを使って反映する。以下はその例。このあと定義文をチェックし(apachectl -t),問題なければ,Apache を再起動(apachectl restart)して,作業は完了である。
      <VirtualHost *:80>
          ServerName yasuda.homeip.net
          DocumentRoot /usr/local/www/apache22/data
          <Directory />
              Options ExecCGI Includes FollowSymLinks
              AllowOverride None
          </Directory>
          <Directory "/usr/local/www/apache22/data">
              Options ExecCGI Includes FollowSymLinks
              AllowOverride AuthConfig
              Order allow,deny
              Allow from all
              # 以下 denial list 内容の CIDR を指定する
              deny from 113.59.0.0/17
              deny from 113.64.0.0/11
              deny from 119.254.0.0/15
              deny from 121.128.0.0/11
              deny from 121.160.0.0/11
              deny from 121.8.0.0/13
              deny from 140.112.0.0/16
              deny from 173.162.64.0/18
              deny from 173.245.70.0/24
              deny from 175.177.100.0/24
      ...
      

    さて,ipwhois.pl の実行結果から,我が家のサーバ・アタック杯ヤクザ国家の発表です。アタック元 IP の国コードとして多いのは,順に,一位:米国 21 ポイント,二位:ウクライナ 17 ポイント,三位:中国 12 ポイント,四位:ロシア 10 ポイント,五位:日本 8 ポイント,六位:韓国 5 ポイント,七位:オランダ,イタリア 4 ポイントでした。米国,中国,ロシアが上位に来るのは予想がつきましたが,ウクライナの二位は意外でした。もちろんこの国コードはサーバから見ての要求元なので,中国のヤクザが米国のプロクシ経由で攻撃したものは米国に分類されているはずである。

    ipwhois.pl コードは以下のとおり。コピペして使っていただいてもよい(無保証)。whois の項目を調べるための Perl モジュール Net::Whois::IPNet::CIDR が必要なので,動かすには事前に cpan コマンドでシステムにモジュールをインストールしておく必要がある。
     

    #!/usr/bin/perl -w
    # -*- coding: utf-8; mode: cperl; -*-
    # Get ip-route and generate deny list from whois info
    # $Id: ipwhois.pl 5 2011-12-11 12:18:13Z isao $
    # 2011 (c) isao yasuda.
     
    use strict;
    use Net::Whois::IP qw(whoisip_query);
    use Net::CIDR qw(:all);
    use Getopt::Std;    # command line processing
    use File::Basename; # get file basename
    binmode(STDOUT, ":utf8"); binmode(STDERR, ":utf8");
     
    # command line.
    my %opts = ('o' => "0");
    Getopt::Std::getopts('o:', \%opts) ||
        die "Usage: " . basename($0) . " -o denylist \< (stdin)\n";
    my %ccode; # 国コード集計カウンタ配列
    if ($opts{'o'}) { # deny list open
        open(DL, ">> $opts{'o'}") || die "$opts{'o'} open failed.\n";
    } else { # 指定がなければ標準出力
        open(DL, ">-");
    }
    # IP 毎の処理
    while (<STDIN>) {
        chomp($_); $_ =~ s/\s+//g;
        # Whois 情報取得
        my $ip = $_;
        my $response = whoisip_query($ip);
        # 国コード取得
        my $country;
        if ($response->{country}) {
            $country = $response->{country};
        } else {
            $country = $response->{Country};
        }
        $country =~ s/\s+//g; $country =~ tr/a-z/A-Z/;
        # CIDR 取得 なければ IP をセット
        my $cidr; my $comm = "";
        if ($response->{route}) {
            $cidr = $response->{route};
        } else {
            if ($response->{CIDR}) {
                $cidr = $response->{CIDR};
            } else { # IP 範囲から CIDR を計算
                my $range = $response->{inetnum}; $range =~ s/\s+//g;
                $cidr = Net::CIDR::range2cidr($range);
                unless ($cidr =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\/[0-9]+$/) {
                    $comm = "\t(CIDR $cidr not available)";
                    $cidr = $ip;
                }
            }
        }
        # 複数 CIDR が得られた場合はじめの一つのみにする
        my @acidr = split(/,/, $cidr);
        if ($#acidr) {
            $cidr = $acidr[0]; $cidr =~ s/\s+//g;
            $comm = "\t(Some CIDRs found. Selected one)";
        }
        # 端末出力
        print STDERR "$ip\t$country\t$cidr$comm\n";
        # Apache 用 deny list 出力
        print DL "deny from $cidr\n";
        # 国コード集計
        if (exists($ccode{$country})) {
            $ccode{$country} += 1;
        } else {
            $ccode{$country} = 1;
        }
    }
     
    # 国コード集計出力.
    print STDERR "Country list:\n";
    foreach (keys %ccode) { print STDERR "$_ = $ccode{$_}; "; }
    print STDERR "\n";
    close(DL);
     
    
     

    追記:

    こんなことをやっていたために,クラブワールドカップ・柏レイソル vs 北中米カリブ海代表モンテレイの試合を見忘れてしまった。柏レイソルが PK で勝利した。すげぇ。ベスト 4 進出。次はブラジル王者サントスとの対戦。楽しみじゃ。
     

    12.12 追記:

    whois から複数の CIDR が返却される場合があるため,その場合一つだけ採用するよう ipwhois.pl を訂正した。

    麻雀点数集計プログラム

    |

    サッカー五輪予選を観ながら,Web 無料麻雀ゲームをした。われらが日本代表が一生懸命アウェイで頑張っているというのに,観ている方はなんと暢気なものか。半荘を 10 回もやり,珍しく一人勝ち。
     

    20111120-majan.jpg
     

    バブル時代の高レート勝負だったらいったいいくら稼いだのかちょっと知りたくなって,プログラムを書いて計算してみた。プログラムは mjcalc。Perl である。

    mjcalc -u [ウマ] -b [ビンタ] -k [レート] < 点数データ
    
    ここで,[ウマ] は "数字1-数字2" の形式で指定し,"数字1" は 3 位から 2 位への,"数字2" は 4 位から 1 位へのウマとなる。省略すると 10-20 が仮定される。[ビンタ], [レート] は数値を千単位で指定する。省略すると,それぞれ 100, 2 を仮定する。点数データは空白文字区切りの 4 つの整数値である。

    計算の考え方は次のとおり。25000 点持ちの 30000 点返し。オカは 20000 点とする。入力データは,1000 点 1 ポイントとして換算された各プレイヤの点数が空白文字で区切られているものととする。入力の点数を合計し,0 の場合はすでにウマ,オカが反映されているものとしてレート計算のみ行う。入力点数の合計が 100 の場合は,半荘終了時の持点として,指定されたウマ,オカを計算に反映する。指定されたビンタ,レートで金額計算を行う。ビンタとは下位のプレイヤが上位者に支払う差ウマのことで,ビンタ 10 万の取り決めならば,上位者に 10 万,自分が配給原点以下の場合,配給原点以上の上位者にはその倍額の 20 万を支払わなければならない。独り沈みの 4 位者ならば 60 万のマイナスというわけである。

    さて,mjcalc で計算してみる。デカリャンピン (1000 点 2 千),ビンタ 10 万の気違いレート。データは上に示した 10 回のデータ (point.txt) である。この Web 麻雀ゲームの集計値はすでに 10-20 のウマ,20 ポイントのオカ,30 ポイントとの差分が集計済みである。

     
    isolde:/Users/i-yasuda/src/tools[1125] % cat point.txt 
    60      -20     2       -42
    18      -21     -52     55
    63      9       -20     -52
    52      -21     3       -34
    11      -16     42      -37
    -29     67      6       -44
    10      -32     -21     43
    63      -21     2       -44
    40      5       -28     -17
    70      -30     -41     1
    isolde:/Users/i-yasuda/src/tools[1126] % ./mjcalc -u 10-20 -b 100 -k 2 < point.txt 
    ウマ1: 10; ウマ2: 20; オカ: 20(以上pt); ビンタ: 100(千円); レート: 2(千円/1pt)
    ----------------------------------------------------------------------
      Input Points     ;  Calculated Points ;   Amount of Money (k-en)   ;
       A    B    C    D;    A    B    C    D;      A      B      C      D;
    ----------------------------------------------------------------------
     +60  -20   +2  -42;  +60  -20   +2  -42;   +720   -240     +4   -484;
     +18  -21  -52  +55;  +18  -21  -52  +55;   +336   -342   -604   +610;
     +63   +9  -20  -52;  +63   +9  -20  -52;   +626   +318   -340   -604;
     +52  -21   +3  -34;  +52  -21   +3  -34;   +704   -242     +6   -468;
     +11  -16  +42  -37;  +11  -16  +42  -37;   +322   -332   +584   -574;
     -29  +67   +6  -44;  -29  +67   +6  -44;   -358   +634   +312   -588;
     +10  -32  -21  +43;  +10  -32  -21  +43;   +320   -564   -342   +586;
     +63  -21   +2  -44;  +63  -21   +2  -44;   +726   -242     +4   -488;
     +40   +5  -28  -17;  +40   +5  -28  -17;   +580   +310   -556   -334;
     +70  -30  -41   +1;  +70  -30  -41   +1;   +740   -260   -482     +2;
    ----------------------------------------------------------------------
    +358  -80 -107 -171; +358  -80 -107 -171;  +4716   -960  -1414  -2342;
     
    

    うん。半荘 10 回で 471.6 万を獲得したというわけである。でも,なんでこんな意味のないことをやっているのか。で,プログラムを書きながら計算の確認のため,やっぱりこの麻雀ゲームをやっていたら,なんと四暗刻を,しかもピンズ一色・嶺上開花(意味無し)でツモってしまいました。
     

    20111124-suanko.jpg
     

    一応,プログラム mjcalc のコードを掲げておきます。ヒマなときにこんなことしかしない,私のような方のために。

     
    #!/usr/bin/perl -w
    # -*- coding: utf-8; mode: cperl; -*-
    #  Mahjong Points Calculator
    #  -------------------------
    #  Usage: mjcalc -u 10-20 -b 100 -k 2 < data
    #    -u 10-20: ウマ 10-20 (ワンツー)
    #    -b 100: ビンタ 100K円
    #    -k 2: レート 1000点 2K円
    #  2011(c) isao yasuda.
     
    use strict;
    use utf8;
    use Getopt::Std;    # command line processing
    use File::Basename; # get file basename
    binmode(STDOUT, ":utf8"); binmode(STDERR, ":utf8");
     
    # command line
    my %opts = ('u' => "0", 'b' => 0, 'k' => 0);
    Getopt::Std::getopts('u:b:k:', \%opts) ||
        die "Usage: " . basename($0) .
        " [ -u uma-range -b binta-k -k k-en ] \< (stdin)\n";
    my $umai1 = 10; my $umai2 = 20; my $okai = 20;
    my $uma1 = 0; my $uma2 = 0; my $oka = 20; my $kaesi = 30;
    my $binta = 100; $binta = $opts{'b'} if ($opts{'b'});
    my $krate = 2;   $krate = $opts{'k'} if ($opts{'k'});
    if ($opts{'u'}) {
        unless ($opts{'u'} =~ /^[0-9]+-[0-9]+$/) {
            die "-u operand invalid.\n";
        }
        ($umai1, $umai2) = split(/-/, $opts{'u'}, 2);
        $uma1 = $umai1; $uma2 = $umai2;
    }
    if (($uma1 == 0) && ($uma2 == 0)) {
        $oka = 0; $kaesi = 0;
    }
    if ($oka != 0) {
        print STDERR "ウマ1: $uma1; ウマ2: $uma2; オカ: $oka(以上pt); ";
    }
    print STDERR "ビンタ: $binta(千円); レート: $krate(千円/1pt)\n";
    my %totalpt = ('A' => 0, 'B' => 0, 'C' => 0, 'D' => 0); # ポイント総計
    my %totalmn = ('A' => 0, 'B' => 0, 'C' => 0, 'D' => 0); # 金額総計
    my %totalin = ('A' => 0, 'B' => 0, 'C' => 0, 'D' => 0); # 入力ポイント総計
    print
        "----------------------------------------------------------------------\n";
    print
        "  Input Points     ;  Calculated Points ;   Amount of Money (k-en)   ;\n";
    print
        "   A    B    C    D;    A    B    C    D;      A      B      C      D;\n";
    print
        "----------------------------------------------------------------------\n";
     
    # Main
    while (<STDIN>) {
        utf8::decode($_); chomp($_);
        my @tensu = split(/\s+/, $_, 4);
        my $sowa = $tensu[0] + $tensu[1] + $tensu[2] + $tensu[3];
        unless (($sowa == 100) || ($sowa == 0)) {
            print STDERR "点数総和 $sowa は矛盾します。無視します。\n";
            next;
        } else {
            if ($sowa == 100) { # 持ち点
                $oka = $okai; $uma1 = $umai1; $uma2 = $umai2; $kaesi = 30;
            } else {            # すでにオカ・ウマ計算済
                $oka = $uma1 = $uma2 = $kaesi = 0;
            }
        }
        # プレーヤ-点数 配列
        my %plpt = ('A'=>$tensu[0],'B'=>$tensu[1],'C'=>$tensu[2],'D'=>$tensu[3]);
        # input print
        printf("%+4d %+4d %+4d %+4d; ",
               $plpt{'A'}, $plpt{'B'}, $plpt{'C'}, $plpt{'D'});
        $totalin{'A'} += $tensu[0];
        $totalin{'B'} += $tensu[1];
        $totalin{'C'} += $tensu[2];
        $totalin{'D'} += $tensu[3];
        my @ptord; # 点数昇順リスト (last, pt4, pos3, pt3, .., top, pt1)
        foreach my $key (sort { $plpt{$a} <=> $plpt{$b} } keys %plpt) {
            push(@ptord, ($key, $plpt{$key}));
        }
        # ビンタ計算
        my @bbuff = (0, 0, 0, 0); # 点数やり取りバッファ
        # 4
        if ((($sowa == 100) && ($ptord[1] < 25)) ||
            (($sowa == 0) && (($ptord[1] + $umai2) < -5))) {
            # to 3
            if ((($sowa == 100) && ($ptord[3] < 25)) ||
                (($sowa == 0) && (($ptord[3] + $umai1) < -5))) {
                $bbuff[1] -= $binta; $bbuff[3] += $binta;
            } else {
                $bbuff[1] -= ($binta * 2); $bbuff[3] += ($binta * 2);
            }
            # to 2
            if ((($sowa == 100) && ($ptord[5] < 25)) ||
                (($sowa == 0) && (($ptord[5] - $umai1) < -5))) {
                $bbuff[1] -= $binta; $bbuff[5] += $binta;
            } else {
                $bbuff[1] -= ($binta * 2); $bbuff[5] += ($binta * 2);
            }
            # to 1
            if ((($sowa == 100) && ($ptord[7] < 25)) ||
                (($sowa == 0) && (($ptord[7] - $umai2) < -5))) {
                $bbuff[1] -= $binta; $bbuff[7] += $binta;
            } else {
                $bbuff[1] -= ($binta * 2); $bbuff[7] += ($binta * 2);
            }
        } else {
            # to 3
            $bbuff[1] -= $binta; $bbuff[3] += $binta;
            # to 2
            $bbuff[1] -= $binta; $bbuff[5] += $binta;
            # to 1
            $bbuff[1] -= $binta; $bbuff[7] += $binta;
        }
        # 3
        if ((($sowa == 100) && ($ptord[3] < 25)) ||
            (($sowa == 0) && (($ptord[3] + $umai1) < -5))) {
            # to 2
            if ((($sowa == 100) && ($ptord[5] < 25)) ||
                (($sowa == 0) && (($ptord[5] - $umai1) < -5))) {
                $bbuff[3] -= $binta; $bbuff[5] += $binta;
            } else {
                $bbuff[3] -= ($binta * 2); $bbuff[5] += ($binta * 2);
            }
            # to 1
            if ((($sowa == 100) && ($ptord[7] < 25)) ||
                (($sowa == 0) && (($ptord[7] - $umai2) < -5))) {
                $bbuff[3] -= $binta; $bbuff[7] += $binta;
            } else {
                $bbuff[3] -= ($binta * 2); $bbuff[7] += ($binta * 2);
            }
        } else {
            # to 2
            $bbuff[3] -= $binta; $bbuff[5] += $binta;
            # to 1
            $bbuff[3] -= $binta; $bbuff[7] += $binta;
        }
        # 2
        if ((($sowa == 100) && ($ptord[5] < 25)) ||
            (($sowa == 0) && (($ptord[5] - $umai1) < -5))) {
            # to 1
            if ((($sowa == 100) && ($ptord[7] < 25)) ||
                (($sowa == 0) && (($ptord[7] - $umai2) < -5))) {
                $bbuff[5] -= $binta; $bbuff[7] += $binta;
            } else {
                $bbuff[5] -= ($binta * 2); $bbuff[7] += ($binta * 2);
            }
        } else {
            # to 1
            $bbuff[5] -= $binta; $bbuff[7] += $binta;
        }
        # ポイント,ビンタ,レート計算
        # プレーヤ-点数 配列
        my %ptrslt = ($ptord[0]=>0, $ptord[2]=>0, $ptord[4]=>0, $ptord[6]=>0);
        # 4
        $ptord[1] -= $uma2; $ptord[1] -= $kaesi;
        $ptrslt{$ptord[0]} = $ptord[1];
        $ptord[1] = $ptord[1] * $krate; $ptord[1] += $bbuff[1];
        # 3
        $ptord[3] -= $uma1; $ptord[3] -= $kaesi;
        $ptrslt{$ptord[2]} = $ptord[3];
        $ptord[3] = $ptord[3] * $krate; $ptord[3] += $bbuff[3];
        # 2
        $ptord[5] += $uma1; $ptord[5] -= $kaesi;
        $ptrslt{$ptord[4]} = $ptord[5];
        $ptord[5] = $ptord[5] * $krate; $ptord[5] += $bbuff[5];
        # 1
        $ptord[7] += $uma2; $ptord[7] -= $kaesi; $ptord[7] += $oka;
        $ptrslt{$ptord[6]} = $ptord[7];
        $ptord[7] = $ptord[7] * $krate; $ptord[7] += $bbuff[7];
     
        # プレーヤ-金額 配列
        my %mnrslt = ($ptord[0] => $ptord[1], $ptord[2] => $ptord[3],
                      $ptord[4] => $ptord[5], $ptord[6] => $ptord[7]);
        printf("%+4d %+4d %+4d %+4d; ",
               $ptrslt{'A'}, $ptrslt{'B'}, $ptrslt{'C'}, $ptrslt{'D'});
        printf("%+6d %+6d %+6d %+6d;\n",
               $mnrslt{'A'}, $mnrslt{'B'}, $mnrslt{'C'},$mnrslt{'D'});
        foreach my $key (keys %ptrslt) {
            $totalpt{$key} += $ptrslt{$key}; $totalmn{$key} += $mnrslt{$key};
        }
    }
     
    # 総計出力
    print
        "----------------------------------------------------------------------\n";
    printf("%+4d %+4d %+4d %+4d; ",
           $totalin{'A'}, $totalin{'B'}, $totalin{'C'}, $totalin{'D'});
    printf("%+4d %+4d %+4d %+4d; ",
           $totalpt{'A'}, $totalpt{'B'}, $totalpt{'C'}, $totalpt{'D'});
    printf("%+6d %+6d %+6d %+6d;\n",
           $totalmn{'A'}, $totalmn{'B'}, $totalmn{'C'}, $totalmn{'D'});
     
    

    ※ 11.27 付記
    最初の版には,ウキの判定とビンタ計算のバグがありました。訂正しました。

    あと一点。同点者がいることを考慮してません。一局目の一巡目でいきなり役満放銃で一発ハコ一人沈みみたいな場合でない限り,こんなことはなかろうと思い,無視しました。

    しばらく前に,会社の品質保証部のセキュリティ情報で Apache Web サーバの DoS 脆弱性が話題になり,現場に対し担当システムを確認するよう指示がまわっていた。担当顧客システムの調査/対応が一段落したので,自宅の Web サーバもチェックしてみることにした。

    この脆弱性のアドヴァイザリは「JVNTR-2011-05 Apache HTTPD サーバにサービス運用妨害 (DoS) の脆弱性 (CVE-2011-3192, JVNVU#405811)」。Apache 2.x 系すべてが対象とのこと。Range ヘッダ処理で Web サーバのみならずシステム全体のサービス不能を引き起こす問題である。

    自宅の FreeBSD 8.2-RELEASE 公開 Web サーバを確認すると,インストールした Apache22 の CHANGES(修正履歴ファイル)には Fix a regression introduced by the CVE-2011-3192 byterange fix in 2.2.20. とあるので,すでに対策済みの版だとわかった。OS を組込んだ 9 月には,FreeBSD Apache22 ports は対策版に更新されていたわけである。この問題を検証できる Perl コード Apache Killer - killapache.pl が公開されている。検証のため,公開サーバをこのプログラムで攻撃してみた。

    isolde:/Users/i-yasuda/var/ % perl killapache.pl beatrice
    Host does not seem vulnerable
    

    となって,確かに対策済みだとわかった。killapache.pl 攻撃コードは Perl IO::Socket 及び Parallel::ForkManager モジュールを必要とするのであらかじめ組込んでおく必要がある。ためしに,メインで使っている書斎の Mac OS X Tiger マシンを攻撃してみると,みるみるうちに CPU 利用率が 100% に跳ね上がってそれが延々と続き,操作がずしんと重くなった。

    この脆弱性は有名だし,攻撃コードが公開されているくらいだから,ご自分で Web サーバを運用されている方はぜひチェックして,未対応なら Apache を最新版に更新していただきたい。インストールした Apache のドキュメントを見て,CVE-2011-3192 アドヴァイザリが対策されていればよい。

    ruby マークアップ・ツール

    |

    昨日書いた泉鏡花『貧民倶樂部』の記事において,鏡花テクスト引用のために大量のルビをマークアップしなければならなかった。HTML で <ruby><rb></rb><rp></rp><rt>これ</rt><rp>)</rp></ruby> とマークアップすると,これ となる。<ruby> タグをサポートしていないブラウザならタグ以外のテキストがそのまま出力されて 此(これ)となる。LaTeX にも奥村先生のマクロ集に \ruby 命令があり,この場合は \ruby{此}{これ} とマークアップする。これらのルビ付加用マークアップ作業は,やっている最中に元の文章が分断されて判りにくくなりかつ極めて面倒なので,私は自分で書いた簡単なツールで一括整形している。今日はその整形ツール convertruby を紹介しておく。私は Mac OS X で使用しているが,FreeBSD, Linux でも動作するはずである。同じ必要に迫られた方はプログラム・コードをコピってお使いください。

    このツールは,標準入力から UTF-8 テキストを読み,此(これ)のように,漢字 + + 読み + のように書いた文字列に対して,マークアップを行う。デフォルトでは HTML タグを付加する。-l オプションを指定すると LaTeX 形式で整形する。対象テキストの括弧は全角でないといけない。そういう融通の利かないところがある。開括弧より前のテキストを後ろから逆方向に走査し,漢字以外が出現するまで漢字をスタックにプッシュし,あとでこのスタックからポップして得られる文字列(先入れ後出しにより文字の順番が戻る)を親文字と判定する。「漢字」は Unicode CJK 統合漢字に属するかを \p{InCJKUnifiedIdeographs} 正規表現文字クラスで判断している。Perl コードは以下の通り。

    #!/usr/bin/perl -w
    # -*- coding: utf-8; mode: cperl; -*-
    #
    #     convertruby: ルビ・マークアップ
    #     - ベタのテキストを <ruby> or \ruby シーケンスに整形する
    #     - 例: 此(これ) ["(", ")" は全角括弧]
    #       -> <ruby><rb>此</rb><rp>(</rp><rt>これ</rt><rp>)</rp></ruby> (default)
    #       -> \ruby{此}{これ} (with -l option)
    #     - usage: convertruby [-l] < stdin
    #          -l: for LaTeX; default: for HTML
    #
    use strict;
    use utf8;
    use Getopt::Std;
    use File::Basename;
    binmode(STDOUT, ":utf8");
    # コマンドライン・オプション処理
    my %opts = ('l' => 0);
    Getopt::Std::getopts('l', \%opts) ||
        die "Usage: " . basename($0) .
        " [-l] \< (stdin)\n   -l: for LaTeX; default: for HTML\n";
    # 行毎の主処理
    while (<STDIN>) {
        my $line = $_;
        utf8::decode($line); utf8::decode($_);
        my ($yomi, $leftt, $rightt);
        # 「(読み)」を含まない行はそのまま出力
        unless ($_ =~ /([^)]*)/gi) {
            print $line; next;
        }
        # 「(読み)」を走査し,これがある限りその前後を切り出す
        while ($line =~ /([^)]*)/gi) {
            $yomi = $&; $leftt = $`; $rightt = $';
            # 「(読み)」から括弧を外す
            $yomi =~ s|[()]||g;
            # 対象漢字部を後方から走査し,漢字スタックに push する
            my @kstack = (); my $k;
            my @istack = split(//, $leftt); # 「読み」の左側テキストの文字配列
            while ((@istack) &&
                   (($k = pop(@istack)) =~ /\p{InCJKUnifiedIdeographs}/g)) {
                push(@kstack, $k); $k = "";
            }
            # 最後に pop した非漢字を左側テキスト配列スタックに戻しておく
            push(@istack, $k) if ($k);
            # 漢字スタックから漢字部文字列を pop で復元
            my $kanji = "";
            while (@kstack) {
                $kanji .= pop(@kstack);
            }
            # 抽出した漢字部よりも前の文字列を出力
            if (@istack) {
                print $_ for @istack;
            }
            # ruby マークアップ部分を出力
            if ($kanji) {
                if ($opts{'l'}) {
                    # LaTeX (with -l option)
                    print '\ruby{' . $kanji . '}{' . $yomi . '}';
                } else {
                    # HTML (default)
                    print '<ruby><rb>' . $kanji .
                        '</rb><rp>(</rp><rt>' . $yomi . '</rt><rp>)</rp></ruby>';
                }
            } else {
                # 「漢字(読み)」のパターンでないものはそのまま出力
                print "($yomi)";
            }
            # 走査対象文字列に右側テキスト(残り)文字列を格納して,繰り返し
            $line = $rightt;
        }
        # 残り文字列を出力
        print $rightt if ($rightt);
    }
    
    GNU Emacs からは,以下を .emacs に追加すれば,利用できるようになる。ここでは convertruby/usr/local/bin 下にインストールされているものとしている。この定義を Emacs に評価させた後,テキスト・リージョンを指定し,M-x convert-ruby-html, M-x convert-ruby-latex を実行すると,当該リージョンがそれぞれ HTML, LaTeX のルビ・マークアップ整形を施される。

    ;; convertruby for HTML
    (defun convert-ruby-html (start end)
      "Convert text to ruby HTML sequence."
      (interactive "r")
      (call-process-region
       start end
       "/usr/local/bin/convertruby"
       t (list t nil) nil
       "")
      )
    ;; convertruby for LaTeX
    (defun convert-ruby-latex (start end)
      "Convert text to ruby HTML sequence."
      (interactive "r")
      (call-process-region
       start end
       "/usr/local/bin/convertruby"
       t (list t nil) nil
       "-l")
      )
    

    泉鏡花『貧民倶樂部』の引用に際しては,新字・新仮名遣いで引用文を作成し(図 1),自作のソフトで旧字・旧仮名遣いに変換し,次に,ルビを付けたい漢字に「(よみ)」を付加し(図 2),最後に Emacs 上で convertruby を実行して <ruby> タグをマークアップした(図 3)。ただし,図 3-1 は HTML 整形の,図 3-2 は LaTeX 整形の結果例である。
     

    20111012-ruby-1.jpg図 1. ルビなしで新字・新仮名遣いで記述
    20111012-ruby-2.jpg図 2. 表記変換後,ルビを付加
    20111012-ruby-3.jpg図 3-1. HTML 整形実行結果
    20111012-ruby-4.jpg図 3-2. LaTeX 整形実行結果

    misima 漢詩作成支援: 平仄音韻分析・詩語検索 misimakansiservlet を使っている方からメールをいただいた。そこで思い出したように,久しぶりに辞書のメンテナンスを行った。平仄などのデータを格納した辞書=漢字データベースは SQLite3 で作成したのだけど,SQLite3 オペレーションを中心とするメンテナンス手順を忘れてしまっていた。思い出したのを整理し,ここにも備忘録を残しておく。以下,beatirce:/home/isao[nnnn] % は misima サーバ機端末の tcsh プロンプト,nnnn はコマンド履歴番号を示す。

    misimakansiservlet において,漢詩分析デーモン misimakansiserver は漢字データベース KANJITBL で見つからなかった文字をログに出力するようになっている。私はこれを定期的にチェックし,それらの辞書にない文字(ユーザが入力したが辞書になかった漢字)を手作業で DB に追加する。ログのその部分を抽出する。

    beatirce:/home/isao[1000] % grep -e '\*\*\*' /var/log/kansiserver.log | sort | uniq
    ***** 㱕 が KANJITBL にありません.
    ***** 僲 が KANJITBL にありません.
    ***** 嵆 が KANJITBL にありません.
    ***** 扃 が KANJITBL にありません.
    ***** 涴 が KANJITBL にありません.
    ***** 疴 が KANJITBL にありません.
    ***** 裛 が KANJITBL にありません.
    ***** 髥 が KANJITBL にありません.
    beatirce:/home/isao[1001] % 
    

    というわけで,今回この 8 文字を追加。漢字字典を調べ,平仄,読み,語義などを DB のソースコード kanji.src にテキストエディタで追加する。完了したら,create table で漢字データベースファイル KANJI.db を生成する。そしてこれに kanji.src の内容を .import コマンドでロードする。kanji.src のデータ記述はカンマ区切りの CSV 形式なので,ロード実行には -separator , オプションを指定している。

    beatirce:/home/isao[1001] % cd misima
    beatirce:/home/isao/misima[1002] % sqlite3 KANJI.db < mkkanji.sql
    beatirce:/home/isao/misima[1003] % sqlite3 -separator , KANJI.db ".import kanji.src KANJITBL"
    beatirce:/home/isao/misima[1004] % 
    

    ここで,mkkanji.sql とは create 文で DB のスキーマ(データ項目とその属性)を定義する元ネタである。misimakansiservlet の場合は以下のようなものである。

    -- -*- coding: utf-8; mode: sql; -*-
    -- misimakansiservert 平仄音韻分析用漢字テーブルレイアウト
    -- 漢字 ji, 平仄 hs, 韻 in1, 韻 in2, 音読 yo, 訓読 yk, 備考 bk
    create table KANJITBL (
           ji text not null,
           hs text not null,
           in1 text not null,
           in2 text not null,
           yo text not null,
           yk text not null,
           bk text not null
    );
    

    これで漢字データベースはできた。データ内容を確認して問題なければ,/usr/local/etc/misima/ の下にコピーする。これで Web サーブレットから使えるようになる。以下,SQLite3 DB 操作のメモ。

    1. SQLite3 コマンドラインの起動
    2. 扱う DB を引数に指定する。
      beatrice:/home/isao/misima[1004] % sqlite3 KANJI.db
      SQLite version 3.7.7.1 2011-06-28 17:39:05
      Enter ".help" for instructions
      Enter SQL statements terminated with a ";"
      sqlite> 
      
    3. テーブル名の表示
    4. sqlite> .table
      KANJITBL
      
    5. DB スキーマの表示
    6. sqlite> .schema KANJITBL
      CREATE TABLE KANJITBL (
                 ji text not null,
                 hs text not null,
                 in1 text not null,
                 in2 text not null,
                 yo text not null,
                 yk text not null,
                 bk text not null
      );
      
    7. DB 件数の確認
    8. sqlite> select count(*) from KANJITBL;
      6929
      
    9. tcsh コマンドラインから直接 SQL を入力する
    10. sqlite3 [opt] [db-file] に続けてクオート(ダブルクオーテーションマークで括る)して SQL 文を書けばよい。SQLite3 は標準入力からも SQL を受け付けるので,テキストファイルに書いておきリダイレクトで読み込ませてもよい。
      beatrice:/home/isao/misima[1005] % sqlite3 KANJI.db \
      "select * from KANJITBL where ji='髥';"
      髥|◎|hs:14:塩|sk:29:艷|ぜん;ねん|-|ほおひげ;鬚髯:柔らかいあごひげとほおひげ;
      beatrice:/home/isao/misima[1006] % cat sql.txt
      select * from KANJITBL where ji='㱕';
      select * from KANJITBL where ji='僲';
      select * from KANJITBL where ji='嵆';
      select * from KANJITBL where ji='扃';
      select * from KANJITBL where ji='涴';
      select * from KANJITBL where ji='疴';
      select * from KANJITBL where ji='裛';
      select * from KANJITBL where ji='髥';
      beatrice:/home/isao/misima[1007] % sqlite3 KANJI.db < sql.txt
      㱕|○|hk:05:微|-|き|かえる;かえす|帰・歸の異体字;
      僲|○|hs:01:先|-|せん|-|仙人;僊の異体字;
      嵆|○|hk:08:齊|-|けい;げ|-|稽古の当て・古いことを集めて考える;嵆山:山の名;嵆康:姓の一;
      扃|○|hs:09:青|-|けい;きょう|-|かんぬき;とざす;
      涴|●|sj:13:阮|sk:21:箇|えん;おん;わ|-|涴演:川が曲がりまわって流れるさま;けがす・けがれる;
      疴|◎|hs:05:歌|sk:22:禡|あ;か;け|-|やまい;痾に同じ・痾疾;
      裛|●|sn:14:緝|sn:16:葉|ゆう;おう;よう|-|つつむ・香がまとわりつく;書物の覆い;しっとりと濡れる;衣の部;
      髥|◎|hs:14:塩|sk:29:艷|ぜん;ねん|-|ほおひげ;鬚髯:柔らかいあごひげとほおひげ;
      beatrice:/home/isao/misima[1008] % 
      

    これで追加した漢字の平仄分析ができるようになった。
     

    20111008-kanjituika.jpg

    ジョブズ氏逝く・小沢裁判

    |

    今日の朝日夕刊の一面は,「小沢裁判・全面否認」と「アップル創業者スティーブ・ジョブズ氏死去」の記事だった。

    * * *

    スティーブ・ジョブズは,マイクロソフトのビル・ゲイツと並んで,パーソナル・コンピュータ業界の偉大なカリスマである。ゼロックス・パロアルト研究所やベル研究所,カリフォルニア大学バークレー,マサチューセッツ工科大学の研究成果をいただくことと,M & A による領土拡張とによる商売の大成功。お互いに憎み合いしのぎを削るマイクロソフトとアップルはどちらもこうした共通点がある。彼らの実業家センスは現代アメリカン・ドリームの象徴である。Windows は,コンピュータについては何も知らない人が使うパソコンに,頼まなくても搭載されているオペレーティング・システムとして世界を席巻している。一方,ジョブズのアップル・マッキントッシュにはアンチ Windows の熱狂的なファンがいる。かつて,ド素人向け「PC」(IBM-PC/AT の略)用という Microsoft Windows のイメージに対し,Apple Mac OS は QuarkXPress 全盛を受けて DTP オペレータやデザイナといったプロフェショナル・クリエイタ御用達の「おしゃれ」なイメージがあった。「少数派」ってわけである。ご存知の通りアップルはその後 iPod を世に出して,ソニーのウォークマンを駆逐し,レコード業界を窮地に陥れた。コンピュータが音楽を滅ぼす時代を作った。社名の示す通り,禁断の林檎を食わせた。いいことである。

    私も Mac を使っているけれども,「おしゃれ」を自称する一般のマックファンのような思い入れはまったくない。Windows も会社でありがたく使わせてもらっている。品質では悪名高い Windows 以上にバグが多い,そしてなかなか訂正されない,というのが,私の Mac に対する不満である(アップルがこれであんまり叩かれないのは,「少数派」の熱狂的サポータに支えられているからである)。UNIX として使用できなおかつ Adobe の Photoshop, Illustrator が動くマシン,— もっと正確にいうと — LaTeX でヒラギノフォントが使えるマシン,ということが,私の Mac を使う一番の,— もっと正確にいうと,唯一の — 理由である。ん,ジョブズ氏死去と何の関係があるって? 私とジョブズ氏とは,本来,何の関係もありません。

    * * *

    小沢さんの裁判は,検察審査会(=「正義感」だけの何もわからない素人)が密室で決めた「強制起訴」決議に基づく(ということは,検察がこれら 11 人の怒れるアマチュア集団を言葉巧みに誘導したんでしょうな)。先日彼の子分たちの一審有罪が宣告されたばかりである。この二つの「裁判」はいったい何の何処に犯罪性があるのかさっぱりわからない — だから「説明責任」で騒がしい — のだけれど,世の中は「金権巨悪を裁け」のような扱いである。

    小沢さんは,「僕が悪かった」以外は何を言おうが「説明」と捉えてもらえない「説明責任」という,メビウスの輪のように循環する衆愚的虐待に苛まれている。朝日は書いている:「小沢氏が起訴された背景には,説明不足に対する国民の不信感があるのは間違いない」。でも,身に覚えのない罪を着せられた人は,普通,「説明」のしようがないのではあるまいか。この記事が述べていることは,すなわち,日本という国は不信感で人が起訴されうる国だということである。これを改めて認識しよう。収賄の事実がまったく出ていないのにもかかわらず,生身の人間(たとえ政治家であろうと)を「説明不足に対する国民の不信感」ゆえに被告席に立たせているこの異常な事態を,朝日はなぜ問題視しないのか? この信じられない立件と強制起訴のおかげで,関係者は皆,三流週刊誌やバカ・ネット住民によって,普通の精神の持ち主なら死にたくなるであろうほどに,誹謗中傷され,人格を否定されているのである。

    マスコミのレベルは国民の品性を象徴するものだと私は思っている。いま私は自称先進国・日本という国がまったく理解できない。

    妻がマーラーの『亡き子を偲ぶ歌』が聴きたいというので,アグネス・バルツァの盤があったはずだと CD 棚を探した。ほどなく見つかった。私もマーラーが聴きたい気分になり交響曲第 7 番を取り出した。Gustav Mahler: Symphonie Nr. 7, "Lied der Nacht". Claudio Abbado (dir.), Chicago Symphony Orchestra. Deutsche Grammophon, 1984。ところがケースを開けたら,しばらく死蔵していたためか,CD 保護スポンジ材が化学反応か何かで CD にへばりついてしまっていた(セット物 CD にしばしば付いて来るこの保護スポンジ材は,CD に対して悪い経年変化を来すので,買ってすぐこれを捨て去るのがよい)。CD にこびり付いたスポンジを OA クリーナーでゴシゴシ拭いたら CD の印刷面がレーベルもろともかき消されてしまった。「ちゃんと鳴るかな」と不安になりつつ,オーディオにセットして再生したところ,果たしてウンともスンとも言わない。今度は CD を Mac の DVD デバイスで再生してみるときちんと鳴る。書斎のオーディオが故障してしまったらしいのである。「えー!? サーバの次はオーディオが故障かよー」。クソ暑かった今夏,とうとう耐えきれなくなったか。

    CD プレーヤ,プリ・アンプ,パワー・アンプ,スピーカ。壊れたのはこのうちどれだ? めんどくせぇー。アナログ・プレーヤも音が出ないので,CD プレーヤではなさそうである。スピーカなら 2 台同時にアウトなんてありえない。となるとプリかパワーのアンプだ。書斎のプリ,パワーはそれぞれ YAMAHA の C-2, B-6 というモデルを長らく愛用している。B-6 のピラミッド型の個性的デザインもたまらなく好きなんである。このほかに,以前使用していた Technics 製 60A, 70A というプリ,パワーのアンプを予備として残してある。この 4 機 — これらは皆 1970 年代に生産された超オンボロであるが,オーディオ機器としての質はいまだに現役なんである — を切替えて確認したところ,故障したのは C-2 プリ・アンプだと特定できた。

    リビングで使用しているプリ・アンプ — これも YAMAHA 製 C-2x という古色蒼然たるモデル,壊れた C-2 の上位機である — を書斎に移すことにした。リビングには Technics 70A を復活させる。システム・コンポーネント・オーディオのコードの繋ぎ替え作業の苛立たしさはやったことがある人にしかわからないだろう。70A は通常のフォノ・アンプ(アナログ・レコード信号のプリ増幅回路)しか搭載しておらず,リビングで使っているアナログ・プレーヤの MC カートリッジの小電圧には対応していない(C-2, C-2x は MC カートリッジ用の素晴らしいフォノ・アンプを備えており,トランスは不要である)。そこで MC 昇圧トランスを仲介しないといけないのだが,お蔵入りにした MC 昇圧トランス DENON 製 AU-300LC を,どこに仕舞ったのかなかなか探し出せず苦労した。見つけたときは,捨てなくてよかったと胸を撫で下ろした。何とか接続作業を終え,書斎の再生装置が再び鳴るようになった。でも,C-2 には思い入れがある。ヤマハはまだ修理を受入れてくれるかな,と心配になる。
     

    20111002-audio-1.jpgCD ラベル消え (左)/YAMAHA C-2, B-6 (右)

    20111002-audio-2.jpgTechnics A60, A70 (左)/DENON AU-300LC (右)
    20111002-audio-3.jpgYAMAHA C-2x, C-2 (左)/やっと復活 (右)
     

    ああ,やっぱりスピーカから出て来る音響のほうが断然よい。この至福は,iPod なんぞに満足しているうちのガキどもにはわからない(昨今,オーディオに少しでもこだわりをもつのはオヤジ世代になってしまった ...)。マーラーの 7 番 Lied der Nacht をいま聴いているところである。この曲は,五つの楽章それぞれの変化が多様過ぎて聞き所がよくわからず,マーラーの交響曲のなかではあんまり人気がないように思う。「夜の歌」と題されているけれども静謐なイメージがまるでなく,じつはヴァルプルギスの夜なのではないかと思わせるくらいに狂躁的な要素が強い。病的なスケルツォがあったりパッパラパー的ファンファーレがあったりと,全体としてパラノイアな雰囲気があり,私はけっこう好きなんである。私の愛聴するアバド,シカゴ響による演奏がドイツ・グラモフォンでまだ生きているようなので,リンクを設置しておく。
     

    マーラー:交響曲第7番「夜の歌」
    クラウディオ・アバド指揮
    シカゴ交響楽団
    ユニバーサル ミュージック クラシック (2011-07-06)

    FreeBSD メモリディスク

    |

    misima 旧仮名遣い・旧字変換サービスは大量の辞書を読み込む。高速化のためには出来る限りファイル入出力のオーバヘッドを減らしたい。そこでサイト運用において FreeBSD のメモリディスクに辞書などの misima リソースを格納して,インコアで処理できるようにしている。FreeBSD メモリディスクとはメモリに割り当てられたファイルシステム機能であり,昔の所謂 RAMDISK というのか,HDD に比べて高速なメモリにファイルを割り当てるものである。以下,そのオペレーションのメモ。

    misima の辞書等のリソースは一式 /usr/local/etc/misima ディレクトリ下に格納している。だいたい 5MB 程度である。この容量をもつファイルイメージでメモリファイルシステムを生成しマウントする。

    # [1] mkdir -p /usr/local/etc/misima
    # [2] cd /tmp
    # [3] dd if=/dev/zero of=newimage bs=1k count=5k
    # [4] mdmfs -F newimage -s 5m md0 /usr/local/etc/misima
    # [5] cd ~/src/misima/etc/
    # [6] tar cf - . | ( cd /usr/local/etc/misima; tar xvf -)
    

    [3] でまっさらな 5MB のファイル newimage を作成し(名前は任意。dd コマンドでゼロを 5MB = 1,024 x 5,120 バイト分書き込んでいるだけである),[4] でこれをメモリディスクデバイス md0 として /usr/local/etc/misima にマウントしている。もちろんこの時点で中味はまだ何もないので,[5][6] のように,開発ソースディレクトリから辞書などをコピーして準備完了となる。mdmfs コマンドの日本語マニュアルはここを参照。

    mount, df コマンドで確認すると,以下のような表示がされるはずである。

    # mount
    ......
    /dev/md0 on /usr/local/etc/misima (ufs, local, soft-updates)
    # df -k
    Filesystem  1024-blocks     Used     Avail Capacity  Mounted on
    ......
    /dev/md0           4718     3608       734    83%    /usr/local/etc/misima
    

    メモリ上のファイルシステムなので,当然ながらリブートすると跡形もなく消え去ってしまう。misima サービス起動時に上記オペレーションを自動的に実行するようなシェルスクリプトを書いて,運用するとよい。5MB くらいならキャッシュに入ってるって? ま,そうなんだけどね。

    FreeBSD 8.2-RELEASE HDD 増設

    |

    SATA-HDD を FreeBSD 8.2-RELEASE に追加してみた。disklabel コマンドが最近の FreeBSD では bsdlabel コマンドに変更されているなどのおかげで手間取ったが,何とか増設できた。備忘録を残しておく。ただし,以下はコマンドラインからのオペレーションによって増設 HDD をまるごと一ファイルシステムとして追加する方法である。FreeBSD 初期インストール時は,sysinstall (FreeBSD の標準インストーラ) で HDD を初期化するほうがよいと思う。

    増設 HDD がどのデバイスで認識されているか /var/run/dmesg.boot を確認する。私の場合,ad6 で認識されたようである。ちなみにブートパーティションのあるメイン HDD のデバイスは ad4 である。

    ad6: 476940MB <SAMSUNG HD501LJ CR100-10> at ata3-master UDMA100 SATA ...
    

    スーパユーザで,このデバイスを初期化し(dd),標準的ディスクラベルを書き込み(bsdlabel -w),ファイルシステムを生成し(newfs),最後にマウントする(mount)。マウンティングポイントは /disk としておく。

    # dd if=/dev/zero of=/dev/ad6 bs=1k count=1
    # bsdlabel -w ad6
    # newfs /dev/ad6a
    # mkdir -p /disk
    # mount /dev/ad6a /disk
    

    newfs コマンドで指定している ad6a/dev/ad6a% が存在していたので,これを使った。

    マウントがうまく行けば,/etc/fstab にもエントリを追加しておく。

    # Device                Mountpoint      FStype  Options         Dump    Pass#
    /dev/ad6a               /disk           ufs     rw              2       2
    

    NFS で他の PC からも参照するのであれば /etc/exports にもエントリを追加しておく。

    /disk  -alldirs  -network 192.168.1.0 -mask 255.255.255.0
    

    指定内容は割愛。man exports で確認してください。

    サーバが故障してリカバリに四苦八苦。しばらく触らないとすぐ設定方法を忘れる。また同じことが起こるはずなので,私自身のために書き残しておく。つまり,記述は私自身の運用環境に依存するので,不特定の方の参考にはならない可能性が高い。
     

    1. sendmail

    私のメール環境は,送信はプロバイダの SMTP サーバを用い,受信を自宅サーバで行い各ユーザのメールボックスに配信する運用になっている。SPAM メール対策に SpamAssassin を使い,少しでも機械的メール送信プログラム対策に資するよう Milter-Greylist を使っている。家族の PC で動くメールソフトから取りに来る受け口となる POP3 サーバ Qpopper, 着信メールの自動振分のための Procmail, Web アーカイブとして閲覧できるようにするための MHonArc, プロバイダ・アカウント宛に来たメールを取得する Fetchmail 等々,関連プログラムは少なくない。まずこれらのソフトウェアを ports でインストールする。ここでは Qpopper と SpamAssassin についてだけ少し。

    Qpopper とのコネクションは inetd が受け付けるようにしているので,/etc/inetd.conf 中の POP3 のエントリを以下の通りの内容で記述しておく。

    pop3  stream  tcp  nowait  root  /usr/local/libexec/qpopper  qpopper -s
    

    SpamAssassin については,FreeBSD 8.2-RELEASE の現時点での最新 ports /usr/ports/mail/p5-Mail-SpamAssassin/ から導入を試みると,ライブラリのバージョンで齟齬を起こしてうまく make できなかった。調べるのも面倒なので,日本語版 ports /usr/ports/japanese/p5-Mail-SpamAssassin/ を試してみるとうまくインストールできた。ただし,この日本語版 ports は日本語処理に MeCab を活用するパッケージになっていて,これを自動的にインストールしてくれるのだけど,UTF-8 で MeCab を調整するよう要求してくる。そこで以下のように MeCab を UTF-8 で導入したあとで SpamAssassin を組込むとよいと思う。

    # cd /usr/ports/japanese/mecab && make WITH_CHARSET=utf8 install clean
    # cd /usr/ports/japanese/mecab-ipadic && make WITH_CHARSET=utf8 install clean
    # cd /usr/ports/japanese/p5-MeCab && make install clean
    # cd /usr/ports/japanese/p5-Mail-SpamAssassin && make install clean
    

    メール環境の復旧は何より sendmail.cf 設定ファイルのリカバリが命である。これをバックアップしておけばよいのであるが — 愚かにも — 今回バックアップのなかに見当たらなかった。それで一から設定することに。

    最近の FreeBSD では sendmail.cf の生成は,/etc/mail 下で m4 マクロで元ネタ(mc ファイル)を記述し make することにより行うのが一般的のようである。私のサーバ yasuda.homeip.net 用の mc ファイルとして beatrice.yasuda.homeip.net.mc を以下の通り準備した。

    divert(0)
    VERSIONID(`...')
    OSTYPE(freebsd6)
    DOMAIN(generic)
    MASQUERADE_AS(`yasuda.homeip.net')
    MASQUERADE_DOMAIN(`$j')
    EXPOSED_USER(``daemon news usenet postmaster MAILER-DAEMON'')
    FEATURE(`limited_masquerade')
    FEATURE(`masquerade_envelope')
    FEATURE(`allmasquerade')
    FEATURE(access_db, `hash -o -T /etc/mail/access')
    FEATURE(blacklist_recipients)
    FEATURE(local_lmtp)
    FEATURE(mailertable, `hash -o /etc/mail/mailertable')
    FEATURE(virtusertable, `hash -o /etc/mail/virtusertable')
    define(`confCW_FILE', `-o /etc/mail/local-host-names')
    define(`confMAX_MIME_HEADER_LENGTH', `256/128')
    define(`confNO_RCPT_ACTION', `add-to-undisclosed')
    define(`confPRIVACY_FLAGS', `authwarnings,noexpn,novrfy')
    dnl j,{if_addr},{cert_subject},i,{auth_authen} are already enabled by default
    define(`confMILTER_MACROS_HELO', confMILTER_MACROS_HELO``, {verify}'')
    define(`confMILTER_MACROS_ENVRCPT', confMILTER_MACROS_ENVRCPT``, {greylist}'')
    INPUT_MAIL_FILTER(`greylist', `S=local:/var/milter-greylist/milter-greylist.sock, F=T, T=R:30s')
    MAILER(local)
    MAILER(smtp)
    

    私自身の備忘録なので細かいことは省略する。最後行から 3--5 行は Milter-Greylist のための記述である。一般的な記述は freebsd.mc に書かれているので,ドメイン名などを自分用に修正すれば基本的によいはずである。書き方の詳細は /usr/share/sendmail/cf/README に記載されている。

    mc ファイルができたら,sendmail.cf を make する。

    # cd /etc/mail
    # make SENDMAIL_MC=/etc/mail/beatrice.yasuda.homeip.net.mc all install
    

    さらに /etc/rc.confsendmail_enable="YES" を追加する。これを行わないと,sendmail は localhost 以外のコネクションを拒否する設定で起動するので注意。詳細は man rc.sendmail を参照。これで,リブートすれば壊れる前と同様に sendmail が動作する。

    スパムの踏み台にならないか一応チェックしておくとよい。http://verify.abuse.net/relay.html などで不正な第三者中継のセキュリティホールを診断できる。このサイト・フォームの Address to test: にメールサーバ名を入力して test for relay ボタンをクリックすれば診断を開始する。All tests performed, no relays accepted. が最後に出力されればよいと思う。
     

    2. Movable Type

    本ブログの管理システム Movable Type 4.1 (以下 MT) のリカバリについては,ブログ情報を格納した SQLite3 DB と Web MT data ツリー,cgi-bin ツリーすべてが,壊れたディスクから復元できた。このため,リカバリ作業はこれらのツリーの復元(バックアップアーカイブの解凍),SQLite3 のインストール,そしてMT 動作のために必要な Perl モジュールの追加ということになる。

    まず Web サーバ Apache22 環境を構築しておく。その後バックアップから MT ツリーを復元する。SQLite3 がインストールされているか確認し,なければインストールしておく。そのあとでブラウザから mt-check.cgi にアクセスすると,MT が前提とする Perl モジュールの検出状況が出力される。不足しているものを ports で追加するか,cpan コマンドもしくは perl -MCPAN -e shell でインストールする。再度 mt-check.cgi を実行して動作準備完了が確認できれば,mt.cgi にアクセスする。以前の通り使用可能になるはずである。

    自宅サーバ復旧

    |

    土曜日,自宅のサーバが故障した。NFS マウントしたビデオデータを Mac OS から観ていると頻繁に停止する。挙げ句,サーバ接続が切断される。FreeBSD サーバのログを確認すると,LBA read に失敗した旨のエラーメッセージが大量に出力されている。南無三宝,ディスクがぶっ飛んだらしい。サーバをリブートしたところ,ハードウェアの probe のあと fsck で回復不能エラーを検知して起動が停止してしまった。「エッチな映画ばかり観てるからバチが当たったのよ」とは妻。正しい。教訓その1:NFS サーバにエッチな映画を入れて楽しむのはやめよう。

    ディスクを買って来て,交換し,FreeBSD をインストールし,Apache やらなにやら大量のプログラムをインストールし,サーバ環境を整え,... と考えると気が遠くなった。しかしメールサーバが使えなくなるとやっかいか,ブログもパーか,などなど考えるにつけ,しようがないなーとサーバ復旧に着手したのである。おかげでこの連休はほとんどパーになってしまった。

    日曜日,娘の学園祭を見に行ったあと,川崎駅前ヨドバシカメラで Hitachi Global Storage Technology 社製の SATA2 7200 rpm 1TB HDD バルク品を 5,000 円足らずで購入。帰宅して,サーバ筐体を開けて HDD を交換しようとしたら,なんと壊れた既設の HDD が取り外せない。このベアボーンは,HDD を装着したネジを覆うように,特殊な方法で電源を固定していたのである。「じゃ増設」と考えたんだけど,予備の SATA 用電源コネクタがひとつも空きがない。しかも,SATA 用ケーブルもない。私の知らないうちに(PC を自分で組立てるなんてもう 15 年くらいやっていない) PC の世界も当然ながら様変わりしているわけで,内蔵 HDD の電源コネクタはいまや 4pin ならず,マザーボードとの接続も IDE ケーブルならぬ SATA ケーブルなんである。昔悩んだ IDE プライマリ/セカンダリのジッパ設定から解放されたのは嬉しかったんだけど。ふたたびヨドバシカメラに逆戻りして,4pin-SATA 電源変換ケーブル,SATA ケーブルを追加購入しなければならなかった。教訓その2:内臓周辺機器を増設するときは,あらかじめ筐体内部を確認し,追加の必要なケーブル類を洗い出しておこう。

    20110920-pcsata.jpg
    4pin-SATA 電源変換ケーブル(上)/SATA コネクタ(下)

    取得してあったシステムのバックアップは何ヶ月か前のものであり,ブログをはじめ日々更新されるデータは最新状態ではない。教訓その3:バックアップはこまめに取得し DVD-R などに焼いておこう。ディスクを「増設」するついでに,壊れた HDD 内のまだ生きているデータを漁って,使えるものは使おうということにした。今日はその苦肉の方法のメモを残しておく。

    まずは SATA のプライマリソケットに再度旧 HDD をつないでブートする。当然,fsck で悲鳴を上げて,次のメッセージとともに停止する。

    Automatic file system check failed; help!
    ERROR: ABORTING BOOT (sending SIGTERM to parent)!
    Sep 18 16:03:45 init: /bin/sh on /etc/rc terminated abnormally, going to user mode
    Enter full pathname of shell or RETURN for /bin/sh:
    

    help! は機械のお前じゃなくこっちのセリフじゃ! これに対し,Enter キーを押して,シングルユーザモードでログインする。自作プログラム,文書,ブログ記事など大事なデータを格納しているディスク・パーティションをマウントしてみる。/etc/fstab を参照し,デバイス名とマウンティングポイントを特定する。これらは利用者のインストールの仕方によって変わるので注意。

    # cat /etc/fstab
    # Device  Mountpoint FStype Options Dump Pass#
    /dev/ad4s1b  none    swap   sw      0    0
    /dev/ad4s1a  /       ufs    rw      1    1
    /dev/ad4s1g  /home   ufs    rw      2    2
    /dev/ad4s1h  /shared ufs    rw      2    2
    /dev/ad4s1f  /tmp    ufs    rw      2    2
    /dev/ad4s1d  /usr    ufs    rw      2    2
    /dev/ad4s1e  /var    ufs    rw      2    2
    /dev/acd0 /cdrom  cd9660 ro,noauto  0    0
    linprocfs /compat/linux/proc linprocfs rw 0 0
    

    私の目的とするデータは /usr/home にあるので,この二つだけをもう一度 fsck でチェックし,そのあとで mount してみる。幸いにもこれらパーティションは無事だったようである。vi などのツールは /usr/bin にあるので,それらツールを使う場合もマウントしておく必要がある。

    # fsck -y /dev/ad4s1d
    # fsck -y /dev/ad4s1g
    # /sbin/mount /dev/ad4s1d /usr
    # /sbin/mount /dev/ad4s1g /home
    

    必要なデータをアーカイブする(ここでは省略)。上記マウントができれば当該ファイルシステムは書き込みも可能なはずである。ルートパーティションはおそらく書き込み不可なので,アーカイブはマウントしたファイルシステムに格納する。アーカイブデータを別のコンピュータにコピーするには,ネットワークが使えないといけない。この時点ではネットワーク通信の準備ができていないので,次にこれを行い,取得したデータを scp (ftp でもよい) で別マシンに転送する。以下では anotherpc のユーザ user のホームディレクトリにコピーしている。anotherpc 上でも ssh が動作可能でなければならない。Mac OS など UNIX 系 OS なら OK である。

    # ifconfig msk0 inet 192.168.1.4 netmask 255.255.255.0
    # scp mydata-archives.tar.gz user@anotherpc:/Users/user/
    

    msk0 はネットワークインタフェース名であり,利用する LAN カードによって変わって来る。忘れてしまっていたら,/etc/rc.conf 中に ifconfig_XXX=... なる行があるはずなので,その XXX をネットワークインタフェース名に指定する。IP アドレスは使っていたものをそのまま使うのがよい。詳細は ifconfig のマニュアルを参照。

    必要なデータをすべて別マシンに転送し終わったら,ほっと一息。煙草で一服し,コーヒーでも飲もう。シャットダウンし,電源コードを抜く。新しく追加した HDD の SATA ケーブルをプライマリに,旧い HDD のケーブルを DVD ドライブの次のソケットあたりに繋ぎ直す。ついでに筐体内部に溜まりまくったホコリをエアスプレーで吹き払っておく。とくに冷却ファンの近辺を念入りに。これで PC の騒音がピタリと止む。

    ここからは新しい HDD に最新バージョンの FreeBSD を導入する作業となる。私は Mac で,FreeBSD 8.2-RELEASE の DVD イメージをダウンロードし,ディスクユティリティで DVD-R に書き込んで,このメディアでインストールを行った。とにかく真っ先に sendmail,POP 環境を整えた。その後,上で取得したコピーデータを再度新環境に転送してからもろもろの復旧を行った。まだまだ入れ込めていないプログラムもたくさんあってうんざりである。もう二度とやりたくない。けれども,3 年に一度はやるはめになる。次はもうやる気力が起こらないかも知れない。教訓その4:自宅でサーバ運用なんてやめたほうがよい。

    最後に。警告その1:上記方法がいつもうまく行くとは限らない。 
     

    P.S.

    復旧中に,misima 旧字旧仮名変換支援サービスを使ってくれている友人から「つかえなくなっちゃったけど,どうしたの? 体調でも悪くしてサーバ運用やめちゃったの?」とのメールをもらった。ありがとう。四苦八苦しながらもリカバリしましたよ! 教訓その5:へたに自作プログラムの公開などしないほうがよい。

    下付ルビ

    |

    芭蕉俳句に関する文書を epLaTeX で作成していて,岩波文庫『芭蕉俳句集』p. 47 に出て来る「闇夜きつね下はふ玉眞桑」(妖気漂う闇の夜きつねが好物の真桑瓜に忍び寄る。男の少しおどろな夜這物語の風情がある句)をどう組むか悩んだ。この句は,「闇夜」の右側に「ヤミノヨト」,左側に「スゴク」とルビが振られていて,「やみのよとすごくきつねしたはふたままくは」と読む。「やみのよとすごく」と二度読みする(音で読んだものをさらに訓で読み下す)こういう漢文訓読法を「文選読み」というそうである。右側のルビは通常のルビ付けマクロを使えばよいが,左側に — 横書きスタイルなら下側に — ルビを付加する命令は,聞いたことがない。そんなに難しくなさそうなので,マクロを書いてみることにした。下付ルビ命令 \uruby の定義は以下のとおり。

    \newcommand\uruby[3][0zw]{\leavevmode
      % 親文字とルビの寸法を取得
      \setbox0=\hbox{#2}\setbox1=\hbox{\tiny #3}%
      % 幅の大きいほうの寸法を \dimen0 に格納
      \ifdim\wd0>\wd1 \dimen0=\wd0\else\dimen0=\wd1\fi
      % \dimen1 に「ルビ高さ+深さ+間隔値」(下にずらす量)を設定
      \dimen1=\ht1 \advance\dimen1 \dp1 \advance\dimen1 #1\relax
      % \dimen0 の幅で親文字を出力し,ルビを \dimen1 寸法だけ下に下げる
      \hbox to\dimen0{\hfil#2\hfil}%
      \kern-\dimen0\raise-\dimen1\hbox{\vbox{\hbox to\dimen0{\tiny #3}}}}%
    
    20110702-uruby.jpg

    \uruby[間隔値]{親文字}{ルビ} の書式で使用する。「間隔値」とは,親文字の下へのルビの移動量「ルビ文字の高さ+深さ」をさらに増量させたいとき,その寸法値を指定する。通常のルビ命令は奥村先生の okumacro.sty に定義されており,これと併用すれば課題は解決できることになる。例を以下に示す。

    % -*- coding: utf-8; -*-
    \documentclass[12pt]{tarticle}% 縦書き
    \usepackage{okumacro}% 奥村先生のマクロ集
    % 下ルビ命令: \uruby[間隔値]{親文字}{ルビ}
    \newcommand\uruby[3][0zw]{\leavevmode
      \setbox0=\hbox{#2}\setbox1=\hbox{\tiny #3}%
      \ifdim\wd0>\wd1 \dimen0=\wd0\else\dimen0=\wd1\fi
      \dimen1=\ht1 \advance\dimen1 \dp1 \advance\dimen1 #1\relax
      \hbox to\dimen0{\hfil#2\hfil}%
      \kern-\dimen0\raise-\dimen1\hbox{\vbox{\hbox to\dimen0{\tiny #3}}}}%
    \makeatletter
    \def\kanjistrut{\vrule \@height0.66zw \@depth0.12zw \@width\z@}
    \makeatother
    \begin{document}
    % 『芭蕉俳句集』岩波文庫,p. 47.
    \uruby[4pt]{\ruby{闇 }{ヤミノ}\ruby{夜}{ヨト}}{スゴク}きつね下はふ玉眞桑
    \end{document}
    

    奥村先生の \ruby 命令は,縦書きスタイルで使用するとルビが少し右に離れすぎてしまう。これは親文字とルビとの間に噛ませた箍 \kanjistrut を調整すればよい。また \uruby も縦書きのときは「間隔値」を指定して,もう少し左に(つまり,横書きスタイルでは下に)ずらずようにした方がよい。上記の原稿ではこの二点の調整を行っている。処理結果は右図のとおりである。

    FreeBSD kern.maxdsiz

    |

    全文検索エンジン msearch で,うちのメールアーカイブをインデックス対象として 20000 通を越える大量データを処理したところ,Out of memory during request for xxxxxx bytes, total sbrk() is ... 云々のメッセージを出力して,インデックス作成が abend してしまった(abend は「夕べ」という意味のドイツ語ではなく,Abnormal End = 異常終了というコンピュータ用語である。為念。昔,夕方になると一日の業務のゴミが原因で abend するシステムバグの対策をさせられたことがあり,Abend abend と呼んでいた。関係ありませんけど)。

    これは FreeBSD の管理するプロセスのデータセグメントのサイズの制限を超過したときに出るメッセージで,プログラムが自分の仮想メモリに大量データを抱え込んでいると,この事態に陥る可能性が出て来る。ま,制限値を増やせばいいのだけれど,20000 文書程度で出てしまうのもちょっとなんとかならないかなぁと思う。文書数が増えるといずれまた再発するのは確実だからである。Google は 20 億文書インデックスを達成したとき誇らしげにその広告を出していたが,確かに誇るべき数字である。とはいえ,情報検索の世界では数千万件のデータベースは最近では常識的な規模である。高々 20000 文書で OS のメモリ制限に引っ掛かるのは改善できないものか。

    とにもかくにも対策。sysctl -a | grep maxdsiz で現状値を確認すると,536870912 だと知れ,約 500MB。だいたいその 2 倍くらいにチューンしてみる。/boot/loader.conf に次の 1 行を書き足してリブートする。

    kern.maxdsiz="1024M"
    

    sysctl で再確認すると,1073741824 に値が拡張されていた。これで msearch genindex.pl がうまく通るようになった。

    PFonts Cyrillic LaTeX fonts package

    |

    CyrTeX-ru で PFonts の話題が上がっていた。キリル・ポストスクリプトフォントとしては PSCyr が有名であるが,「PSCyr はフリーでないし,品質がちょっと」という意見とともに,PFonts が紹介されていた。PFonts はフリー,かつ 16 種類のファミリ(書体)を揃えている。私も早速インストールして試してみた。

    インストールは,http://narod.ru/disk/12251041001/PFonts.7z.html から PFonts.7z を落として来て,p7zip で解凍し,TeX ツリーにコピーし,マップ登録すればよい。ダウンロード後のオペレーションは以下の通り。

    % mkdir -p PFonts
    % 7za x -oPFonts ~/Downloads/PFonts.7z
    % cd PFonts
    % su -m
    # setenv TEXDIR /usr/local/texlive/texmf-local
    # tar cf - . | ( cd $TEXDIR; tar xvf - )
    # mktexlsr
    # updmap-sys --enable Map=pfonts.map
    

    サンプル文書 (pfonts-test.tex) を以下に掲げておく。\usepackage[scale=スケール]{AcademyP} というような行が各書体の指定である。このマクロのなかで \rmdefault, \sfdefault, \ttdefault を再定義することにより書体が選択される仕組みである。複数の書体を切替えて使う場合,サンプルにあるとおり,\fontfamily{ファミリ名}\selectfont とする。書体に対応するファミリ名はサンプル文書に記してある。

    % -*- coding: utf-8 -*-
    % PFonts test sample, coded by Isao Yasuda, May 10, 2011. 
    \documentclass[a4paper]{article}
    \usepackage[utf8]{inputenc}
    \usepackage[T2A,T1]{fontenc}
    \usepackage[russian]{babel}
    \usepackage[scaled=1.00]{AcademyP}%
    \usepackage[scaled=1.00]{BalticaP}%
    \usepackage[scaled=1.00]{BodoniP}%
    \usepackage[scaled=1.00]{CooperP}%
    \usepackage[scaled=1.00]{CourierP}%
    \usepackage[scaled=1.00]{JournalP}%
    \usepackage[scaled=1.00]{JournalSansP}%
    \usepackage[scaled=1.00]{LazurskiP}%
    \usepackage[scaled=1.00]{LetterGothicP}%
    \usepackage[scaled=1.00]{LiteraturnayaP}%
    \usepackage[scaled=1.00]{NewStandardP}%
    \usepackage[scaled=1.00]{NewtonP}%
    \usepackage[scaled=1.00]{PetersburgP}%
    \usepackage[scaled=1.00]{PragmaticaP}%
    \usepackage[scaled=1.00]{QuantAntiquaP}%
    \usepackage[scaled=1.00]{TextbookP}%
    \renewcommand{\rmdefault}{tdy}%
    \def\sample{%
    Прежде всего откроем тайну которую Мастер не пожелал
    открыть Иванушке.
    Возлюбленную его звали Маргаритою Николаевной.
    Все, что Мастер говорил о ней, было сущей правдой.
    Он описал свою возлюбленную верно.
    Она была красива и умна.}%
    \long\def\chfam#1#2{%
      \vspace{11pt}%
      \bgroup\fontfamily{#1}\selectfont#2\par\sample\par\egroup}%
    \pagestyle{empty}
    \begin{document}
     
    \begin{center}
    \LARGE PFonts test
    \end{center}
     
    Font-name: Type Family-name;\qquad
    Text: \textit{М. А. Булгаков} <<Мастер и Маргарита>>.
     
    \chfam{tdy}{Academy: rm tdy}
    \chfam{tl5}{Baltica: rm tl5}
    \chfam{tbd}{Bodoni: rm tbd}
    \chfam{tcb}{Cooper: sf tcb}
    \chfam{tco}{Courier: tt tco}
    \chfam{tjr}{Journal: rm tjr}
    \chfam{tjs}{JournalSans: sf tjs}
    \chfam{tla}{Lazurski: rm tla}
    \chfam{tlg}{LetterGothic: tt tlg}
    \chfam{tln}{Literaturnaya: rm tln}
    \chfam{tns}{NewStandard: rm tns}
    \chfam{tnw}{Newton: rm tnw}
    \chfam{tp7}{Petersburg: rm tp7}
    \chfam{tpg}{Pragmatica: sf tpg}
    \chfam{tqa}{QuantAntiqua: rm tqa}
    \chfam{ttx}{Textbook: sf ttx}
     
    \end{document}
    

    pdflatex による処理結果(一部)は次のようなものである。処理結果 PDF (pfonts-test.pdf) もリンクしておくので参照いただきたい。

    20110509-pfonts.jpg

    PFonts は T2A, T1, TS1 フォントエンコーディングしかサポートしていない。PSCyr-0.4-beta は一部フォントについて T2D をサポートしているのと比べると残念である。PFonts のポストスクリプトフォントのグリフ一覧を fontforge で確認すると ѣ Ѳ Ѵ のグリフがきちんと揃っているのに,LaTeX PFonts では使えないわけで,これはもったいない。CyrTeX-ru で作者に T2D サポートの要望を出しておいた。

    馬込文士村散歩

    |

    Movable Type 5.1 RC が出たので,ちょっと試してみた。でもカスタマイズをもう一度反映するのにひどく手間がかかりそうだったので,Ver. 4 に戻すことに。ところが,とってあったバックアップからの復元がエラーになり,ハマリまくった。Movable Type のバックアップ・ツールは信用しないほうがよい。Web 環境全体をごっそりコピーするほうがシンプルにして確実である。

    Facebook でロシアの現代女流詩人オリガ・セダコヴァからメッセージをもらった。彼女の詩集や紀行文,プーシキン関係論文,教会スラヴ語関連学術書を読んで,私は彼女の作風に強く惹かれていたので,信じられないくらいうれしかった。インターネット時代となり,友達の友達を六段階くらい辿るとたいていの人が繋がり合うという話があるけれども,ひと昔前ならこんなことは考えられなかった。

    また,同じく Facebook で,ロシアの作曲家ガリーナ・グリゴーリエヴァから,すみだトリフォニーホールで開催される東京カンタート・クロージング・コンサートへの招待があった。彼女はこの催しに招かれて来日するという。そこで彼女の合唱曲『生の夜』が演奏されるんである。「ぜひお会いしましょう」とのこと。こちらもうれしくてならない。だけど,当日はすでに別の用事が入っており,どうしようか悩んでいるところである。
     

    * * *

    連休に入り,妻と馬込文士村周辺を散歩した。大田区馬込・大森山王界隈は,かつて,萩原朔太郎,室生犀星,北原白秋,日夏耿之介,吉屋信子,三好達治,北園克衛,和辻哲郎などなど,錚々たる作家・詩人たちが住まったことで,馬込文士村と呼ばれている。若き日の川端康成,三島由紀夫も住んでいたらしい。大田区郷土博物館で,文士ゆかりの品々を見学した。北原白秋や日夏耿之介,萩原朔太郎の自筆原稿,初版本など,興味深かった。
     

    20110501-bunshimura.jpg

    左は大森駅山王口すぐにある馬込文士村の作家・詩人たちのレリーフ。ちょっとゾンビみたいで怖い。夜にはこの前を歩きたくないものである。右は博物館にあった日夏耿之介の雑誌『戯苑』のカバー。
     

    20110501-hinatsu-tanka.jpg

    この詩歌集にあるような活字の質感は,いまや失われてしまった。日夏耿之介の『黒衣聖母』の黝い浪漫主義には,あの凹凸感のある旧字体活字こそが相応しい。彼の詩は,紋切型のおどろな物語性がなんとも鼻に付くのだが,それゆえにこそ私にとっては堪らない魅力がある。

    怕る怖る眺めあげる
    黄金色の礼拝壇の中央には
    端厳美麗の永貞女彳みたまふ
    その眼前の赤蠟燭の大柱の
    とぼとぼと燃えさぐる灯の睛のいろは
    眼疾を疾んだのか
    気弱い灯影疼みただれて
    白い泪はしめじめと
    紅艷の燭柱の素肌を泫れる
    悪 いかほど淫慘な柔肌か
    日夏耿之介『蠱惑の人形』—『日本の詩歌』12,中公文庫,1976 年,115 頁。

    佐藤惣之助のコーナーに,『六甲颪』,そう,あの阪神タイガースの応援歌の詞が掲げられていた。佐藤の作詞によるもの。プロ野球がやっと開幕,楽しみがまた増えたわけであるが,残念ながら,どうやら今年もわが真弓阪神タイガースはダメ虎のようである。おまけに金本アニキの連続出場記録も,どっちらけの終焉。アニキを温存せぇとは言わんけどやで,配慮が足らなさ過ぎやおまへんか。「意を決して」,ではなく,「判断ミス」で記録が途絶えた,なんていったいなんなんだ。アニキがカワイソ。なんの覚悟もない,ブレーン任せの判断誤りばかりで,まったく存在感,リーダシップが感じられないこと — 真弓監督,菅総理の共通点である。

    その他,博物館展示品では,坪田譲治宛萩原朔太郎書翰の,罫を無視した大胆な筆跡が印象的であった。それにしても,昔の詩集・小説本は装丁が奇抜で,工芸的意匠に溢れているんである。

    文士村散歩といっても,周辺は細い道が入りくねった閑静な高級住宅街であって,地図を片手に歩いても,番地をたよりに道を探すのに苦労し,お金持ちの家が眼につくばかりで,なんの面白みもなかった。北園克衛が住んでいたという場所を探したのだけれども,まるでわからなかった。12,000 歩ばかり歩いて適度な運動をして来たという感じ。

    大森駅に着いたらハラがペコペコ。喜多方ラーメン坂内食堂を見つけ,ラーメンと餃子を食った。アサヒ・スーパードライが腹に沁みて旨かった。
     

    The One Ring, Tengwar fonts

    |

    J. R. R. トールキンの『指輪物語』にテングワールという謎めいた文字が出て来る。架空の言語は謎めいたファンタスティックな世界の完結性をこの上もなく表わしている。あの恐ろしい力を持つ The One Ring にモルドール語で刻印された火文字を,LaTeX tengwarscript パッケージで組んでみた。

    20110418-mordor.jpg

    One ring to rule them all, one ring to find them,
    One ring to bring them all and in the darkness bind them.
    一つの指輪は,すべてを統べ,一つの指輪は,すべてを見つけ,
    一つの指輪は,すべてを捕えて,くらやみのなかにつなぎとめる。
    J. R. R. トールキン『指輪物語1』瀬田貞二・田中明子 訳,評論社,1992年,p. 112.

    tengwarscript は CTAN: macros/latex/contrib/tengwarscript/ にある。バージョンは 1.3。フォントは何種類も出回っているが,私は AnnatarFormal だけを組込んで試してみた。上の画像は Annatar Italic である。流麗でなんとも美しい。原稿は以下のとおり。このモルドール語テクストはパッケージ添付 tengtest.tex にサンプルとして掲載されている。これを pdflatex で処理した。

    % -*- coding: utf-8; -*-
    % Tengwar on The One Ring from "The Load of the Ring" 
    \documentclass[12pt]{article}
    \usepackage[pdftex]{color}
    \usepackage[T1]{fontenc}
    \usepackage[all]{tengwarscript}
    \definecolor{charc}{cmyk}{0,0.77,0.87,0}%
    \begin{document}
    \pagestyle{empty}
    \color{charc}
    \begin{center}
    \tengwarannataritalic[2.0]
    \tengwa{254}
    \Textendedcalma\TTthreedots\Tnuumen\Tessenuquerna\TTthreedots%
    \Tungwe\Tando\Toore\TTrightcurl\Tumbar\Ttinco\TTthreedots%
    \Tlambealt\TTrightcurl\Tquesse\TTdoublerightcurl
    \Tromanperiod\Ts
    \Textendedcalma\TTthreedots\Tnuumen\Tessenuquerna\TTthreedots%
    \Tungwe\Tungwe\Tumbar\TTnasalizer\TTdot\Ttinco\TTthreedots%
    \Tlambe\TTrightcurl
    \tengwa{255}\\
    \Textendedcalma\TTthreedots\Tnuumen\Tessenuquerna\TTthreedots%
    \Tungwe\Tthuule\Troomen\Tquesse\TTthreedots\Ttinco\TTthreedots%
    \Tlambealt\TTrightcurl\Tquesse\TTdoublerightcurl
    \Tromanperiod\Ts
    \Textendedungwe\TTthreedots\Tumbar\Toore\TTrightcurl%
    \Tesse\Tkern{-0.2}\Tmalta\TTrightcurl%
    \Textendedcalma\TTdot\Ttelco\TTdot\Tquesse\Troomen\Tparma%
    \TTnasalizer\TTdot\Ttinco\TTthreedots\Tlambe\TTrightcurl
    \end{center}
    \end{document}
    

    tengwarscript のインストールは通常の LaTeX パッケージ組込となにも変わらない。ダウンロードした tengwarscript リソースを TDS に準拠して TeX ツリーに格納し,tengwarscript.mapupdmap-sys で登録する。フォントもほぼ同じだけれど,Formal については TrueType フォントファイル名を tengwarscript.map の記載に合わせてリネームする必要がある(もちろん map を訂正してもよい)。テングワール文字の出力命令については,ドキュメント tengwarscript.pdf を参照。
     

    ※ 4.20 付記

    一応,端末上のインストール・オペレーションもメモっておく。端末操作は tcsh シェルである。TeX ツリーはローカル texmf とし,そのトップ・ディレクトリ名称を /usr/local/texlive/texmf-local としている。

    まず,作業ディレクトリ(ここでは ~/tmp/tengwar)で tengwarscript パッケージをダウンロード,展開し,dtx, ins ファイルを latex 処理する。dtx はドキュメントでもあり,以下の操作ではリファレンス解決のため 2 回実行している。もとより PDF はすでに添付されているのできちんとした dvi ファイルが不要ならば,1 回の処理でもよい。

    % mkdir -p ~/tmp/tengwar
    % cd ~/tmp/tengwar
    % set WGET="wget -nH -nd"
    % set TEXDIR="/usr/local/texlive/texmf-local"
    % $WGET http://mirror.ctan.org/macros/latex/contrib/tengwarscript.zip
    % unzip tengwarscript.zip
    % cd tengwartscript
    % latex tengwarscript.dtx
    % latex tengwarscript.dtx
    % latex tengwarscript.ins
    

    次に,Annatar 及び Formal フォントをダウンロード,解凍する。Formal フォントについては,map の記述に合うようにファイル名を変更する。

    % $WGET http://home.student.uu.se/j/jowi4905/fonts/tngan120.zip
    % unzip tngan120.zip
    % $WGET http://tengwarformal.limes.com.pl/fonts/TengwarFormal-12c-ttf-pc.zip
    % unzip TengwarFormal-12c-ttf-pc.zip
    % cd TengwarFormal-12c-ttf-pc/fonts/
    % mv TengwarFormal12b.ttf TengwarFormal12.ttf
    % mv TengwarFormalA12b.ttf TengwarFormalA12.ttf
    

    su -m で特権ユーザとなり,上で展開したリソースを TeX ツリーに格納し,フォントマップ登録を行う。

    % cd ~/tmp/tengwar/tengwarscript
    % su -m
    # mkdir -p $TEXDIR/{doc/latex,tex/latex,fonts/{tfm,vf,type1,truetype,map/dvips,enc/dvips}}/
    tengwarscript
    # cp -p *.pdf $TEXDIR/doc/latex/tengwarscript/
    # cp -p *.cfg *.sty $TEXDIR/tex/latex/tengwarscript/
    # cp -p tfm/* #$TEXDIR/fonts/tfm/tengwarscript/
    # cp -p vf/* $TEXDIR/fonts/vf/tengwarscript/
    # cp -p map/* $TEXDIR/fonts/map/dvips/tengwarscript/
    # cp -p enc/* $TEXDIR/fonts/enc/dvips/tengwarscript/
    # cp -p *.ttf $TEXDIR/fonts/truetype/tengwarscript/
    # cp -p TengwarFormal-12c-ttf-pc/fonts/*.ttf $TEXDIR/fonts/truetype/tengwarscript/
    # mktexlsr
    # updmap-sys --enable Map=tengwarscript.map
    

    以上。

    Moon Calendar

    Profile

    ISAO YASUDA。システムエンジニア。神奈川県在住。昭和 30 年代を懐かしむオヤジ。ロシアに興味があります。
    [more], [About our site]

    Notice

    この文書はフィクションであり,実在する個人,団体等とは一切関係ありません。

    R-18 指定サイトです。そのうち「18 歳以上ですか」の認証を入れる予定です。

    文書の記述内容は無保証です。不適切な表現があればコメントにてご指摘ください。

    コメント,トラックバックは,現在,運用を停止しています。ご意見等ありましたら isao@yasuda.homeip.net 宛電子メールにてお願いします。

    Links

    About this archive

    All Entries of Category 文房清玩

    Previous: 政治・動物園

    Next: 日曜大工

    Recent Entries in Main Index.
    All Entries in Archive Index.

    February 2012

    Sun Mon Tue Wed Thu Fri Sat
          1 2 3 4
    5 6 7 8 9 10 11
    12 13 14 15 16 17 18
    19 20 21 22 23 24 25
    26 27 28 29      
    Powered by Movable Type 4.1 blog counter