fmlのProgramming Style



(株)インターネットイニシアティブ
札幌支店インターネット技術部
深町 賢一



Copyright (C) 1998 Ken'ichi Fukamachi
All rights reserved.





(page 1)


Table of Contents

fml overview
Programming Style
Perl4と5のはざまで
ポータビリティ
まとめ
今後について





(page 2)





fml overview



(page 3)


メーリングリスト

メーリングリストの概念図
MTA(Mail Transport Agent)がメールを受け、下受けのfmlに渡す。
fmlが一種のフィルタリングとして機能し再びMTAにMLのメンバーへの配送をゆだねる。
ml.gif



(page 4)


動機: perlの選択


どっちを使うかは内容次第
text処理中心ならperl

メーリングリストはtext処理が一杯だろう
socket使いたい(自力SMTP)
最近(当時)流行りつつあって面白そう:) # 4.01xの頃の話



(page 5)


mgp2ps4 (sh+perl)

この資料を印刷するのに作ったdebug拡張込み10分で作成(いいかげん)プログラム:)
postscriptを一行修正するだけのもの。shell script と perl の混成
#!/bin/sh
PSMULTI4="psmulti -rtol -r180 -pages 4"
FILTER=/tmp/disable_showpage.pl
... variables ...

trap "rm -f $FILTER $tmp" 0 1 3 15
set -- `getopt hdp:cv $*`
... getopt ...

echo x - disable_showpage.pl 1>&2
sed 's/^X//' >disable_showpage.pl << 'END-of-disable_showpage.pl'
Xwhile (<>){
X if ($flag && /showpage/) {
X s/showpage/pshowpage/; $flag = 0;
X }
X if (/^\/(BeginDocument|BeginEPSF)/) { $flag = 1;}
X print;
X}
END-of-disable_showpage.pl

if [ $pages -eq 4 ]
then
mgp2ps $opt $* | perl $FILTER | $PSMULTI4 | perl $FILTER
...


(page 6)


動機: 何故作る?

- それは1993年頃…

(センスにめまい) => 捨て!



K. Fukamachi,
Interdisciplinary Information Science vol.1, 157 (1995)


(page 7)


デザイン・ポリシー







(page 8)


歴史

Official Release History (fj.sources)

日付 リリース
1998/09 2.2
1997/08 2.1
1995/01 1.5
1994/09 1.3
1994/05 1.2.1
1994/03 1.2

[備考]
1.6 %Envelope hashの導入
2.0 幻のversion?



(page 9)


Supported Systems

今は1.3H(1998/09上旬)


Windows NT4
βテスト中で、2つのバージョンがある
include形式
POP3版(POP -> fml -> MTA)

sendmail(+smtpfeed), qmail, exim などで実験



(page 10)


必要な環境


自分もしくはML専用のアカウント

localhost
同じLAN上の複数のMTA
同じホストで違うポートでMTA

それぞれ別のマシン可
配送だけUNIXでsmtpfeedとかも可 :)



(page 11)


特徴(1)

(anyone/members_only/moderator)


(page 12)


特徴(2)

UNIX FROM,RFC934,RFC1153,MIME/Multipart
gzip,Lha + Ish,Lha + uuencode,tar + gzip
zip + base64,uuencode


(page 13)


特徴(3)

メールサイズ
メンバー数
1メール当たりのコマンド数
address と実名のテーブル

スタートレック宇宙歴
mail2irc





(page 14)





Programming Style




(page 15)


fmlの場合の決めごと

これらは開発のはじまる前に決められた。




(page 16)


関数名の例

大文字で始まる
&Distribute(*e)
&SetEvent &ClearEvent &Tick

Lisp風
チェックをしてyes/noを返す類い(p == predicate)
&SecureP &MailLoopP


大文字
ユーザ定義用なので変数と同様に大文字
&DEFINE_SUBJECT_TAG("[ ]");
e.g. [Elena ID] style


(page 17)


変数名の例

ユーザカスタマイズ用
$DOMAINNAME = "fml.org";
$FQDN = "beth.fml.org";
$MAIL_LIST = "elena\@$DOMAINNAME";
$PERMIT_POST_FROM = "members_only";
$REJECT_POST_HANDLER = "reject";

システムの使うグローバル変数
%Envelope (sendmailのENVELOPE相当)
$SiteInitPath
$SitedefPath



(page 18)


時代によってperlらしくなくなった例

while(<>) { $WHOLE_MAIL .= $_;}


while ($p = sysread(STDIN, $_, 1024)) {
$bufsiz += $p;
if ($INCOMING_MAIL_SIZE_LIMIT &&
($bufsiz > $maxbufsiz)) {
...
}
}

e.g. directory操作 => 境界値テスト



(page 19)


疑似構造体

関数間で一気に渡すための構造体
e.g. &Distribute(*Envelope);

fml (> 1.6) sendmail
$Envelope{'h:From:'} <=> e->e_from.q_paddr
h, mode, macro, message ...

* perl 4の頃に設計されたため疑似的
* ARRAYは滅多にないためあまり困らない
* ユーザは &DEFINE_FIELD_* を通して間接アクセス
(いじろうと思えばいじれるが)


(page 20)


Hashによる関数定義

Hashに関数定義テーブルを持つ。これはperl 4でも使えた。
ユーザが自由にオーバーロードできる。
fml全域に渡り同様の技法が数多く使われている。

@PermitProcedure
%LocalProcedure
@DenyProcedure

- fml default
%Procedure = ('help', 'ProcFileSendBack');

- config.ph
%LocalProcedure = ('help', 'MyhelpFunction');

- call
$fp = $Procedure{$proc};
$status = &$fp($_, *Fld, *e, *misc);


(page 21)


Hash and Chips

-- libfml.pl

if ($proc = $Procedure{$_}) {
$status = &$proc($_, *Fld, *e, *misc);
last GivenCommands if $status eq 'LAST';
next GivenCommands;
}

-- libfop.pl

for $proc (
'hdr',
'cnstr',
'retrieve',
'encode',
'split',
'encode_as',
'destr'
) {
$prog = $_fp{$proc, $mode};
&$prog(*conf, *r, *misc) if $prog;
}



(page 22)


main()

&CheckUGID;
chdir $DIR || die "Can't chdir to $DIR\n";
&InitConfig; # Load config.ph, initialize conf, date,...
&Parse; # Phase 1, pre-parsing here
&GetFieldsFromHeader; # Phase 2, extract fields
&FixHeaderFields(*Envelope); # Phase 3, fixing fields information
&CheckCurrentProc(*Envelope); # Phase 4, check the current proc

&Lock; # Lock!
if ($USE_LOG_MAIL) { &use('logmail'); &MailCacheDir;}
if (! &MailLoopP) {
&CacheMessageId(*Envelope);
&RunStartHooks; # run hooks
&ModeBifurcate(*Envelope); # 1. &Distribute
# 2. &Command
}
&Unlock; # UnLock!

&RunHooks; # run hooks after unlocking
&Notify() if $Envelope{'message'} || $Envelope{'error'};
&ExecNewProcess; # start new process execution (if defined)
exit 0; # the main ends.



(page 23)


プログラミング・スタイルのまとめ







(page 24)


ちょっとしたこと(1)

開発中のdebugコードは基本的に残す=> ... if $debug
注釈のメンテもわすれずに/注釈の付け過ぎにも注意
ポータビリティ
newsyslog(8)的バックアップ, RCS バックアップ
各MLは独立したもの
システムに複数のユーザのそれぞれの世界(MLだから)



(page 25)


コメントの割合


自分のコード 31.4 %
互換コード 34.9 %
libexec/ 32.6 %
人のコード 34.2 %

[参考]
/sys/kern 16.4 %
/sys/uvm 37.6 %
/sys/vm 29.2 %



(page 26)


ちょっとしたこと(2)

まとめおくりのcrontabの設定
教育あるのみ?
テンプレート作成支援/インストーラが頑張る?




(page 27)


fml told me (1)

ありふれたことが便利にできること
変数はtoggle?switch?決断は常に2,3手先を読む

$debugやswitch構文のdefaultなどを必ず書く
改良はアルゴリズムとデータ表現自体ということも

パラメータはこのくらいの範囲…
こんなことするやつはいないはありえない!いや必ずいる!

(page 28)


fml told me (2)

何もしない時おかしいことがおこらないように
MLなら0通、10万通テスト

小さいものに分割

perlよりCかも?


(page 29)


fml Project

cvs.fml.org などの運用形態はなし
ドキュメント部分などはやってもいいのかもしれない

基本的にメールで patch をもらうなど
僕が査読して当てる
1. スタイルはその時に修正される
2. 完全に独立保守されているもの
(僕はいじっていない)
e.g. libtraffic.pl, libmember_name.pl





(page 30)





Perl4と5のはざまで…


(page 31)


Perl4と5のはざまで…

fmlはperl4と5両方で動くようにしてある
perl5は5.003くらいになるまで静観していた
5.003になるはるか前に今のスタイルが確立し終っている
4で動くものをあえて5で書きなおす必要はない
運用面
開発面(としては面白くない?)

今まではそうだが、では新規分については?
互換性、ポータビリティ、壱からテスト?
Reference は使いたいかも…


(page 32)


Object




理念はよいが、のめりこみ過ぎる側面もありうる

世の中はぐちゃぐちゃなのを見つめよう
すべての理論は適用可能なパラメータレンジがある

常にバランス感覚を忘れないように
適材適所
# quick hack や perl poem もいいけれどね:)
perlというカテゴリにしばられる必要もない

e.g. libnntp.plはNNTP::Client(3)を使っている
すでにあったから





(page 33)





ポータビリティ


(page 34)


ポータビリティを上げるためには?

UNIX間ではperlが差を吸収しているのであまり苦労はない
config.guess(GNU automake)で推測, SYSV系はちょっと…

MTAの挙動,信頼性はOSによってかなりばらつきがある
インストーラーとドキュメントで吸収せざるをえない

既にあるものは使おう(しかし枯れてるものに絞るべき)

UNIXコマンドに依存しない e.g. 自力SMTP

UNIX以外へのportは教訓が得られる
fork, alarm, get*() 系。暗黙のUNIXの仮定



(page 35)


例: SocketInit

Socket 初期化コード

0. $STRUCT_SOCKADDRはインストーラが推測
1. eval("use Socket;");
2. eval("require 'sys/socket.ph';");
3. SYSV ? (config.guessによる判定) => &AF_INET
4. BSD or LAST RESORT => &AF_INET

* やりすぎか?
* perl自体のinstallをしくじっている場合も救える(こともある)


(page 36)


プロファイル

行数
メイン+ライブラリ 20456
インストール 6454
互換コード 583
人の書いたコード 2838
libexec/ 7379
bin/ 4714
sys/SOLARIS2/ 16
sys/WINDOWS_NT4/ 1194

UNIXの特別な機種向けはほとんどない。
NT用の特殊なコードは全体からみればごくわずか
ほとんどUNIX版をそのまま使っている
上の1000行の内訳はほとんどインストーラとPOP版のコード



(page 37)


Porting to NT

NT用の特殊コードは全部合わせて1000行程度
include版はほとんどなし
インストーラとPOP版で半々程度

fork, alarm, select 駆動の問題

fileパス名などで暗黙のうちにUNIXな前提をしている

get*() 系






(page 38)





まとめ


(page 39)


まとめ

スタイルを持とう。決めたら守り抜こう

バランス感覚と適材適所
短期開発可能な良さと弱点とを見つめること
perlらしさとperlらしさを避けることと…

わかりやすいコードを書こう
技巧に走っても効率はさしてかわらない
コメントはつける(付け過ぎても駄目)
アルゴリズムとデータ構造(〜ポータビリティ)

根本的な部分は言語によらない
走り疲れたら初心に戻ろう

インストーラ, 設定インターフェイス, ドキュメント




(page 40)


今後について

コードをきれいに(永遠のテーマ?)

ドキュメントの整理(永遠のテーマ?)
翻訳
系統的にまとまったものがあるべき
Operation, Design&Implementation は分離すべき
それ以外にFAQ?

インストーラ、設定ツールの改良, GUI?

日本語化?e.g. &Mesg(*e, buf, id, @argv)
ポーティング

cvs.fml.org ?


(page 41)


スタイルという点についての参考文献

The Elements of Programming Style
B.W. Kernighan and P.J. Plauger
(プログラミング書法, 木村 泉訳, 共立出版)

Software Tools
B.W. Kernighan and P.J. Plauger
(ソフトウエア作法, 木村 泉訳, 共立出版)

ポータビリティの高いソフトウエアのソースコード
(page 42)