home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

C言語系/「デーモン君のソース探検」読書メモ/02, getopt(3) (v1)

C言語系/「デーモン君のソース探検」読書メモ/02, getopt(3) (v1)

C言語系 / 「デーモン君のソース探検」読書メモ / 02, getopt(3) (v1)
id: 541 所有者: msakamoto-sf    作成日: 2010-01-08 13:56:50
カテゴリ: BSD C言語 

お題:"-"で始まるファイルを削除するには

rm -- -foobar

とするが、オプション解析の中身とかどうなってるの?

rmのソース何処?

$ locate rm
...
/usr/src/bin/rm/rm.c
...

オプション解析部分:

while ((ch = getopt(argc, argv, "dfiPRrW")) != -1)
    switch (ch) {
    case 'd':
        dflag = 1;
        break;
    /* ... */
    case '?':
    default:
            usage();
    }
argc -= optind;
argv += optind;

if (argc < 1)
    usage();

詳細は "man 3 getopt" 参照。
getopt(3)

getopt() 関数は、引数リストを使い果たした場合 -1 を返します。引数リストに
あるオプションの解釈は、オプション `--' (2 つのダッシュ) によって取り消す
ことができます。これは getopt() に引数処理の終わりの合図となり、-1 を返し
ます。すべてのオプションの処理が終わると (すなわち、最初のオプションでな
い引数に出会うと) getopt() は -1 を返します。

ということで、"--"に到達するとgetopt(3)が-1を返してループを抜け、通常の引数として処理されるようになる。
rmに限らず、getopt(3)を使って引数を処理しているプログラムの殆どでこの仕組みが適用されると思われる。

ついでに、"-r"オプションで再帰的削除が指示された場合は"fts関数"という、UNIXファイル階層の再帰的処理サポート関数群を使っている。Linuxではglibc2から使えるようになっている。

man 3 fts

参照:

NAME
     fts, fts_open, fts_read, fts_children, fts_set, fts_close - traverse a
     file hierarchy

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <sys/types.h>
     #include <sys/stat.h>
     #include <fts.h>
...
FTS *fts_open(...)
FTSENT *fts_read(FTS *ftsp);
FTSENT *fts_children(FTS *ftsp, int options);
int fts_set(FTS *ftsp, FTSENT *f, int options);
int fts_close(FTS *ftsp);

こんなのがあったなんて・・・知らなかった。゚(゚´Д`゚)゚。

とまれ、じゃぁgetopt(3)の中どうなってるの?(それにしてもこの本のデーモン君、好奇心旺盛だな・・・)

$ locate getopt.c
...
/usr/src/lib/libc/stdlib/getopt.c
...

他にも沢山"getopt.c"が出てきてる(他のユーザーランドプログラムのソースに含まれてたりする)が、まぁ今見たいのは標準関数のgetopt(3)なので、stdlib/getopt.cでOKだろう。

getopt.cの解説については書籍を参照。
getopt.cのソースを、実験用ソースにコピペすれば、libcの中のgetopt()ではなくてコピペした方のgetopt()を使うようになるので、色々弄れて理解も進む。
ただし、"++"/"--"演算子が式中に組み込まれている箇所がわかりにくい所もあるので注意。

例:
if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) {
...
if (*++oli != ':') {
...
else if (nargc <= ++optind) {

分解すると

++oli;
if (*oli != ':') {

になる所が一行の

if (*++oli != ':') {

にまとまっていたりする。それと条件分岐の判別が一緒になっていたりするので、そこだけ注意。

例:
if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) {

ちなみに、"--"で始まる長いオプション

foobar --opt1-name=value

みたいな指定は、GNUによる拡張であるgetopt_long(), getopt_long_only()で使える。最近のBSD系でもサポートされている。
参照:getopt_long, getopt_long_only

歴史
getopt_long() と getopt_long_only() 関数は GNU ライブラリではじめて登場し
ました。 getopt_long() の最初の BSD 実装は NetBSD 1.5 で、
getopt_long_only() の最初の BSD 実装は OpenBSD 3.3 で登場しました。
getopt_long() は FreeBSD 5.0 で、 getopt_long_only() は FreeBSD 5.2 で始
めて FreeBSD に含まれました。

$ locate getopt_long
/usr/share/man/cat3/getopt_long.0
/usr/share/man/man3/getopt_long.3
/usr/src/lib/libc/stdlib/getopt_long.3
/usr/src/lib/libc/stdlib/getopt_long.c

getopt.cに比べ、getopt_long.cは長くて複雑。読むのは諦めました・・・。



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-01-10 15:45:47
md5:14ac2a37b75e615e81296c3d5bf778b5
sha1:8697d009109fb45e52655dbdc8adc3e92c12d0f4
コメント
コメントを投稿するにはログインして下さい。