チャリンコ通勤もそろそろ寒くなってきたと感じる今日この頃のmikioです。今回は、manの書き方について述べてみます。

manとは

UNIX系のフリーソフトウェア/オープンソースソフトウェアを世に出す場合、その使い方を示した「man」形式のマニュアルを付属させるのが一般的です。端末上で「man hoge」とやると「hoge」のマニュアルを見ることができるので大変便利で、UNIXを使っている方は日々お世話になっている機構だと思います。ちなみに「man -t hoge」とやるとPostScript形式のデータが出力されるので印刷して見ることもできるんです。

そういうわけでUNIXのソフトウェアはmanをつけて配布するのがあたりまえ的になっていて、つけてないと「なんでやねん」とお叱りをうけることもあります。Debian/GNU Linuxでは、パッケージに含まれる全てのコマンドには各々に対応するmanファイルをつけるべきであると決められているくらいです。

データ形式

manで閲覧するからman形式と便宜的に呼んでいるわけですが、正確にはroff(実際にはnroff/troff/groff)という文書整形コマンドのマクロパッケージ「an」ないし「andoc」がサポートするデータ形式です。roffファミリがマクロパッケージを読み込ませる時の引数が「-m」なので、実際には「-man」とか「-mandoc」とかいう指定になって、マニュアルなんだなとわかりますね。man形式の詳しい書式についてLinux上で「man 7 man」を実行すると説明を見ることができます(Web版)。

さて、このman形式、実際に書いてみるとわかりますが、すげー面倒です。原始的なマークアップ言語のひとつだそうですが、SGML/HTML/XMLに比べるとものすごく書きにくく読みにくい形式なんです。最も簡単な例を見てみましょう。

.TH "HADOUKEN" 1 "2007-10-24" "Man Page" "Utility Commands"

.SH NAME
hadouken \\- launch wave propagation of fighting spilit

.SH SYNOPSIS
.B hadouken
[\\fB\\-f\\fR|\\fB-d\\fR] \\fItarget\\fR [\\fIlevel\\fR]

.SH DESCRIPTION
.PP
Launch wave propagation of fighting spilit toward an enemy and beat him.
\\fIenemy\\fR specifies the intended target.
\\fIlevel\\fR specifies the strength level from 1 (soft) to 3 (hadd).
\\fBGouki\\fR can do this blow in the air.

.SH OPTIONS
.TP
.B \\-f
switch to burning mode and daunt the ememy
.TP
.B \\-d
switch to electricity mode and fall the enemy unconscious

.SH SEE ALSO
.BR syouryuuken (1),
.BR tatsumakisenpuukyaku (1)

これを「hadouken.1」とかいう名前のファイルとして保存して、「less hadouken.1」とすると整形済みのページを見ることができます。

hadouken.png

「.TH」でメタデータを指定して、「.SH」で見出しを記述して、「.PP」でパラグラフの開始を指示するとかはまだいいでしょう。面倒なのは、ボールドにするために行を分けて「.B」するのとか、行中でボールドやイタリックにするためにエスケープシーケンス「\fB」「\fI」を使って、さらに元に戻すのに「\fR」とかやらねばならないことです。例えば、コマンドラインの書式として「tcucodec mime [-d] [-en name] [-q] [file]」を表そうとすると、「\fBtcucodec mime \fR[\fB\-d\fR]\fB \fR[\fB\-en \fIname\fB\fR]\fB \fR[\fB\-q\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR」とか書かなくちゃならないんです。こんなのいちいち手で入力してたら人生終わってしまいますよね。

HTMLからの変換

Tokyo CabinetのマニュアルはHTMLで記述しているのですが、上記の理由から同じ内容のman形式のデータもパッケージに入れないといけないので、気が進まないながらも「.TH」とか「.B」とか書いていた私です。しかし、いけどもいけどもルーチンワーク。こんなの自動化しなきゃ嘘ですよね。そもそもHTMLと同じ内容なんだからイチイチ手で書き直すなんて愚の骨頂じゃないですか。

ということで、コンバーターを書きました。上場企業なのにPHPを使わないで恐縮ですが、今回は小粒で便利なテキスト処理言語であるAWKを用います。

#! /bin/awk -f

function strip(text){
  gsub("^ *<[a-zA-Z0-9]*[^>]*>", "", text)
  gsub("</[a-zA-Z0-9]*> *$", "", text)
  return text
}

function unescape(text){
  gsub("&lt;", "<", text)
  gsub("&gt;", ">", text)
  gsub("&quot;", "\\"", text)
  gsub("&amp;", "\\\\&", text)
  gsub("-", "\\\\-", text)
  return text
}

BEGIN {
  date = strftime("%Y-%m-%d")
  printf(".TH \\"%s\\" %d \\"%s\\" \\"%s\\" \\"%s\\"\\n\\n", "INTRO", 3, date, "Man Page", "Tokyo Cabinet")
}

/ *<h[1-3] *[^>]*>.*<\\/h[1-3]> *$/ {
  text = $0
  text = strip(text)
  text = unescape(text)
  text = toupper(text)
  printf("\\n")
  printf(".SH %s\\n", text)
}

/ *<p *[^>]*>.*<\\/p> *$/ {
  text = $0
  text = strip(text)
  text = gensub("<code *[^>]*>([^<]*)</code>", "\\\\\\\\fB\\\\1\\\\\\\\fR", "g", text)
  text = gensub("<var *[^>]*>([^<]*)</var>", "\\\\\\\\fI\\\\1\\\\\\\\fR", "g", text)
  gsub("<[^>]*>", "", text)
  text = unescape(text)
  printf(".PP\\n")
  printf("%s\\n", text)
}

/ *<dl *[^>]*> *$/ {
  printf(".PP\\n")
  printf(".RS\\n")
}
/ *<\\/dl> *$/ {
  printf(".RE\\n")
}
/ *<dt *[^>]*>.*<\\/dt> *$/ {
  text = $0
  text = strip(text)
  text = gensub("<var *[^>]*>([^<]*)</var>", "\\\\\\\\fI\\\\1\\\\\\\\fB", "g", text)
  gsub("<[^>]*>", "", text)
  gsub("[\\\\||\\\\[|\\\\]]", "\\\\fR&\\\\fB", text)
  text = unescape(text)
  printf(".br\\n")
  printf("\\\\fB%s\\\\fR\\n", text)
}
/ *<dd *[^>]*>.*<\\/dd> *$/ {
  text = $0
  text = strip(text)
  text = gensub("<code *[^>]*>([^<]*)</code>", "\\\\\\\\fB\\\\1\\\\\\\\fR", "g", text)
  text = gensub("<var *[^>]*>([^<]*)</var>", "\\\\\\\\fI\\\\1\\\\\\\\fR", "g", text)
  gsub("<[^>]*>", "", text)
  text = unescape(text)
  printf(".RS\\n")
  printf("%s\\n", text)
  printf(".RE\\n")
}

/ *<ul *[^>]*> *$/ {
  printf(".PP\\n")
  printf(".RS\\n")
}
/ *<\\/ul> *$/ {
  printf(".RE\\n")
}
/ *<li *[^>]*>.*<\\/li> *$/ {
  text = $0
  text = strip(text)
  text = gensub("<code *[^>]*>([^<]*)</code>", "((\\\\\\\\fB\\\\1\\\\\\\\fR))", "g", text)
  text = gensub("<var *[^>]*>([^<]*)</var>", "[[\\\\\\\\fI\\\\1\\\\\\\\fR]]", "g", text)
  gsub("<[^>]*>", "", text)
  text = unescape(text)
  printf("%s\\n", text)
  printf(".br\\n")
}

END {
  printf("\\n")
  printf(".SH SEE ALSO\\n")
  printf(".PP\\n")
  printf(".BR tcutil (3),\\n")
  printf(".BR tchdb (3),\\n")
  printf(".BR tcbdb (3)\\n")
  printf(".PP\\n")
  printf("Please see\\n")
  printf(".I http://tokyocabinet.sourceforge.net/spex-en.html\\n")
  printf("for detail.\\n")
}

もちろん、こんな小さいプログラムには一般的なHTMLを完全にman形式に直す能力はありません。しかし、手間を減らすのが目的なのですから、自分のユースケースのサブセットを処理できればよいのです(時に「完璧」なものを作りたくなって湯水の如く時間を使ってしまうこともありますが…)。今回の場合は、Tokyo Cabinetのマニュアルに表れる、h1, h2, h3, p, dl, dt, dl, ul, li, code, var だけ処理できればOKです。

まとめ

そんなこんなで、man形式のマニュアルが無事に完成しました。UNIX系のソフトウェアの公開を継続して行っていくならば、manを書くスキルがおいおい必要になってくると思います。ただ、手で書くのはめちゃくちゃ面倒なので、HTMLやその他の形式で書いてから、プログラムで自動変換することになるでしょう。その際に楽するためには、論理的な構造だけはmanで期待されるものに近いようにしておくとよいでしょう。なお、変換にはdoxygenやpod2manなどの既存のツールを使うというのもよい考えです。

そうそう、Tokyo Cabinet 1.0.0がついにリリースされました。B+木やトランザクションも実装されて、QDBMに相当する機能を全てサポートして、性能はQDBMを完全に凌駕するようになりました。DBMに興味のある方にはぜひ使っていただきたいと思います。

週末にまた~りとコーヒーをすすりながらDavid Recordonのブログを読んでたら、Six ApartのRelationship Update Streamという先日、紹介させて頂いたOAuthを使っている面白いプロダクトに遭遇したのでエンジニアブログを使って共有したいと思います。

Relationship Update Streamとは

こいつがどういう代物かというと、例えばとあるオープンなウェブサービス上で公開されているユーザのRelationship情報に変化が生じた(e.g. User AがUser Bとマイミクになった)という情報をAtomを利用して他のサービスと共有ができるデベロッパーのためのプラットフォームの事です。Six Apart Update Streamと似ているけどSix Apartのサービスに特化しないところがミソですね。

利用方法

ストリームの利用方法は公式ページでかなり解り易く紹介されているので深く追求はしませんが軽く要約すと:

更新情報を入手するにはhttp://updates.elsewhere.im/atom-stream.xmlにHTTP GETリクエストを送る。接続が確立するとAtom Serverはクライエントにストリームに注入された情報、そしてタイムスタンプを毎秒送信します。タイムスタンプの存在意義は何らかの不都合で接続に問題が生じた場合にタイムライン上の特定の場所からリスナーを再接続するためとの事です。ここで注意すべき点が情報量が半端じゃないという事。耐えられないなら消火ホースから水を飲むなと紹介されてますね。

対して更新情報をストリームに注入するにはhttp://updates.elsewhere.im:8080/にOAuth認証でAtom FeedをHTTP PUTするだけと簡素。ここの注意点は公開設定のフィルターがまだ無いので公開されている関係情報だけを注入してくださいと注意書きがあります。そして今は誰でもストリームに情報を注入できるわけではないようです(予め連絡が必要と書いてる)。サーバへの負荷、そしてセキュリティを考えると仕方のない事かもしれませんね。

興味をもたれた方は是非、本家のページに目を通してみてください。これが熱いと思うのは私だけではないはず。

超まとめ

Relationship Update Streamは今月の19日にローンチされ、開始してから間もないプラットフォームですが既に様々なサービスが参戦する(source)との事で今後の動きが楽しみです。ODFの時間などを使って遊び倒したいものの一つですが、こういったものを使うなら何か趣味のレベルで面白いウェブサービスを作りたいものですね。

お久しぶりです、最近はすっかり寒くなってきましたねー。原宿のオフィス環境に最近慣れてきたトールマエサカです。さておき、今日は認証API系のお話をしたいと思います。

OAuthとは?

OAuthとは、最近注目されているウェブ上での認証プロトコルの事です。1.0プロトコルの最終ドラフトが、今月リリースされ、良い機会なのでエンジニアブログに書いてみました。

このエントリーを書いている課程で資料を検索してみたらまちゅさんが大変素晴らしい記事:

を既に書かれている事に気がついたので私は軽くなめる程度にします。

で、実際にOAuthがどういう風に役に立つかというと、例えばウェブサービスAがウェブサービスBから、サービスBでのユーザの認証情報(idとかpassword)を問わずにデータをユーザの代理として入手できるという感じで概要に紹介されてます。

OAuth Core 1.0 Draft 4からの引用ですが、以下の文が良い感じでOAuthを定義しています:

The OAuth protocol enables websites or applications (Consumers) to access Protected Resources from a web service (Service Provider) via an API, without requiring Users to disclose their Service Provider credentials to the Consumers. More generally, OAuth creates a freely-implementable and generic methodology for API authentication.

私の直訳:

OAuthプロトコルはウェブサイト、もしくはアプリケーション(コンシューマ)がAPI経由でユーザにサービスプロバイダ用の証明情報をコンシューマに提供させず、サービスプロバイダにより保護されたリソースにアクセスする事を可能にします。より一般的にいうと、OAuthはAPI認証を自由に実装できて、かつジェネリックな手法を実現します。

で、肝心なところはドラフトの一番最初にも書かれていますが、OAuthは特定のサービスにログインするための認証プロトコルではなくて、OAuthをサポートするサービス間(OAuthネットワーク)での連携を実現するためのプロトコルであるという事です。

なぜOAuthが必要か?

こういったプロトコルは世の中には既に存在します。ここで問題なのが数多く存在するウェブサービス同士の標準的なプロトコルが存在しないという事です。つまりサービスAのエンジニアはサービスAをサービスBと連携するためにサービスBの仕様を調査し、それ相応の行動を取らねばなりません。世の中にあるメジャーなウェブサービス達がまさにそうですね。

そこで、OAuthです。ドラフトに書かれている様に、OAuthコミュニティの目標は様々に存在するデータ交換に際する認証プロトコルを、コミュニティによってドライブされたプロトコルに統一する事です。ちなみに新しいプロトコルというよりかは既に存在する様々なプロトコルとベストプラクティスを参考にしているとドラフトに書かれています。実際にプロトコルを開発しているメンバー達はメジャーな組織の人たちばかり。SixApartでも前向きに応援してる様ですね。

プロトコルの内容

ドラフトは軽く読むには長いかと思いますが認証部分をビジュアライズするだけであれば、認証フローを見るだけで十分。

認証フローは以下の三つのステップに分かれていると表記されてますね:

  1. コンシューマが認証Request TokenとToken Secretを取得する
  2. ユーザがRequest Tokenを承認する
  3. コンシューマがRequest TokenをAccess Tokenと交換する

コンシューマがAccess TokenとToken Secretを入手してはじめてユーザの代理として守られた外部サービスのデータにアクセスする。説明が大雑把ですが、基本的に簡素です。

まとめ

OAuthのコンセプトは個人的には素晴らしいと思うのですが実際にはまだレビュー段階で何とも言えませんし、データアクセスプロトコル自体の標準化がまだ先に待ち構えていますし、これから新しく出来るウェブサービス達がOAuthを実際に採用するか?、そして昔から存在する老舗が採用するか?など色々と見所があります。最悪の想定だと既に幾つも存在するバラバラのプロトコルの一つになってしまう可能性もあるわけで、個人的にこういうネタが大好きなのでこれからが本当に楽しみです。