Linux программирование в примерах
Строка 49 сохраняет начальный каталог для дальнейшего использования, пользуясь
.getcwd()Строка 51 вычисляет число дескрипторов, которые может использовать
. Мы не хотим, чтобы она использовала все доступные дескрипторы файлов, если функция обратного вызова также хочет открывать файлы. В вычислении используетсяnftw()(см. раздел 4.4.1 «Понятие о дескрипторах файлов») для получения максимально возможного числа и вычета из негоgetdtablesize(), который был вычислен ранее в строке 13.SPARE_FDSЭта процедура служит основанием для больших объяснений. В обычном случае по крайней мере три дескриптора уже используются для стандартного ввода, стандартного вывода и стандартной ошибки.
нужно некоторое количество дескрипторов файлов для открытия и чтения каталогов; внутри себяnftw()используетopendir()при открытии каталогов для чтения. Если функции обратного вызова также нужно открывать файлы, мы должны предотвратить израсходование функциейopen()всех доступных дескрипторов файлов для открывания каталогов. Мы делаем это, вычитая некоторое число из максимально допустимого. Для данного примера мы выбрали пять, но если функции обратного вызова нужно открывать файлы, должно использоваться большее число, (nftw()знает, как восстановиться при израсходовании дескрипторов файлов; мы не должны беспокоиться о таком случае.)nftw()Строки 52–58 являются главным циклом над нашими аргументами; строки 53–57 проверяют ошибки; когда они появляются, код выводит диагностическое сообщение и увеличивает значение переменной
.errorsСтроки 60–64 являются частью эксперимента с
, выводящего начальный и конечный каталоги, если было использованоFTW_CHDIR.-сПо-настоящему интересной функцией является
; это функция обратного вызова, которая обрабатывает каждый файл. Она использует базовый шаблон для функции обратного вызоваprocess(), который является операторомnftw()для значенияswitch.flag69 /* process --- выводит каждый файл на нужном уровне */7071 int process(const char "file, const struct stat *sb,72 int flag, struct FTW *s)73 {74 int retval = 0;75 const char *name = file + s->base;7677 printf("%*s", s->level * 4, ""); /* сделать отступ */7879 switch (flag) {80 case FTW_F:81 printf("%s (file)\n", name);82 break;83 case FTW_D:84 printf("%s (directory)\n", name);85 break;86 case FTW_DNR:87 printf("%s (unreadable directory)\n", name);88 break;89 case FTW_SL:90 printf("%s (symbolic link)\n", name);91 break;92 case FTW_NS:93 printf("%s (stat failed): %s\n", name, strerror(errno));94 break;95 case FTW_DP:96 case FTW_SLN:97 printf("%s: FTW_DP or FTW_SLN: can't happen'\n", name);98 retval = 1;99 break;100 default:101 printf("%s: unknown flag %d: can't happen'\n", name, flag);102 retval = 1;103 break;104 }105106 return retval;107 }Строка 75 использует '
' для получения имени из полного пути. Это значение указателя сохраняется в переменнойfile + s->baseдля повторного использования в функции.nameСтрока 77 делает отступ нужного размера, используя красивый трюк. Используя
,%*sполучает от первого аргумента ширину поля. Это вычисляется динамически как 'printf()'. Строка, которая должна быть выведена — «», пустая строка. Конечным результатом является то, чтоlevel * 4создает для нас отступ нужного размера без необходимости запуска цикла.printf()Строки 79–104 являются оператором
. В данном случае он не делает ничего весьма интересного, кроме вывода имени файла и его типа (файл, каталог и т.д.)switchХотя эта программа не использует
, должно быть ясно, что вы могли бы сделать в функции обратного вызова все, что хотите.struct statЗАМЕЧАНИЕ. Джим Мейеринг (Jim Meyering), сопроводитель GNU Coreutils, замечает, что дизайн
несовершенен из-за ее рекурсивной природы. (Она рекурсивно вызывает себя при обработке подкаталогов.) Если иерархия каталогов становится действительно глубокой, в диапазоне уровней 20 000–40 000 (!),nftw()может выйти за пределы размера стека, уничтожив программу. Есть также и другие проблемы, связанные с дизайномnftw(). Версия GNU Coreutils после 5.0 исправляет это путем использования набора процедурnftw()(см. fts(3)).fts()8.5. Обход дерева файлов: GNU
duGNU версия
в GNU Coreutils используетduдля обхода одной или более иерархии файлов, собирая и выводя сведения, касающиеся количества используемого дискового пространства. У нее большое число опций, которые управляют ее поведением но отношению к символическим ссылкам, форматом вывода чисел и т.д. Это делает разбор кода труднее, чем могло бы быть при более простой версии. (Однако, мы не собираемся позволить этому остановить нас.) Вот сводка опцийnftw(), которые вскоре будут полезны, когда мы рассмотрим код.du