[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
この節では、頑丈なソフトウェアの書き方を記述する。また、 エラーメッセージや、コマンドラインのインターフェース、ライブラリの挙動 の、汎用的な標準についても記述する。
4.1 頑丈なプログラムの作成 | ||
4.2 ライブラリの挙動 | ||
4.3 エラーメッセージの書式 | ||
4.4 コマンドラインのインターフェースの標準 | ||
4.5 長いオプションの表 | ||
4.6 メモリの使用 |
動的に全てのデータ構造を確保することによって、ファイル名、行、ファイル、 シンボルを含む、いかなるデータ構造の長さや数についても、勝手な 制限を避けなさい。 ほとんどのUnixユーティリティでは、"長い行は黙って切り詰める"。 これはGNUユーティリティでは許容できない。
ファイルを読むユーティリティはNUL文字や、0177以上のコードを持つ 文字を含むあらゆる他の印字できない文字も、落とすべきではない。 唯一意味のある例外は、そういった文字を扱えない、ある種のプリンタへのイ ンターフェースを特別に意図したユーティリティだろう。 可能な限りいつでも、UTF-8やその他のエンコーディングを使って、 多バイト文字を表すバイト列を適切に扱えるようにしなさい。
エラーを無視したいと思っているのでなければ、あらゆるシステムコールのエ
ラーを確認しなさい。失敗したシステムコールから発生するあらゆる
エラーメッセージに、もしあればファイルの名前とそのユーティリティの名前
だけでなく、(perror
や同等のものから得られる)システムエラー文字
列を含めなさい。単なる"cannot open foo.c"や"stat failed"は十分でな
い。
malloc
やrealloc
のすべての呼び出しを、ゼロを返したかどう
か確認しなさい。例えそのブロックをもっと小さくしようとしていても、
realloc
の確認をしなさい。2の階乗にブロックサイズを丸めるシステム
では、realloc
はもっと小さい領域を要求する場合に異なるブロックを
得ることがある。
Unixでは、realloc
がゼロを返す場合、記憶領域を破壊してしまう。
GNU realloc
はこのバグを持たない。失敗すると、元のブロックは変更
されない。そのバグは直っているとみなしても構わない。もしあなたのプログ
ラムをUnix上で走らせたくて、こういう損失を避けたいなら、GNU malloc
を使うことができる。
free
は解放されたブロックの中身を変えてしまうと考えなければなら
ない。そのブロックの値を取り出したかったら、必ずfree
を呼ぶ前に
取り出さなくてはならない。
もしmalloc
が対話的でないプログラムで失敗したら、それを致命的な
エラーにしなさい。対話的なプログラム(ユーザからコマンドを呼んでくるも
の)では、そのコマンドを中止して、コマンド読み込みループから返るのがよ
り良い。こうすると、そのユーザは仮想メモリを解放するために他のプロセス
を殺して、再びそのコマンドを試すことができる。
もし引数の文法が上手く行かなくなるわけでないなら、引数の解読に
getopt_long
を使いなさい。
静的な記憶領域がプログラムの実行中に書き込まれるためであるとき、それを 初期化するための、明示的なCのコードを使いなさい。変更されないデータに 対する、Cの初期化付き宣言を残しておきなさい。
(ファイルディレクトリや、utmp、カーネルメモリの配置のような)Unixのデー
タ構造を見えにくくする、低水準のインターフェースを避けるよう努めなさい。
これらは互換性を失いがちだからだ。もしあるディレクトリの全ファイルを見
付ける必要があるなら、readdir
や他の高水準のインターフェースを使
いなさい。これらはGNUによって互換性を持ってサポートされるだろう。
好ましいシグナルハンドリングの機能はBSD流のsignal
と
POSIX sigaction
関数である。別にあるUSGのsignal
は
劣った設計だ。
今日では、POSIXシグナル関数の使用がプログラムを移植しやすくする
一番簡単な方法かもしれない。signal
を使うと、GNU libc version 1
を使うGNU/Linuxシステム上でBSDの振る舞いを得るために、`signal.h'
ではなく`bsd/signal.h'をincludeすべきだ。signal
がUSGの振る
舞いしか持たないシステムをサポートするか、あるいは、それらを諦めてしま
うかはあなた次第だ。
"あり得ない"状態を検出するエラーチェックでは、単に中止しなさい。メッ セージを出力する意味は普通ない。これらのチェックはバグの存在を示している。 そのバグを直したい人なら誰でも、そのソースコードを読み、デバッガを走ら せないといけないだろう。だから、そのソースにコメントでその問題を説明し なさい。関係のあるデータは変数の中で、それはデバッガで検査するのは容易 だろう。だから、それらをどこか他の位置に移す意味はない。
プログラムの終了状態として、エラーのカウントを使ってはならない。 これは上手く行かない。なぜなら、終了状態の値は(0から255までの) 8ビットに制限されているからだ。そのプログラムが一回走る間に256のエラー が起きるかもしれない。もし終了状態として256を返そうとすると、親プロセ スはその状態として0を見ることになり、そのプログラムが成功したかのよう に見えるだろう。
もし一時ファイルを作るなら、TMPDIR
環境変数を確認しなさい。この
変数が定義されていれば、`/tmp'ではなく、指定されたディレクトリを
使いなさい。
ライブラリ関数を再入可能にするよう努力しなさい。それらが動的な記憶領域
の確保を必要とするなら、少なくともmalloc
自体は別として、再入
不能を避けるよう努力しなさい。
名前がぶつかるのを避けるために、ライブラリ用の名前付けの取り決めがある。
二文字以上の長さで、そのライブラリ用の接頭辞を決めなさい。外部に見せる 関数と変数の名前すべてに、この接頭辞を付けるべきだ。さらに、どの特定のラ イブラリ・メンバーでも、これらのうち一つだけが入っているべきだ。これは通 常それぞれを別のソースファイルに置くことを意味する。
二つの外部シンボルが常に一緒に使われ、片方を使ってもう片方を使わない ような意味のあるプログラムがあり得ないようなときには、例外となる。それ らは両方とも同じファイルに入れられる。
ユーザにエントリ・ポイントとして記述されない外部シンボルは、`_'で 始まる名前を持つべきだ。それらはまた、他のライブラリと衝突するのを防ぐ ために、そのライブラリのために選ばれた接頭辞を含むべきだ。これらは、好 むなら、ユーザのエントリ・ポイントと同じファイルの中に含めても良い。
静的な関数や変数は好きなように使って良く、どんな名前付け規則にも当ては まらなくていい。
コンパイラからのエラーメッセージは次のようであるべきだ。
source-file-name:lineno: message |
適切なソースファイルがあるときには、 他の対話的でないプログラムからのエラーメッセージは次のようであるべきだ。
program:source-file-name:lineno: message |
関連のあるソースファイルがないときには、次のようだ。
program: message |
対話的なプログラム(端末からコマンドを読んでいるもの)では、エラーメッセ ージにプログラム名を含めない方が良い。どのプログラムが走っているかを示 す場所は、プロンプトか、スクリーンのレイアウトだ。(同じプログラムが端 末以外のソースから入力を受け取って走るとき、それは対話的ではなく、対話 的でない形式を使ってエラーメッセージを出力するのが一番良いだろう。)
文字列messageは、プログラム名やファイル名に続くときには、大文字 で始めるべきではない。また、ピリオドで終わるべきではない。
対話的なプログラムからのエラーメッセージや使い方のメッセージのような他 のメッセージは大文字で始めるべきだ。しかしピリオドで終わるべきではない。
ユーティリティの挙動をそれを起動した名前に依存させないでください。あ るユーティリティに別の名前をリンクすることは、ときどき有用で、そのこと で何をやるのかを換えるべきでない。
代わりに、動作時のオプションか、コンパイルするときのスィッチか、両方を 別の挙動を選択するために使いなさい。
同様に、プログラムの挙動をそれが使う出力デバイスの種類に依存しないよう にしてください。デバイス独立はシステム設計の重要な原理だ。単に誰かがと きどきオプションを打ち込むのを省略することに妥協してはならない。(端末 を使うときのエラーメッセージの文法を変化させるのは構わない。なぜなら、 それは人々が依存していない別な問題だからだ。)
もし出力が端末に向かうときある挙動が最も有用で、出力がファイルかパイプ なら他の挙動が最も有用なら、端末への出力で有用な挙動をデフォルトにして、 他の挙動のオプションを持つのが通常一番良い。
互換性のために、出力デバイスの種類に依存するプログラムを必要とする。
もしls
やsh
が、あらゆるユーザが期待する方法で働かなかった
ら、それはひどいだろう。これらの場合のうちいくつかでは、出力デバイスの
種類に依存しない、より好ましい別バージョンを我々は補う。例えば、
ls
にとても似ているが、デフォルトの出力形式が常に複数欄形式であ
る、dir
プログラムを提供する。
プログラムのコマンドライン・オプションをPOSIXのガイドラインに従わ
せるのは良い考えだ。これを行う一番簡単な方法は、それらを解析するのに
getopt
を使うことだ。getopt
のGNUバージョンは、特別な引数
`--'が使われなければ、通常オプションが引数のどこにあっても良いこ
とに注意しなさい。これはPOSIXが規定していることではない。GNUの拡
張だ。
一文字のUnix形式オプションと等価な長い名前のオプションを定義してくださ
い。我々はこの方法でGNUをよりユーザに親しみやすいものにしたいと思って
いる。これはGNUの関数getopt_log
を使えば簡単だ。
長い名前のオプションの利点の一つはどのプログラムでも一貫したものにでき るからだ。例えば、ユーザは"verbose"オプションを持つどのGNUプログラム もそれが正確に`--verbose'と綴られると期待することができるべきだ。 この不変性を成すために、あなたのプログラムのオプション名を選ぶとき、共 通の長いオプション名の表を見なさい (see section 4.5 長いオプションの表)。
普通の引数として与えられるファイル名が入力ファイルだけにするのは普通良い 考えだ。どんな出力ファイルでも(願わくは`-o'や`--output'のよう な)オプションによって指定されるだろう。互換性のために普通の引数として出 力ファイル名を許す場合でも、それを指定する他の方法としてオプションを与え てみなさい。これはGNUユーティリティの一貫性を増し、そしてユーザが覚える べき独自性を減らすであろう。
あらゆるプログラムは次の二つの標準的なオプションをサポートすべきだ。 `--version'と`--help'だ。
--version
最初の行をプログラムが解析しやすくする。そのバージョン・ナンバーを最後の スペースの後に始める。加えて、このプログラムの正しい名前を次の形式で含め る。
GNU Emacs 19.30 |
プログラム名は固定文字列であるべきだ。それをargv[0]
から計算しては
いけない。その考えは、そのファイル名ではなく、そのプログラムの標
準的、あるいは、正統な名前を表明することである。コマンドをPATH
か
ら見付けることで、正確なファイル名を見付け出す他の方法があるのだ。
もしプログラムが大きいパッケージの補助的な部分なら、次のようにそのプログ ラム名を括弧の中で記述しなさい。
emacsserver (GNU Emacs) 19.30 |
もしそのパッケージがこのプログラムのバージョン・ナンバーとは違うバージョ ン・ナンバーを持っているなら、閉じ括弧の直前にそのパッケージのバージョン・ ナンバーを記述して良い。
もしこのプログラムを含むパッケージとは別に配布されるライブラリのバージョ ン・ナンバーを記述したいのなら、記述したいそれぞれのライブラ リ毎に行を追加して、バージョン情報を出力することで、そうして良い。それら の行に最初の行と同じ形式を使いなさい。
そのプログラムが使うライブラリ全てを、"単に完全であるためだけに"記述し ないでください。---そうすると、たくさんの役に立たない乱雑さを生み出して しまうだろう。あなたがデバッグをするのに非常に重要であると実際に見出し た場合にだけ、ライブラリのバージョン・ナンバーを記述してください。
バージョン・ナンバーの行の後の、次の行は著作権通知であるべきだ。二つ以上の 著作権通知が必要なら、それぞれ別の行に入れなさい。
次は、そのプログラムがフリーソフトウェアであり、ユーザは自由に複製したり、 ある条件でそれを改変して良いという、簡単な記述が続くべきだ。もしそのプロ グラムがGNU GPLによって保護されているなら、ここでそう言いなさい。また、 法に認められる範囲に対し、無保証であることを書きなさい。
名誉を与える方法として、そのプログラムの主要な作者の名簿を出力して終わら せて構わない。
これらの規則に従う出力の例を示そう。
GNU Emacs 19.34.5 Copyright (C) 1996 Free Software Foundation, Inc. GNU Emacs comes with NO WARRANTY, to the extent permitted by law. You may redistribute copies of GNU Emacs under the terms of the GNU General Public License. For more information about these matters, see the files named COPYING. |
これをあなたのプログラムに一致させるべきだ。当然、適切な年、著作権者、プ ログラムの名前、そして、配布条件の言及を入れ、必要に応じて残りの言葉遣い を換えるべきだ。
この著作権通知は変更がなされた一番最近の年を記述するだけでいい。---以前 のバージョンの変更に対して年を列挙する必要はない。もし不便なら、プログラ ムの名前をこの通知の中で記述しなくて良い。最初の行に現れているから。
--help
`--help'オプションの出力の最後の辺りで、バグ報告をどこにメールする かを表す行があるべきだ。こういう書式を持つ。
Report bugs to mailing-address. |
GNUプログラムによって使われる長いオプションの表をここで示す。きっと不完 全ではあるが、新しいプログラムが互換性を持ちたいであろうオプションをすべ て列挙するつもりだ。もしこの表にまだない名前を使うなら、それらの表と、そ れらの意味をgnu@gnu.orgに送ってください。我々がこの表を更新でき るので(3)。
tar
.
du
, ls
, nm
, stty
, uname
,
and unexpand
.
diff
.
ls
.
etags
, tee
, time
;
`-r' in tar
.
cp
.
shar
.
m4
.
diff
.
gawk
.
recode
.
wdiff
.
ptx
.
wdiff
.
ctags
.
shar
.
tac
.
cpio
and diff
.
shar
.
cpio
and tar
.
head
and tail
.
ptx
.
head
, split
, and tail
.
etags
.
tar
.
chgrp
and chown
.
ls
.
recode
.
su
;
`-x' in GDB.
tar
.
gawk
.
tar
and shar
.
tar
.
tar
.
diff
.
gawk
.
ptx
, recode
, and wdiff
;
`-W copyright' in gawk
.
who
.
du
.
tar
and cpio
.
shar
.
ctags
.
touch
.
m4
;
`-t' in Bison.
m4
.
ctags
.
tar
.
chgrp
, chown
, cpio
, du
,
ls
, and tar
.
du
.
recode
.
look
.
tar
.
csplit
.
ls
, it
means to show directories themselves rather than their contents. In
rm
and ln
, it means to not treat links to directories
specially.
strip
.
strip
.
diff
.
csplit
.
wdiff
.
wdiff
.
diff
.
xargs
.
makeinfo
.
m4
.
ls
.
tar
.
xargs
.
unshar
.
diff
.
sed
.
nm
.
cpio
;
`-x' in tar
.
finger
.
su
.
m4
.
info
, gawk
, Make, mt
, and tar
;
`-n' in sed
;
`-r' in touch
.
gawk
.
ls
.
tar
.
makeinfo
.
ptx
.
tail
.
makeinfo
.
cp
, ln
, mv
, and rm
.
shar
.
ls
, time
, and ptx
.
m4
.
ptx
.
tar
.
ul
.
recode
.
install
.
tar
and shar
.
m4
.
objdump
and recode
who
.
shar
.
ls
.
who
.
diff
.
ls
;
`-x' in recode
.
diff
.
ls
.
diff
.
look
and ptx
;
`-i' in diff
and wdiff
.
ptx
.
etags
.
tee
.
diff
.
diff
.
tar
.
etags
;
`-I' in m4
.
tar
.
expand
.
diff
.
ls
.
cp
, ln
, mv
, rm
;
`-e' in m4
;
`-p' in xargs
;
`-w' in tar
.
shar
.
csplit
.
du
and ls
.
etags
.
wdiff
.
shar
.
split
.
split
, head
, and tail
.
cpio
.
gawk
.
cpio
;
`-l' in recode
.
tar
.
ls
.
su
.
ptx
.
hello
and uname
.
cpio
.
xargs
.
xargs
.
xargs
.
xargs
.
who
.
who
.
diff
.
shar
.
install
, mkdir
, and mkfifo
.
tar
.
tar
.
m4
.
shar
.
shar
.
shar
.
wdiff
.
touch
.
etags
.
wdiff
.
cp
.
wdiff
.
shar
.
gprof
.
etags
.
nm
.
makeinfo
.
gprof
.
gprof
.
shar
.
makeinfo
.
emacsclient
.
info
.
uname
.
cpio
.
objdump
.
xargs
.
cat
.
cat
.
nm
.
cpio
and ls
.
tar
.
tar
, cp
, and du
.
ptx
.
gprof
.
gprof
.
shar
.
rm
.
unshar
.
install
.
diff
.
makeinfo
.
mkdir
and rmdir
.
ul
.
cpio
.
finger
.
cpio
and tar
.
gawk
.
m4
.
csplit
.
tar
and cp
.
su
.
cpio
.
tar
.
tar
.
diff
.
cmp
.
nm
.
nm
.
wdiff
.
ed
.
shar
.
shar
ls
.
diff
.
gawk
.
tar
.
tar
.
chgrp
, chown
, cp
, ls
, diff
,
and rm
.
makeinfo
.
ptx
.
tac
and etags
.
uname
.
m4
.
objdump
.
cpio
.
xargs
.
diff
.
cpio
.
ls
and nm
.
diff
.
ptx
.
tar
.
tar
.
stty
.
ptx
.
du
.
tac
.
recode
to chose files or pipes for sequencing passes.
su
.
cat
.
diff
.
cat
.
diff
.
cat
.
ls
.
ls
.
gawk
.
tar
.
diff
.
unshar
.
shar
.
cat
.
wdiff
.
wdiff
.
tar
and diff
to specify which file within
a directory to start processing with.
wdiff
.
shar
.
recode
.
install
.
strip
.
strip
.
shar
.
cp
, ln
, mv
.
csplit
.
gprof
.
du
.
ln
.
objdump
.
m4
.
uname
.
expand
and unexpand
.
ls
.
tput
and ul
.
`-t' in wdiff
.
diff
.
shar
.
ls
and touch
.
tar
.
du
.
ranlib
, and recode
.
m4
.
hello
;
`-W traditional' in gawk
;
`-G' in ed
, m4
, and ptx
.
ctags
.
ctags
.
ptx
.
tar
.
cpio
.
m4
.
nm
.
cp
, ctags
, mv
, tar
.
gawk
; same as `--help'.
shar
.
shar
.
tar
.
cp
, ln
, mv
.
ctags
.
tar
.
shar
.
ls
and ptx
.
ptx
.
who
.
gprof
.
概して、たった数メガしかメモリを使わないなら、メモリの使用を減らす努力を 行うことに悩まないように。例えば、数メガ以上のファイルを扱うことが他の 事情で実際的でなかったら、それらを処理するのに入力ファイル全体をコアに読 み込むことは理に適っている。
しかしながら、普通に非常に大きいファイルを扱うことのある、cat
や
tail
のようなプログラムにとって、それが処理できるファイルの大きさ
を人為的に制限する手法の使用は避けることが重要だ。もしプログラムが行毎に
働き、ユーザが提供する任意の入力ファイルが与えられるならば、一行だけをメ
モリに保持するべきだ。なぜなら、これは大して難しくなく、全て一度にコアに
入るよりも大きいファイルを扱えることをユーザが望むだろうからだ。
もしあなたのプログラムが複雑なデータ構造を作るなら、単にコアにそれを作っ
て、もしmalloc
がゼロを返したら致命的なエラーにしてしまいなさい。
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |