FTW(3)

名前
ftw, nftw - ファイルツリーを歩きまわる

書式
#include <ftw.h>

int ftw(const char *dirpath,
        int (*fn) (const char *fpath, const struct stat *sb,
                   int typeflag),
        int nopenfd);

#define _XOPEN_SOURCE 500   /* feature_test_macros(7) 参照 */
#define _XOPEN_SOURCE 500
#include <ftw.h>

int nftw(const char *dirpath,
        int (*fn) (const char *fpath, const struct stat *sb,
                   int typeflag, struct FTW *ftwbuf),
        int nopenfd, int flags);

説明
ftw() は、 dirpath で指定されたディレクトリ以下のディレクトリツリー全体を歩きまわり、 ツリー中でエントリーが見付かるごとに、 fn() を呼び出す。 デフォルトでは、ディレクトリそのものが、そのディレクトリにあるファイルや サブディレクトリよりも先に処理される (行きがけ順探索; preorder traversal)。 呼び出し元プロセスが利用可能なファイルディスクリプターを使い切って しまわないようにするため、 ftw() が同時にオープンするディレクトリの最大数を nopenfd で指定することができる。 探索の深さがこの値を越えると、 一つのディレクトリを閉じてから他のディレクトリをオープンし直すこと になるので、 ftw() の動作は遅くなる。 ftw() は、ディレクトリツリーの階層 1 レベルにつき、 最大でも一つのファイルディスクリプターしか使用しない。 ディレクトリツリーで見つかったエントリー毎に、 ftw() は fpath, sb, typeflag の 3つを引き数として fn() を呼び出す。 fpath はエントリーのパス名である。 dirpath が相対パス名で指定された場合には、 fpath は ftw() が呼び出された時点の呼び出し元プロセスのカレントワーキングディレクトリ からの相対パス名となる。 dirpath が絶対パス名で指定された場合には、 fpath は絶対パス名となる。 sb は fpath に対する stat(2) の呼び出しで返される stat 構造体へのポインターである。 typeflag は整数で、以下の値のいずれか一つである:

FTW_F
    fpath が通常のファイルである

FTW_D
    fpath がディレクトリである

FTW_DNR
    fpath が読み込みできないディレクトリである

FTW_NS
    stat(2) の呼び出しがシンボリックリンクでない fpath で失敗した。 これのよくある原因は、呼び出し元が親ディレクトリに対する読み込み許可を持っており、 ファイル名 fpath を見ることができたが、実行許可は持っておらず、 そのため stat(2) ではそのファイルに到達できなかった、というものである。

    fpath がシンボリックリンクで、かつ stat(2) が失敗した場合、 FTW_NS と FTW_SL (後述) のどちらが typeflag に渡されるかは未定義であると、POSIX.1-2001 には書かれている。

ツリーの探索を止めたい場合は、 fn() が 0 以外の値を返せば良い (この値は ftw() 自身の戻り値となる)。 それ以外の場合は ftw() はツリー全体の探索を続け、すべてのツリーを探索し終えたところで 0 を返す。探索中に (malloc(3) の失敗などの) エラーが起こると -1 を返す。

ftw() は動的なデータ構造を用いるので、ツリー探索を安全に中断する唯一の方法は 0 以外の値を fn() の返り値とすることである。割り込みを扱うには、 例えば発生した割り込みをマークしておいて、 0 以外の値を返すようにする シグナルによりメモリーリークを起こさずに探索を終了できるようにするには、 シグナルハンドラーで fn() がチェックするグローバルなフラグをセットするようにすればよい。 プログラムを終了させる場合以外は、 longjmp(3) を使用しないこと。

  nftw()
関数 nftw() は ftw() と同じだが、引き数 flags が追加される点と、 fn() の引き数に ftwbuf が追加される点が異なる。 この flags 引き数は下記のフラグの 0 個以上の論理和を取ったものである:

FTW_ACTIONRETVAL (glibc 2.3.3 以降)
    このフラグは glibc 固有である。 このフラグをセットすると、 nftw() の fn() の返り値の扱いが変わる。 fn() は以下の値のいずれか一つを返す必要がある。

    FTW_CONTINUE
        nftw() は通常通り処理を続ける。

    FTW_SKIP_SIBLINGS
        fn() がこの値を返した場合、処理中のエントリーの兄弟 (同じ階層のエントリー) の処理はスキップされ、親ディレクトリで続きの処理が行われる。

    FTW_SKIP_SUBTREE
        fn() が呼び出されたエントリーがディレクトリ (typeflag が FTW_D) の場合に、この値を返すと fn() の引き数として渡されたディレクトリ内のエントリーの処理が行われなくなる。 nftw() は処理中のディレクトリの兄弟 (同じ階層のエントリー) から処理を続ける。

    FTW_STOP
        nftw () は、返り値 FTW_STOP ですぐに復帰する。

    他の返り値は将来新しい動作に対応付けられる可能性がある。 fn() は上記のリストにある値以外を返さないようにすべきである。 <ftw.h> で FTW_ACTIONRETVAL の定義が有効にするためには、 (「どの」ヘッダーファイルをインクルードするよりも前に) 機能検査マクロ _GNU_SOURCE を定義しなければならない。

FTW_CHDIR
    セットされると、ディレクトリの内容を処理する前に そのディレクトリに chdir(2) する。このフラグは、 fpath が属すディレクトリで何らかの動作を実行する必要がある場合に 便利である。 (このフラグを指定しても fn の fpath 引き数で渡されるパス名には影響しない。)

FTW_DEPTH
    セットされると、帰りがけ順探索 (post-order traversal) を行う。 つまり、ディレクトリそのものを引き数とした fn() 呼出しは、そのディレクトリに含まれるファイルとサブディレクトリに 対する処理の「後で」行われる (デフォルトでは、ディレクトリ自身の処理はディレクトリ内のエントリー より「前に」行なわれる)。

FTW_MOUNT
    セットされると、同じファイルシステムの中だけを探索対象とする (つまり、マウントポイントをまたぐことはない)。

FTW_PHYS
    セットされると、シンボリックリンクを辿らない (おそらくこちらが 通常望ましい動作だろう)。セットされていないとシンボリックリンクを 辿るが、同じファイルが二回報告されることはない。

    FTW_PHYS がセットされずに FTW_DEPTH がセットされると、自分自身に対するシンボリックリンクを配下に持つ ディレクトリに対して fn() が呼び出されることは決してない。

ディレクトリツリーのエントリー毎に、 nftw() は 4つの引き数で fn() を呼び出す。 fpath と sb は ftw() と同じである。 typeflag には、 ftw() で取り得る値のいずれか、または以下の値のいずれかが渡される:

FTW_DP
    fpath がディレクトリで、かつ flags で FTW_DEPTH が指定されていた (FTW_DEPTH が flags に指定されていなかった場合、 ディレクトリに対しては常に typeflag が FTW_D で fn() が呼び出される)。 fpath 配下のファイルとサブディレクトリは全て処理が終わっている。

FTW_SL
    fpath がシンボリックリンクで、かつ FTW_PHYS が flags に セットされていた。

FTW_SLN
    fpath がシンボリックリンクで、存在しないファイルを指している (これがセットされるのは FTW_PHYS がセットされていない場合だけである)。

nftw() が fn() を呼び出す際に渡す 4つめの引き数は FTW 型の構造体である。
struct FTW {
    int base;
    int level;
};
base は、ファイル名 (basename 要素) の、 fpath で渡されるパス名の中でのオフセットである。 level はディレクトリツリーでの fpath の深さを示す。深さはディレクトリツリーのトップ (root) からの 相対値である (dirpath は深さ 0 である)。

返り値
これらの関数は、成功すると 0 を、エラーが発生すると -1 を返す。 fn() が 0 以外を返した場合、ディレクトリツリーの探索を終了し、 fn() が返した値を ftw() や nftw() の結果として返す。 nftw() が FTW_ACTIONRETVAL フラグ付きで呼ばれた場合、ツリーの探索を終了させるために fn() が使用できる、非 0 の値は FTW_STOP だけであり、 この値は nftw() の返り値として返される。

バージョン
nftw() は バージョン 2.1 以降の glibc で利用できる。

準拠
POSIX.1-2001, SVr4, SUSv1. POSIX.1-2008 は ftw() を廃止予定としている。

注意
POSIX.1-2001 の注記によると、 fn がカレントワーキングディレクトリを保持しなかった場合の 結果は規定されていないとされている。

nftw() 関数と、 ftw() における FTW_SL は、SUSv1 で導入された。

ftw() で FTW_SL を一切使わないシステムや、 存在しないファイルを指しているシンボリックリンクの場合にのみ FTW_SL を使うシステム、また ftw() が全てのシンボリックリンクに対して FTW_SL を使うシステムもある。 予測可能な動作をさせるためには、 nftw() を使うこと。

「stat できるがディレクトリではないオブジェクト」 (ファイル, シンボリックリンク, fifo 等) に対しては、すべて FTW_F が返される。 FTW_ACTIONRETVAL は glibc 固有である。

例
以下のプログラムは、一つ目のコマンドライン引き数を名前に持つパス以下の ディレクトリツリーを探索する。引き数が指定されなかった場合は、 カレントディレクトリ以下を探索する。 各々のファイルについて様々の情報が表示される。 二番目のコマンドライン引き数に文字を指定することで、 nftw() を呼び出す際に flags 引き数に渡す値を制御することができる。

  プログラムのソース
#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
static int
display_info(const char *fpath, const struct stat *sb,
             int tflag, struct FTW *ftwbuf)
{
    printf("%-3s %2d %7jd   %-40s %d %s\n",
        (tflag == FTW_D) ?   "d"   : (tflag == FTW_DNR) ? "dnr" :
        (tflag == FTW_DP) ?  "dp"  : (tflag == FTW_F) ?   "f" :
        (tflag == FTW_NS) ?  "ns"  : (tflag == FTW_SL) ?  "sl" :
        (tflag == FTW_SLN) ? "sln" : "???",
        ftwbuf->level, (intmax_t) sb->st_size,
        fpath, ftwbuf->base, fpath + ftwbuf->base);
    return 0;           /* To tell nftw() to continue */
}
int
main(int argc, char *argv[])
{
    int flags = 0;
    if (argc > 2 && strchr(argv[2], 'd') != NULL)
        flags |= FTW_DEPTH;
    if (argc > 2 && strchr(argv[2], 'p') != NULL)
        flags |= FTW_PHYS;
    if (nftw((argc < 2) ? "." : argv[1], display_info, 20, flags)
            == -1) {
        perror("nftw");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

関連項目
stat(2), fts(3), readdir(3)

この文書について
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。