
さてド素人の私がcoreutilsでも読むかー
coreutils のなかでも、たぶん一番シンプルなコマンドは true
だろう!そうに違いない。
Githubのソースコードはこちら。短いから全部コピペだ:
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
/* Act like "true" by default; false.c overrides this. */
#ifndef EXIT_STATUS
# define EXIT_STATUS EXIT_SUCCESS
#endif
#if EXIT_STATUS == EXIT_SUCCESS
# define PROGRAM_NAME "true"
#else
# define PROGRAM_NAME "false"
#endif
#define AUTHORS proper_name ("Jim Meyering")
void
usage (int status)
{
printf (_("\
Usage: %s [ignored command line arguments]\n\
or: %s OPTION\n\
"),
program_name, program_name);
printf ("%s\n\n",
_(EXIT_STATUS == EXIT_SUCCESS
? N_("Exit with a status code indicating success.")
: N_("Exit with a status code indicating failure.")));
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
emit_ancillary_info (PROGRAM_NAME);
exit (status);
}
int
main (int argc, char **argv)
{
/* Recognize --help or --version only if it's the only command-line
argument. */
if (argc == 2)
{
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
/* Note true(1) will return EXIT_FAILURE in the
edge case where writes fail with GNU specific options. */
atexit (close_stdout);
if (STREQ (argv[1], "--help"))
usage (EXIT_STATUS);
if (STREQ (argv[1], "--version"))
version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
(char *) nullptr);
}
return EXIT_STATUS;
}
ちなみに false コマンドはこんななってて共通化されてます:
#define EXIT_STATUS EXIT_FAILURE
#include "true.c"
おーなるほど、面白いなー、とか思ったけども、私はこーゆーのはあんまり好きじゃないな…。抽象化したなら true とか false とか意識しないコードになってるべきだけど、usage() のとこで EXIT_STATUS == EXIT_SUCCESS ? ...
みたいなコードが洩れてるから、これは抽象化というか、単によく似たコードを圧縮しただけだろう…
しかし、そこへいくとOpenBSDの true
はこれですよ:
int
main(int argc, char *argv[])
{
return (0);
}
そして false
はこれ:
int
main(int argc, char *argv[])
{
return (1);
}
まぁそうだよね、みたいなシンプルさ。逆にいうと GNUの true
に書かれているコードはGNU的に省略できない最低限のものなんだろう、きっと。
であらためてGNUのほうをみると、help
と version
オプションがおそらく必須なのと、あとは gettext
の国際化がある。ちょっと面白いのは、51行目のコメントにあるが、オプションがあるせいで true
コマンドが失敗する可能性がある、という。そうすると一応エラー用の設定もしておかねばならんとなり…
そう思うと、OpenBSDのコードはいきがって短くしてるわけでもなく、true
が失敗すると意味論的にも不明瞭になるので余計なことはしない!という気概を感じる。
ちなみに私は神は信じておりますが、OpenBSDの信者ではありません。
私はド素人なので、気になったところを少し見てくと、
initialize_main (&argc, &argv);
これは普通は何もしないようだ。OS/2のときに何かあるらしい。
set_program_name (argv[0]);
プログラム名をセットする。特定のパス(<dirname>/.libs/
とか)は削るが、普通はそのまま。コメントに書いてある(GNUはこういうコメントが丁寧だな)、例えばユーザーが
$ /some/hidden/place/bin/cp foo foo
と打ったら /some/hidden/place/bin/cp: `foo' and `foo' are the same file
というエラーメッセージになるべきだ、ということらしい。これは前から少し不思議に思ってたが、そうあるべきということだったのか。
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
ここは gettext
の設定。PACKAGE="coreutils"
で LOCALEDIR="/usr/local/share/locale"
になっていた。ちなみに翻訳ファイル (.po と .gmo) は、どこから作られたのか分からんが(というかソースコードなんかよりあのmakeファイル読むほうがよほどしんどいだろ、どう考えても…)、ビルドすると po/ja.po
に置かれる。あといつのまにか横に .gmo ファイルも作られている。なのでこれをLOCALEDIR
下にコピーすれば日本語も何となく出ている:
$ cp po/ja.gmo /usr/local/share/locale/LC_MESSAGES/coreutils.mo
あと gettext
が入れ子になっている部分があるが(下のコード)、この N_
を消すと上の文字列だけが翻訳対象になってしまうらしい。そういう謎テクニックらしい。そうなの?
printf ("%s\n\n",
_(EXIT_STATUS == EXIT_SUCCESS
? N_("Exit with a status code indicating success.")
: N_("Exit with a status code indicating failure.")));
で最後に謎なのが、この標準出力を閉じるコードなのだが…
atexit (close_stdout);
このコードはcoreutilsでは共通して呼ばれている。close_stdout
はgnulibの関数で、ちょろっと書いてあるコメントによれば、”標準出力のクローズでエラーがあるようなときは、非0で終了するのは重要なのだ、なぜなら多くのツール、特に make とかのビルド管理系は、終了ステータスで失敗を検知するから”、と。
coreutilsのコマンドはビルドでよく使われるものも多いから、念を入れてこういうコードが埋まっている、ということだろうか?少し検索すると gnulibのメーリングリストでこの件に関して文句いわれてるスレッドがある( Re: Why does close_stdout close stdout and stderr? )。話題が難しすぎて理解できないが、何か闇の入り口てきなものを感じますわ…
試しにコメントアウトしてビルドして、何か問題が起きそうなサンプルが作れるか試してみたが、ちょっとできませんでした。
true
コマンドはcoreutilsのテンプレみたいなシンプルなコマンドだけど、もうすでにGNUの深遠を感じるんだなぁ ぐぬを
コメント