Mercurial > hg > Applications > mh
view conf/doc/ja-mh-format.rf @ 7:c20e4181370f
utf-8 input assumption in case of base64/utf-8
author | kono |
---|---|
date | Sun, 04 Dec 2005 02:30:39 +0900 |
parents | bce86c4163a3 |
children |
line wrap: on
line source
.\" @(MHWARNING) .\" written by MH-plus project .SC MH-FORMAT 5 .NA mh-format \- MH システムのフォーマット・ファイル .SY いくつかの \fIMH\fR コマンド .DE いくつかの \fIMH\fR コマンドは、実行中に フォーマット文字列またはフォーマット・ファイルを利用します。 例えば、 \fIscan\fR\0(1) はそれぞれのメッセージの一覧表示をどのように作成するかを 示すためにフォーマット文字列を使いますし、 \fIrepl\fR\0(1) はメッセージの返信をどのように作成するかを示すために フォーマット・ファイルを使います。 フォーマット文字列は \fIMH\fR によって能率的に解析されるよう設計されて いるので、必ずしも書いたり理解したりしやすいとは言えません。 したがって、初心者は、あるいは \fIMH\fR の上級ユーザーでさえも、 これを是非とも扱わなければならないというようなものではありません。 いくつかの scan フォーマットが @(MHETCPATH)/scan.time、@(MHETCPATH)/scan.size、そして @(MHETCPATH)/scan.timely として用意されています。 他の \fIscan\fR 及び \fIrepl\fR フォーマット・ファイルに関しては、 @(MHETCPATH) ディレクトリを見て下さい。 あるいは、それはあなたのサイトで書かれたものかも知れませんが。 実際に新しいフォーマットを書いたり、既存のものを編集したりする \fIMH\fR 熟練者があなたのサイトにいれば、 あなた自身が出来なくてもそれで充分です。 このマニュアルはそれをどうやるかについて説明しています。 なお、C 言語の \fIprintf\fR ルーチンについて熟知している事を前提にしています。 フォーマット文字列は通常のテキストと、`%' で始まる特別な複数文字の エスケープ・シーケンスから成り立っています。 フォーマット文字列では、 通常の C でのバックスラッシュ文字が使えます。 すなわち、`\\b'、`\\f'、`\\n'、`\\r'、そして `\\t' の事です。 フォーマットファイルでの継続行は `\\' の直後に改行文字が来ます。 文字 `%' または `\\' 自身をフォーマット文字列に置く際には、 それぞれ `%%' と `\\\\' を用います。 .\" 文法を先に述べ、意味を後で述べます。 エスケープ・シーケンスには三つの種類があります。 ヘッダーの \fIコンポーネント\fR、組み込みの \fI関数\fR、 そして流れの \fI制御\fR です。 コンポーネント・エスケープは `%{\fIcomponent\fR\^}' と指定し、 メッセージで見つかったそれぞれのヘッダーが処理されます。 例えば、`%{date}' はメッセージの \*(lqDate:\*(rq 行の内容を意味します。 全てのコンポーネント・エスケープは文字列の値を持ちます。 通常、コンポーネントの値は任意のコントロール文字(タブや改行を含む)を スペースに変換し、先行する、あるいは連なっているスペースは省かれます。 しかし、 コマンドによっては、 いくつかのコンポーネント・エスケープに違った解釈を与えるかもしれません。 詳細はそれぞれのコマンドのマニュアルを参照して下さい。 関数エスケープは `%(\fIfunction\fR\^)' と指定します。 全ての関数は組み込みで、たいていは文字列か数字の値を持ちます。 .ne 12 .Uh "制御エスケープ" 制御エスケープは `%<'、`%?'、`%|'、`%>' .\" `%['、`%]' のどれかです。 これらは組合わさって条件文を構成します。 .sp .nf %<条件 \fIフォーマット・テキスト 1\fP %?条件2 \fIフォーマット・テキスト 2\fP %?条件3 \fIフォーマット・テキスト 3\fP \.\.\. %| \fIフォーマット・テキスト N\fP %> .fi .sp ここで余分なスペースは分かりやすくするために付けただけのものです。 これらの構造は明瞭に入れ子にする事が出来ます。 これらのフォームは一般的な \fBif\-elseif\-else\-endif\fP ブロックで、 このうちの一つの \fIフォーマット・テキスト\fP 部分だけが実行される形式です。 `%<' と `%?' の制御エスケープは条件を評価します。 この条件はたぶん、コンポーネントまたは関数のどちらかでしょう。 文法上、次の4つの構造があります。 .sp 1 .nf %<{component} %<(function) %?{component} %?(function) .fi .sp これらの制御エスケープは、関数またはコンポーネントの値がゼロでない (整数値のエスケープの場合)か空でない(文字列値のエスケープの場合)かを 調べます。 もしこのテストが真と評価されれば 次の対応する制御エスケープ(`%|'、`%?'、`%>' のうちの一つ)までの フォーマット・テキストが普通に解釈されます。 次に、 対応する `%>' 制御エスケープまでの全てのフォーマット・テキスト(あれば) が飛ばされます。 `%>' 制御エスケープは解釈されません。 通常、 解釈は `%>' エスケープの後から再開されます。 しかし、もしこのテストが偽と評価されれば、 次の対応する制御エスケープ(再び `%|'、`%?'、`%>' のうちの一つ)までの フォーマット・テキストが飛ばされます。 もし出会った制御エスケープが `%?' ならば、 その制御エスケープに対応する条件が評価され、解釈はその評価の後から 前の段落に書かれているように続けられます。 もし出あった制御エスケープが `%|' ならば、 対応する `%>' エスケープまでのフォーマット・テキストが普通に解釈されます。 先述のように、 `%>' 制御エスケープは解釈されません。 通常、 解釈は `%>' エスケープの後から再開されます。 `%?' 制御エスケープとそれに続くフォーマット・テキストは なくても構いません。そして、たぶんゼロ回またはそれ以上含まれているでしょう。 `%|' 制御エスケープとそれに続くフォーマット・テキストは なくても構いません。そして、たぶんゼロ回または一回含まれているでしょう。 .\" '%[' と '%]' のエスケープはループ構造を作ります。 .\" 繰り返し実行されるフォーマット文字列に於いて .\" (\fIscan\fP のように)、これらのエスケープは実行のメイン・ボディを .\" 区切ります。'%[' の前に現われるフォーマット・テキストは .\" 最初のメッセージを処理する前に一回だけ実行され、 .\" '%]' エスケープの後に現われるフォーマット・テキストは無視されます .\" (これらのエスケープは入れ子にする事は出来ません)。 .\" .Uh "関数エスケープ" .ne 10 たいていの関数は次のような特定の型の引数を期待します。 .sp 1 .nf .ta +\w'引数の型 'u +\w'アドレスのコンポーネント 'u \fI引数の型\fR \fI説明\fR \fI使用例\fR literal 文字通りの数字、 %(\fIfunc\fR 1234) または文字列 %(\fIfunc\fR text string) comp 任意のコンポーネント %(\fIfunc\fR\^{\fIin-reply-to\fR\^}) date 日付のコンポーネント %(\fIfunc\fR\^{\fIdate\fR\^}) addr アドレスのコンポーネント %(\fIfunc\fR\^{\fIfrom\fR\^}) expr 関数、 %(\fIfunc\fR\^(\fIfunc2\fR\^)) コンポーネント、制御、 %(\fIfunc\fR %<{\fIreply-to\fR\^}%|%{\fIfrom\fR\^}%>) または、それらの入れ子 %(\fIfunc\fR\^(\fIfunc2\fR\^{\fIcomp\fR\^})) .re .fi \fIdate\fR 型と \fIaddr\fR 型は \fIcomp\fR 型と同じ文法ですが、 要求するヘッダー・コンポーネントがそれぞれ、 日付の文字列であるか、アドレスの文字列であるかが違います。 \fIexpr\fR 型の引数を除いて、その他の引数は必須です。 \fIexpr\fR 型の引数に於いては、 先行する `%' はコンポーネント・エスケープと関数エスケープの場合は 必ず省略しなければなりません。 また制御エスケープの場合は必ず付けなければなりません (関数名と最初の制御エスケープの間にはスペースも必要)。 フォーマット文字列の評価は、 整数レジスタ \fInum\fR と文字列レジスタ \fIstr\fR を持った単純なマシンを 想定して行ないます。 関数エスケープが処理される時、 もし、\fIexpr\fR 型の引数を取る場合に、引数がなかった場合は、 適切に \fInum\fR または \fIstr\fR の現在の値が使われます。 .\" 戻り値 .Uh "戻り値" コンポーネント・エスケープはそのメッセージ・ヘッダーの値を \fIstr\fR に 書き出します。 \fIinteger\fR 型または \fIboolean\fR 型の戻り値を返す関数は \fInum\fR に、 \fIstring\fR 型の戻り値を返す関数は \fIstr\fR にそれぞれ書き込みます (\fIboolean\fR 型は \fIinteger\fR 型の部分集合で、値 0=偽 と 1=真 を取ります)。 制御エスケープは \fIboolean\fP 型の値を返し、\fInum\fP に書き出します。 全てのコンポーネント・エスケープと、 \fIinteger\fR 型または \fIstring\fR 型を返す関数は、 レジスタ \fIstr\fR または \fInum\fR に値を書き出す事に加えて、 その値を呼び出し元に返します。 これらのエスケープは 他のエスケープの引数として呼ばれた場合を除いて (トップレベルから呼ばれた場合は)、 その値を表示します。 \fIboolean\fR 型を返すエスケープは \fInum\fP に書き出した値を 呼び出し元にも返しますが、その値は表示されません。 .nf .ta \w'Formataddr 'u +\w'Argument 'u +\w'Rboolean 'u \fI関数\fR \fI引数の型\fR \fI戻り値\fR \fI説明\fR msg integer メッセージ番号 cur integer メッセージはカレント・メッセージか .\" unseen integer メッセージは未読か size integer メッセージのサイズ strlen integer \fIstr\fR の長さ width integer 出力バッファの大きさ(バイト単位) charleft integer 出力バッファに残っているバイト数 timenow integer 現在の UNIX 時刻(秒単位) me string ユーザーのメールボックス eq literal boolean \fInum\fR == \fIarg\fR ne literal boolean \fInum\fR != \fIarg\fR gt literal boolean \fInum\fR > \fIarg\fR match literal boolean \fIstr\fR が \fIarg\fR を含むか amatch literal boolean \fIstr\fR が \fIarg\fR で始まるか plus literal integer \fIarg\fR + \fInum\fR minus literal integer \fIarg\fR \- \fInum\fR divide literal integer \fInum\fR ÷ \fIarg\fR modulo literal integer \fInum\fR を \fIarg\fR で割った余り num literal integer \fIarg\fR を \fInum\fR に書き出す lit literal string \fIarg\fR を \fIstr\fR に書き出す getenv literal string 環境変数 \fIarg\fR の値を \fIstr\fR に profile literal string プロファイル・コンポーネント \fIarg\fR を \fIstr\fR に .\" dat literal int dat[arg] の返り値 nonzero expr boolean \fInum\fR がゼロでない zero expr boolean \fInum\fR がゼロ null expr boolean \fIstr\fR が空 nonnull expr boolean \fIstr\fR が空でない void expr \fIstr\fR または \fInum\fR を設定 comp comp string コンポーネントのテキストを \fIstr\fR に設定 compval comp integer \*(lq\fBatoi\fR(\fIcomp\fR\^)\*(rq を \fInum\fR に .\" compflag comp integer コンポーネント・フラグ(内部表現)を \fInum\fR に trim expr \fIstr\fR の後ろに付いたスペースを取り除く putstr expr \fIstr\fR を表示する putstrf expr \fIstr\fR を決まった長さで表示する putnum expr \fInum\fR を表示する putnumf expr \fInum\fR を決まった長さで表示する .\" addtoseq literal メッセージをシーケンスに入れる (LBL オプション時) @BEGIN: MIME_HEADERS hencode expr string \fIstr\fR を RFC\-2047 エンコードする hdecode expr string \fIstr\fR を RFC\-2047 デコードする @END: MIME_HEADERS .re .fi 以下の関数は引数として日付のコンポーネントを必要とします。 .sp 1 .nf .ta \w'Formataddr 'u +\w'Argument 'u +\w'Rboolean 'u \fI関数\fR \fI引数の型\fR \fI戻り値\fR \fI説明\fR sec date integer 秒 min date integer 分 hour date integer 時 (0-23) wday date integer 曜日 (日曜=0) day date string 曜日 (略号) weekday date string 曜日 (英語表記) sday date integer 曜日が明示されているか? (1=明示,0=暗示される,\-1=分からない) mday date integer 日 yday date integer 一年の最初から何日目か mon date integer 月 month date string 月 (略号) lmonth date string 月 (英語表記) year date integer 年 (たぶん > 100) zone date integer タイムゾーン tzone date string タイムゾーン文字列 szone date integer タイムゾーンは明示されているか? (1=明示,0=暗示される,\-1=分からない) date2local date 日付を現地時刻に変える date2gmt date 日付をグリニッジ時刻に変える dst date integer サマータイムになっているか? clock date integer UNIX 時刻(秒単位) rclock date integer 現在より何秒前か tws date string 正式な RFC\-822 表記 pretty date string ユーザーに分かりやすい表記 nodate date integer \fIstr\fR が日付文字列でない .re .fi .ne 12 以下の関数は引数として、アドレスのコンポーネントを必要とします。 `*' と書かれている関数は、 戻り値として ヘッダー・コンポーネントの中の最初に書かれているアドレスだけを保持します。 .sp 1 .nf .ta \w'Formataddr 'u +\w'Argument 'u +\w'Rboolean 'u \fI関数\fR \fI引数の型\fR \fI戻り値\fR \fI説明\fR proper addr string 正式な RFC\-822 表記 friendly addr string ユーザーに分かりやすい表記 addr addr string mbox@host または host!mbox 表記* pers addr string 個人名* note addr string コメント文* mbox addr string ローカル・メールボックス* mymbox addr integer ユーザーのアドレスか? (0=no,1=yes) host addr string ホスト・ドメイン* nohost addr integer ホスト名が書かれてない* type addr integer ホストの形式* (0=ローカル,1=ネットワーク, \-1=UUCP,2=分からない) path addr string ホストの経路指定* ingrp addr integer アドレスがグループに入っているか* gname addr string グループ名* formataddr expr \fIarg\fR を \fIstr\fR に(コンマで区切った) アドレス・リストとして追加 putaddr literal アドレス・リスト \fIstr\fR を表示する。 引数がラベル(コンポーネント名)になる。 行の幅は \fInum\fR から得る。 .re .fi エスケープが入れ子になった場合、 最も内側のものから最も外側のものへと評価されていきます。 最も外側のエスケープは `%' が必要ですが、内側のエスケープは要りません。 例えば、 .ti +.5i %<(mymbox{from}) To: %{to}%> はヘッダー・コンポーネント \*(lqFrom:\*(rq の値を \fIstr\fR に書き出します。 それから (\fImymbox\fR\^) は \fIstr\fR を読み、 その結果を \fInum\fR に書き出します。 そして制御エスケープが \fInum\fR を評価します。もし \fInum\fR がゼロで ない場合は、文字列 \*(lqTo: \*(rq とそれに続けてヘッダー・コンポーネント \*(lqTo:\*(rq の値が表示されます。 (\fImymbox\fR\^{\fIcomp\fR\^}) について、補足説明をしておきましょう。 一般に、これはヘッダー・コンポーネント \*(lq\fIcomp\fR\*(rq にある 全てのアドレスを、ユーザーのメールボックス名及び全ての \fIAlternate-Mailboxes\fR と比較します。 これは、どれかのアドレスと一致すれば、真を返しますが、 もし \*(lq\fIcomp\fR\*(rq ヘッダーがメッセージに存在しない場合も 真を返します。必要なら、この場合には (\fInull\fR\^) 関数で明示的に 調べておきましょう。 \fIAlternate-Mailboxes\fR に関しては \fImh-profile\fR\0(5) も参照して下さい。 関数エスケープまたはコンポーネント・エスケープが解釈され、 結果が直ちに表示される時には、 表示幅を指定する事が出来、与えられた文字数内にきっちりと表示されます。 例えば、%4(\fIsize\fR\^) というような数値のエスケープはメッセージのサイズを 多くとも 4桁で表示します。もし、桁あふれをした場合は、最初の位置に `?' が出力されます(例えば `?234')。 %4(\fIme\fR\^) というような文字列のエスケープは最初の 4文字を表示し、 それ以降は切り捨てます。 表示幅より短い場合は、右側に指定の幅まで文字(通常、空白)を埋めます。 もし、表示幅指定がゼロから始まってる場合は、この埋める文字はゼロになります。 上述のように、 関数 (\fIputnumf\fR\^) と (\fIputstrf\fR\^) は、その結果を 先行する表示幅指定で指定された文字数で表示します。 例えば、 %06(\fIputnumf\fR\^(\fIsize\fR\^)) はメッセージのサイズを 6文字の幅で表示し、頭はゼロで埋められます。 %14(\fIputstrf\^\fR{\fIfrom\^\fR}) は \*(lqFrom:\*(rq ヘッダー・コンポーネントを 14文字の幅で表示し、 後ろはスペースで埋められます。 \fIputstrf\fR に対して、 表示幅として、負の数を与えると、その文字列はその幅の中に右寄せで表示され、 埋められる文字は左側におかれます。 なお、関数 (\fIputnum\fR\^) と (\fIputstr\fR\^) は結果を必要最小限の文字数で 表示し、表示幅指定は無視します。 出力可能な表示幅は内部レジスタに保存されていて、 その幅を越える出力は全て捨てられます。 コメントは関数の引数が期待される場所を除いて、たいていの場所に置く事が 出来ます。コメントは `%;' で始まり、(エスケープされてない)改行で終ります。 これを全て頭に置いた上で、 ここに \fIscan\fR のデフォルトのフォーマット文字列があります。 これを読みやすくするために、いくつかの部分に分けます。 最初の部分は次の通りです。 .ti +.5i %4(msg)%<(cur)+%| %>%<{replied}\-%?{encrypted}E%| %> これはまず、メッセージ番号を 4桁で表示する事を表しています。 次に、メッセージがカレント・メッセージである場合は `+'、そうでない場合は スペースを出力する事を表しています。 次に、もし \*(lqReplied:\*(rq 行が存在するなら `\-'、 存在しない場合は \*(lqEncrypted:\*(rq を調べ、 存在するなら `E'、そうでない場合はスペースを表示します。 次、 .ti +.5i %02(mon{date})/%02(mday{date}) 月と日をそれぞれ 2桁(一桁の場合は頭に 0 が付く)で、 スラッシュで区切って表示します。 そして、 .ti +.5i %<{date} %|*%> \*(lqDate:\*(rq 行が存在すればスペース、さもなくば `*'。 次、 .ti +.5i %<(mymbox{from})%<{to}To:%14(friendly{to})%>%> もしメッセージが自分が出したものであり、且つ \*(lqTo:\*(rq ヘッダーがあるなら、 `To:' と \*(lqTo:\*(rq 行の最初のアドレスをユーザーに分かりやすい表記で 表示します。 続いて、 .ti +.5i %<(zero)%17(friendly{from})%> もし、上記二つの条件判断のどちらかに失敗した場合、 \*(lqFrom:\*(rq 行がユーザーに分かりやすい表記で表示されます。 制御エスケープの条件判断は \fInum\fR レジスタに値を設定するので、 後でそれを利用する事によって、AND や OR の処理をさせる事が出来ます。 最後に、 .ti +.5i %{subject}%<{body}<<%{body}%> \*(lqSubject:\*(rq 行と ボディの最初の部分(あれば)を表示します。 もっと複雑な例として、 次はデフォルトの \fIreplcomps\fR フォーマット・ファイルを考えてみましょう。 .ti +.5i %(lit)%(formataddr %<{reply-to} まず \fIstr\fR レジスタを空にし、\*(lqReply-To:\*(rq ヘッダーが存在するなら それを整形します。もし存在しないなら、以下の else-if 部分が実行されます。 .ti +.5i %?{from}%?{sender}%?{return-path}%>)\\ \*(lqFrom:\*(rq、\*(lqSender:\*(rq、そして \*(lqReturn-Path:\*(rq ヘッダーのうち一つが存在した時点で止まります。で、 .ti +.5i %<(nonnull)%(void(width))%(putaddr To: )\\n%>\\ もし \fIformataddr\fR の結果が空でなかった場合、 それがアドレスとして \fIwidth\fR の幅で (必要なら行を折り返して)、 頭に \*(lqTo: \*(rq をつけて表示します。 .ti +.5i %(lit)%(formataddr{to})%(formataddr{cc})%(formataddr(me))\\ \fIstr\fR を空にし、 \*(lqTo:\*(rq と \*(lqCc:\*(rq ヘッダー、そしてユーザーのアドレス (\fIrepl\fR の \*(lq\-cc\*(rq オプションで何を指定したかに依る)が 整形されます。 .ti +.5i %<(nonnull)%(void(width))%(putaddr cc: )\\n%>\\ 結果が空でない場合は、上と同じように \*(lqcc: \*(rq を付けて表示します。 .ti +.5i %<{fcc}Fcc: %{fcc}\\n%>\\ もし \fIrepl\fR に \*(lq\-fcc\ folder\*(rq オプションが指定された場合 (%{\fIfcc\fR\^} に関する詳細は \fIrepl\fR\0(1) を参照)、 \*(lqFcc:\*(rq ヘッダーが出力されます。 .ti +.5i %<{subject}Subject: Re: %{subject}\\n%>\\ \*(lqSubject:\*(rq コンポーネントが存在する時は、 適切な返信サブジェクトが出力されます。 .nf .ti +.5i %<{date}In-reply-to: Your message of "\\ .ti +.5i %<(nodate{date})%{date}%|%(pretty{date})%>."%<{message-id} .ti +.5i %{message-id}%>\\n%>\\ .ti +.5i \-\-\-\-\-\-\-\- .fi もし \*(lqDate:\*(rq コンポーネントが存在するなら、 \*(lqIn-Reply-To:\*(rq ヘッダーが \*(lqYour message of \*(rq を付けて 表示します。 もし、日付が解析可能なら、ユーザーに分かりやすい形式で出力されます。 さもなくば、そのまま出力します。 \*(lqMessage-ID:\*(rq があれば、それも含めます。 ダッシュ(`\-')の列は生のテキストなので、そのまま出力されます。 この最後の部分はもうちょっと詳しく述べるのに良い例です。 ここにその部分を再び仮想的なコードで書いてみましょう。 .sp 1 .nf .in +.5i .ta .5i 1i 1.5i 2i if (comp_exists(date)) then print (\*(lqIn-reply-to: Your message of \\\*(lq\*(rq) if (not_date_string(date.value)) then print (date.value) else print (pretty(date.value)) endif print (\*(lq\\\*(rq\*(rq) if (comp_exists(message-id)) then print (\*(lq\\n\\t\*(rq) print (message-id.value) endif print (\*(lq\\n\*(rq) endif .re .in -.5i .fi .sp 1 これは複雑に見えますが、実際上、 この方法は個々のヘッダー行を取り出し、 ユーザーが好む任意の形式で出力するのに充分に柔軟性のあるものです。 .Fi 無し。 .Pr 無し。 .Sa scan(1), repl(1), ap(8), dp(8) .De 無し。 .Co 無し。 .Hi このソフトは MH 6.3 のために寄贈されました。その前までは、 出力形式の指定はもっと書き易いものでしたが、極めて柔軟性に欠けるものでした。 .Bu \fIMH\fR を BERK オプション付で構築したホストでは、 アドレスの解析は出来ません。 .En