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) */
#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(). По умолчанию каталоги обрабатываются раньше файлов и подкаталогов, которые в них содержатся (предварительный обход). Чтобы избежать использования всех файловых дескрипторов вызывающего процесса, в nopenfd задаётся максимальное количество одновременно открываемых ftw() каталогов. Когда это количество превышается, работа ftw() немного замедляется, так как каталоги будут закрываться и снова открываться. Функция ftw() использует не более одного файлового дескриптора для работы с каждым уровнем дерева каталогов. Для каждого найденного элемента дерева ftw() вызывает функцию fn() с тремя аргументами: fpath, sb и typeflag. В fpath указывается полное имя элемента или в виде пути относительно рабочего каталога вызывающего процесса на момент вызова ftw() (если dirpath указан в виде относительного пути), или в виде абсолютного пути (если dirpath указан в виде абсолютного пути). В sb задаётся указатель на структуру stat, возвращаемую вызовом stat(2) для fpath. В typeflag указывается целое число, которое может быть одним из следующих:

FTW_F
    fpath обычный файл

FTW_D
    fpath каталог

FTW_DNR
    fpath каталог, который не может быть прочитан

FTW_NS
    Вызов stat(2) завершился с ошибкой для fpath, который не является символьной ссылкой. Вероятно, проблема в том, что вызывающий имеет право на чтение родительского каталога, и поэтому файл с именем fpath доступен, но не имеет права выполнения, и поэтому файл недоступен для stat(2).

    Если fpath — символьная ссылка и stat(2) завершается с ошибкой, то в POSIX.1-2001 сказано, что это неопределённая ситуация, вне зависимости от того, указан ли FTW_NS или FTW_SL в typeflag или нет.

Для остановки обхода дерева fn() возвращает ненулевое значение; оно станет возвращаемым значением для ftw(). Пока fn() возвращает 0, ftw() будет продолжать свой поиск до полного обхода дерева (в этом случае она возвратит ноль), или пока не возникнет ошибка (например, из-за malloc(3)). В этом случае функция возвратит значение -1.

Так как ftw() использует динамические структуры данных, то единственным безопасным способом для выхода из процесса обхода дерева будет возврат ненулевого значения из fn(). Для завершения обхода по сигналу без утечек памяти в обработчике нужно устанавливать глобальный флаг, проверяемый 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
        При возврате этого значения родственные элементы пропускаются и продолжается обход родителя.

    FTW_SKIP_SUBTREE
        Если fn() вызывается для элемента, являющегося каталогом (typeflag равно FTW_D), то объекты внутри каталога не будут переданы fn() в качестве аргументов. nftw() продолжит обход со следующего родственного элемента каталога.

    FTW_STOP
        Заставляет nftw() немедленно завершить работу со значением FTW_STOP.

    В будущем могут появиться другие возвращаемые значения для новых действий; fn() не должна возвращать значений, отличных от перечисленных выше. Чтобы получить определение FTW_ACTIONRETVAL из <ftw.h>, должен быть определён макрос тестирования свойств _GNU_SOURCE.

FTW_CHDIR
    Если установлен этот флаг, то будет выполняться chdir(2) для каждого каталога перед обработкой его содержимого. Это полезно, если программе требуется выполнить какое-то действие в каталоге, в котором расположен fpath (наличие данного флага не влияет на путь, который передаётся в fpath аргумента fn).

FTW_DEPTH
    Если установлен этот флаг, то производить обход в обратном порядке, т.е. вызывать fn() для обработки самого каталога после обработки содержимого его подкаталогов (по умолчанию каждый каталог обрабатывается раньше своего содержимого).

FTW_MOUNT
    Если установлен этот флаг, то оставаться в пределах одной файловой системы (т.е. не переходить в другую точку монтирования).

FTW_PHYS
    Если установлен этот флаг, то не следовать по символьным ссылкам (то, что обычно нужно). Если флаг не задан, то выполняется переход по символьным ссылкам, но ни один файл не будет обработан дважды.

    Если FTW_PHYS не задан, но задан FTW_DEPTH, то функция fn() никогда не будет вызвана для каталога, который является потомком самого себя.

Для каждого элемента дерева каталогов функция nftw() вызывает fn() с четырьмя параметрами. Аргументы fpath и sb те же, что и у ftw(). Аргумент typeflag может иметь те же значения что и у ftw(), или любое из следующих значений:

FTW_DP
    fpath является каталогом, и в flags установлен FTW_DEPTH (если FTW_DEPTH отсутствует в flags, то каталоги всегда будут просматриваться с typeflag равным FTW_D). Обработаны все файлы и подкаталоги в fpath.

FTW_SL
    fpath является символьной ссылкой и в flags установлен FTW_PHYS.

FTW_SLN
    fpath является символьной ссылкой, указывающей на несуществующий файл (это может произойти, только если не установлен флаг FTW_PHYS).

Четвёртый аргумент, передаваемый nftw() при вызове fn(), является структурой типа FTW:
struct FTW {
    int base;
    int level;
};
base — смещение на имя файла (т.е. базовая часть) в пути, заданном в fpath. level — глубина fpath в дереве каталогов относительно корня дерева (dirpath имеет глубину 0).

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении эти функции возвращают 0 и -1 при ошибке. Если fn() возвращает ненулевое значение, то обход дерева прекращается и значение, полученное от fn(), возвращается в качестве результата ftw() или nftw(). Если nftw() вызывается с флагом FTW_ACTIONRETVAL, то для прекращения обхода дерева fn() должна вернуть ненулевое значение FTW_STOP, и это значение возвращается в качестве результата nftw().

ВЕРСИИ
Функция nftw() доступна в glibc начиная с версии 2.1.

СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2001, SVr4, SUSv1. В POSIX.1-2008 функция ftw() помечена как устаревшая.

ЗАМЕЧАНИЯ
В POSIX.1-2001 отмечено, что результат непредсказуем, если fn не сохраняет текущий рабочий каталог.

Функция nftw() и использование FTW_SL с ftw() впервые появились в SUSv1.

В некоторых системах ftw() никогда не использует FTW_SL, в других FTW_SL используется только для символьных ссылок, которые не указывают на существующий файл, а ещё есть системы, в которых ftw() использует FTW_SL для каждой символьной ссылки. Для предсказуемого результата используйте nftw().

Значение FTW_F возвращается для всех объектов (файлов, символических ссылок, FIFO и т. п.), для которых можно вызвать stat, но которые не являются каталогами. Флаг FTW_ACTIONRETVAL есть только в glibc.

ПРИМЕР
Следующая программа обходит дерево каталогов начиная с пути, указанном в первом аргументе командой строки или начиная с текущего каталога, если аргумент не указан. Она отображает различную информацию о каждом файле. Во втором параметре можно указать символы, которые управляют содержимым аргумента flags у nftw().

  Исходный код программы
#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;           /* указать nftw() продолжать */
}
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)