FreeBSDのlsを読む fts(3)でlsを作ってみよう〜。

FreeBSDのlsで使われているfts(3)がやたら便利そうなので、試してみた。環境はFedora6です。

ls.c

#include <stdio.h>
#include <stdlib.h>
#include <fts.h>

int main (int argc, char *argv[])
{
	FTS *ftsp;
	FTSENT *p;
	static char dot[] = ".";
	static char *dotav[] = {dot, NULL};

	if (argc == 1)
		argv = dotav;
	else
		argv++; /* 無理矢理 */
	ftsp = fts_open(argv, 0, NULL);
	while((p = fts_read(ftsp)) != NULL) {
		printf("%s\n", p->fts_path);
	}
	fts_close(ftsp);

	exit(EXIT_SUCCESS);
}

たったこれだけで、ls完成。見た目はopendir(2),readdir(2)とさほど変わらない。

試す。

% gcc -o ls ls.c
% ./ls
.
./ls
./ls.c
./tags
.

ドットを除いていないので、見栄えは悪い。

なんと、再帰的にディレクトリをトラバースする。

% ./ls / | head
/
/.autofsck
/media
/media/.hal-mtab-lock
/media/.hal-mtab
/media
/boot
/boot/lost+found
/boot/lost+found

headは付けた方がいい

エラー処理を追加

やはり、ファイル名が見付からなかったときの位の処理は欲しい。

#include <stdio.h>
#include <stdlib.h>
#include <fts.h>
#include <err.h>

int main(int argc, char *argv[])
{
	FTS *ftsp;
	FTSENT *p;
	static char *dotav[] = {".", NULL};

	if (argc == 1)
		argv = dotav;
	else
		argv++; /* 無理矢理 */
	if ((ftsp = fts_open(argv, 0, NULL)) == NULL)
		err(EXIT_FAILURE, "fts_open");
	while((p = fts_read(ftsp)) != NULL) {
		switch(p->fts_info) {
		case FTS_NS:
		case FTS_ERR:
			err(EXIT_FAILURE, "fts_info");
		case FTS_DNR: /* 読み取り不可 */
		case FTS_D:
		case FTS_F:
		case FTS_DP:
		case FTS_SL:
		case FTS_DEFAULT:
			printf("%s\n", p->fts_path);
			break;
		default:
			/* debug */
			/* fprintf(stderr, "%d %s\n", p->fts_info, p->fts_path); */
			/* exit(EXIT_FAILURE); */
			break;
		}
	}
	fts_close(ftsp);

	exit(EXIT_SUCCESS);
}

非標準にこだわってみた。

まとめ

  • 実際コーディングすることで、traverse()の処理を把握できた。
  • fts(3)はやたら便利。
    • lsというよりfindに近い処理が可能である。
  • err(3)も便利。
  • 移植性はない。残念。

参考

これらの関数は非標準の BSD 拡張である。