mind-tech

主にテクノロジーや啓発系、副業などのテーマ中心

NetBSDのソースを読んでいく rm編

テーマ「あるファイル-exampleのように"-"で始まるファイルをrm ./-example のようにファイル名の前にパスを書かずに削除するには?」

$ which rm
/bin/rm


普通にmanに "--" を記述すればファイルオプションが終了するって書いてあるけど天才中学生デーモン君は気づかなかったらしいw

とりあえず、rmのソースを見てみる。

main(int argc, char *argv[])
{
int ch, rflag;

setprogname(argv[0]);
(void)setlocale(LC_ALL, "");

Pflag = rflag = 0;
while ((ch = getopt(argc, argv, "dfiPRrW")) != -1)
switch (ch) {
case 'd':
dflag = 1;
break;
case 'f':
fflag = 1;
iflag = 0;
break;
case 'i':
fflag = 0;
iflag = 1;
break;
case 'P':
Pflag = 1;
break;
case 'R':
case 'r': /* Compatibility. */
rflag = 1;
break;
case 'W':
Wflag = 1;
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;

if (argc < 1)
usage();

checkdot(argv);

if (*argv) {
stdin_ok = isatty(STDIN_FILENO);

if (rflag)
rm_tree(argv);
else
rm_file(argv);
}

exit(eval);
/* NOTREACHED */
}


getoptをまずはmanから見てみる。

getoptはコマンドもある。関数の説明を見たいので今回はライブラリの方のmanを見る

$ man 3 getopt



ここに全部書いてあるけど、getoptは第3引数にオプション名の書式を渡す。

例えば"-f"じゃswitch文の中でfになる。またcc -o ls ls.cのようにオプションがパラメータを持つ場合も"o:"のように":"をつける。

またオプションとして用意された文字列とマッチしなければ"?"を返すのでdefaultに捕まる。

"--"の場合は-1を返り値として返すので、whileの継続条件から外れる。

またargc -= optind,argv += optindを解析する。

上記は解析したオプションを飛ばす為に必要。

例えば2つオプションがあった場合、コマンド名もいれると3つ、よって引数argcを3つ減らし、文字列のポインタの配列で、3つポインタを飛ばす、これでオプションとしてではなく引数として扱うべき所を処理できる。

以下、それを見ていく

optindはソース内のどこにも記述されていない。

manをよく見ればわかるが#include とEXAMPLEに記述があるので、そこを調べてみる。

locateで調べると/usr/include/unistd.h にあるらしい。


optindはextern intと宣言されているのでgetoptを実装しているファイルで定義されているはず。

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

getoptのソースを見てみる

全部載せると長いので要点だけ。


getopt(nargc, nargv, ostr)
int nargc;
char * const nargv[];
const char *ostr;
{
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
......
......

まず、関数内にstaticな変数placeがある、これは関数の内外のどちらに位置するかで意味が変わる事に注意。

両方、staticを付けると他ファイルからアスセスできない事からprivateと同義だが、関数内の場合、普通は関数が呼び出される度に毎回新しく変数は生成される一方、プログラムが動いている間ずっと存在する、つまり前の呼び出しで代入された値を利用可能。

また、これだと毎回呼び出し時にplaceにEMSGが代入されると思いきや、関数にstaticな変数はコンパイル時に初期化される、そして呼び出しの時はこの行は無い事と同じとみなされる。

またplaceは下記のplace = nargv[optind]と記述されているようにオプションを指す

if (optreset || !*place) {              /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}


また、 if (optreset || !*place) { の!*place は*place == "¥0"と同義で文字列の終わりだったらという意味、

ここで文字列の最後かどうか確かめる意味は、例えばrm -r -f dir と使われるだけならいいがrm -rf dirとされた場合、rの次のオプション、fの場所を覚えていないといけない、よってplaceは関数にstaticな変数でないといけないし-r -fの場合rの次は"\o"なので上記のif文を通ってplaceが-fを指すようになる。

また、ここを通ってオプションの先頭文字が"-"でなければ-1を返スコとでgetopt()の呼び出し側ではオプションの解析が終了し、残りは引数になる。

そして以下の場所で次の文字を調べて"-"の場合、オプション全体では"--"となるのでここで-1を返し終了している。
if (place[1] && *++place == '-' /* found "--" */
&& place[1] == '\0') {
++optind;
place = EMSG;
return (-1);
}

そしてここでoptoptに実際のオプション文字が代入され、オプションの書式ostr中にこのオプションがあるかどうかstrchrで探している

if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':')
(void)fprintf(stderr,
"%s: unknown option -- %c\n", getprogname(),
optopt);

次にBADCH(最初の方で"?"と定義されてある)を返している、これは書式の中にオプション文字があってその次の文字が":"でないならそのオプションはパラメータを取らない

return (BADCH);




よってこの部分でoptindを1進め、最後にoptoptつまりオプション文字を返している

if (!*place)
++optind;
}
else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (opterr)
(void)fprintf(stderr,
"%s: option requires an argument -- %c\n",
getprogname(), optopt);
return (BADCH);
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt);


やっぱgetoptのソースはっとこ。

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
#else
__RCSID("$NetBSD: getopt.c,v 1.21 2001/04/24 09:07:43 joda Exp $");
#endif
#endif /* LIBC_SCCS and not lint */

#include "namespace.h"

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifdef __weak_alias
__weak_alias(getopt,_getopt)
#endif

int opterr = 1, /* if error message should be printed */
optind = 1, /* index into parent argv vector */
optopt, /* character checked for validity */
optreset; /* reset getopt */
char *optarg; /* argument associated with option */

#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""

/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt(nargc, nargv, ostr)
int nargc;
char * const nargv[];
const char *ostr;
{
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */

_DIAGASSERT(nargv != NULL);
_DIAGASSERT(ostr != NULL);

if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-' /* found "--" */
&& place[1] == '¥0') {
++optind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':')
(void)fprintf(stderr,
"%s: unknown option -- %c¥n", getprogname(),
optopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (opterr)
(void)fprintf(stderr,
"%s: option requires an argument -- %c¥n",
getprogname(), optopt);
return (BADCH);
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}

Copyright ©mind-teck All rights reserved..
🔒プライバシーポリシー