FreeBSDのlsを読む -tオプションを追え!!

ls -tで更新時刻順に並び替えることが出来る。

% ls -t

と、昨日知ったので、ls -tでのソート処理を追ってみることにしました。

まずトップダウンで概要を探る。

mainから、vimの*(カーソル下の単語検索)でがが〜っとトップダウンしていく。まずは見るだけ。

cmp.cまで辿り着いた。

cmp.cはその名のとおり、比較関数の定義。mastercmp()から関数ポインタで呼ばれる。

まず、名前順で整列するnamecmpを見る。

int
namecmp(const FTSENT *a, const FTSENT *b)
{

	return (strcoll(a->fts_name, b->fts_name));
}
  • strcollはで定義されてる。文字列の比較。Rubyで言うところの<=>演算子
  • 構造体FTSENTは、で定義されてる。manを参照すべし。
  • namecmpは、ファイル構造体FTSENTから、ファイル名を読んで、比較した値を返す。
  • 落ち着いて読めばcmp.cにある関数は全て同じ処理をしている。FTSENTを比較する。
  • 逆順はrevで、aとbを逆にしただけ。

-tオプションはmodcmpで処理される。

int
modcmp(const FTSENT *a, const FTSENT *b)
{
	if (b->fts_statp->st_mtimespec.tv_sec >
	    a->fts_statp->st_mtimespec.tv_sec)
		return (1);
...(略
}
  • 面倒そうな処理を行っているが、落ち着いて読むと、 mtimeで比較してる事がわかる。
  • 構造体が入り組んでいるので読みにくい。

-tオプションの比較関数について理解することができました。

もう一度トップダウンで進んでみた。

ls -tを追う。

main
  • f_sizesort = 1;
    • サイズソートフラグを立てる。
  • sortfcn = modcmp;
    • sortfcnにmodcmp関数ポインタを代入
  • traverse();へ飛ぶ
static void traverse(int argc, char *argv[], int options)

lsのメイン処理にあたる。

  • ftsp = fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
    • fts_openは、で定義されてる。mastercmpが重要どころ。
  • fts_read()
    • 実際の処理(読んでない)
  • display()
    • 表示を呼ぶ(読んでない)
static int mastercmp(const FTSENT * const *a, const FTSENT * const *b)

比較の処理。

  • return (sortfcn(*a, *b));
    • mainで指定したsortfcn比較関数を呼ぶ。→modcmp()を呼ぶ

lsの大体の流れが把握出来てきました。

まとめと感想

今回はls -tのソート処理を追ってみました。

  • ls -rで逆順表示が出来る。
  • ls -Rで再帰的にls出来る。
  • lsは関数ポインタだらけ。
  • strcoll(3)は便利。
  • fts(3)は深い。

オプション1つについて、ボトムアップで読んでいけばそれほど辛くなく読めそうです。

次は、lsの根幹、traverse()を読んでいきたいと思います。

参考

  • Manpage of FTS
    • fts 関数は、 UNIX ファイル階層をたどるために提供されている。
    • かなり便利そう。
  • ふつうのLinuxプログラミング 10章 ディレクトリツリーのトラバース
    • fts(3)移植性の低い部類のAPIだそうです。ディレクトリツリーは"自力でトラバースしろ"と書いてある・・・。
  • Manpage of ERR
    • エラーメッセージを整形して標準エラー出力に表示する。
    • perror(3)とexit(3)を組み合わせたものらしい。便利だが非標準。
  • GNU ls
    • 激しく読めない。複雑。マクロいっぱい。でも読む。
    • ディレクトリのトラバースはreaddir(2)を使ってガリガリやってる。
    • ソートはqsort(3)を使ってあった。sort_functionsは必見。