Linux программирование в примерах - Роббинс Арнольд
Шрифт:
Интервал:
Закладка:
return 0;
}
Эта простая программа выводит текущий каталог, переходит в родительский каталог, затем выводит новый текущий каталог. (Переменная cp здесь на самом деле не нужна, но в реальной программе она была бы использована для проверки ошибок). При запуске программа создает следующий вывод:
$ <b>ch08-getcwd</b>
Current dir: /home/arnold/work/prenhall/progex/code/ch08
Changing to ..
Current dir is now: /home/arnold/work/prenhall/progex/code
Формально, если аргумент buf равен NULL, поведение getcwd() не определено. В данном случае версия GLIBC getcwd() вызовет malloc() за вас, выделяя буфер с размером size. Идя даже дальше, если size равен 0, выделяется «достаточно большой» буфер для вмещения возвращенного имени пути. В любом случае вы должны вызвать для возвращенного указателя free() после завершения работы с буфером.
Поведение GLIBC полезно, но не переносимо. Для кода, который должен работать на разных платформах, вы можете написать замещающую функцию, которая предоставляет те же самые возможности, в то же время заставив ее непосредственно вызывать getcwd() на системе с GLIBC.
Системы GNU/Linux предоставляют файл /proc/self/cwd. Этот файл является символической ссылкой на текущий каталог:
$ <b>cd /tmp</b> /* Сменить каталог */
$ <b>ls -l /рroc/self/cwd</b> /* Посмотреть на файл */
lrwxrwxrwx 1 arnold devel 0 Sep 9 17:29 /proc/self/cwd -> /tmp
$ <b>cd</b> /* Перейти в домашний каталог */
$ <b>ls -l /proc/self/cwd</b> /* Снова посмотреть на него */
lrwxrwxrwx 1 arnold devel 0 Sep 9 17:30 /proc/self/cwd -> /home/arnold
Это удобно на уровне оболочки, но представляет проблему на уровне программирования. В частности, размер файла равен нулю! (Это потому, что это файл в /proc, который продуцирует ядро; это не настоящий файл, находящийся на диске.)
Почему нулевой размер является проблемой? Если вы помните из раздела 5.4.5 «Работа с символическими ссылками», lstat() для символической ссылки возвращает в поле st_size структуры struct stat число символов в имени связанного файла. Это число может затем использоваться для выделения буфера соответствующего размера для использования с readlink(). Здесь это не будет работать, поскольку размер равен нулю. Вам придется использовать (или выделять) буфер, который, как вы полагаете, достаточно большой. Однако, поскольку readlink() не выдает символов больше, чем вы предоставили места, невозможно сказать, достаточен буфер или нет; readlink() не завершается неудачей, когда недостаточно места. (См. в разделе 5.4.5 «Работа с символическими ссылками» функцию Coreutils xreadlink(), которая решает проблему.)
В дополнение к getcwd() GLIBC имеет несколько других непереносимых процедур. Они избавляют вас от хлопот по управлению буферами и обеспечивают совместимость со старыми системами BSD. Подробности см в getcwd(3).
8.4.3. Перемещение по иерархии: nftw()
Обычной задачей программирования является обработка целых иерархий каталогов: выполнение действий над каждым файлом и каждым каталогом и подкаталогом в целостном дереве. Рассмотрите, например, команду du, которая выводит сведения об использовании диска, 'chown -R', которая рекурсивно изменяет владельцев, или программу find, которая находит файлы, подходящие по определенным критериям.
На данный момент вы знаете достаточно, чтобы написать собственный код для открывания и чтения каталогов вручную, вызова stat() (или lstat()) для каждого элемента и рекурсивной обработки подкаталогов. Однако, такой код трудно сделать правильным; можно выйти за пределы дескрипторов файлов, если вы будете оставлять родительские каталоги открытыми при обработке подкаталогов; нужно решить, обрабатывать ли символические ссылки как таковые или как файлы, на которые они указывают; нужно суметь справиться с каталогами, которые недоступны для чтения или поиска и т.д. Также трудно писать один и тот же код снова и снова, когда он требуется в нескольких приложениях.
8.4.3.1. Интерфейс nftw()
Чтобы избавиться от проблем, System V предложила функцию ftw() («file tree walk» — обход дерева файлов), ftw() осуществляла всю работу по «прохождению» дерева (иерархии) файлов. Вы предоставляете ей указатель на функцию, и она вызывает эту функцию для каждого объекта файла, с которым сталкивается. Ваша функция должна затем обработать каждый объект файловой системы, как считает нужным.
Со временем стало ясно, что интерфейс ftw() не вполне выполнял свою работу;[84] например, первоначально он не поддерживал символические ссылки. По этим причинам к X/Open Portability Guide, который теперь является частью POSIX, была добавлена nftw() («new (новая) ftw()» [важно]). Вот прототип:
#include <ftw.h> /* XSI */
int nftw(const char *dir, /* Отправная точка */
int (*fn)(const char *file, /* Указатель функции на */
const struct stat *sb, /* функцию из четырех аргументов */
int flag, struct FTW *s),
int depth, int flags); /* Максимум открытых fds, флаги */
А вот аргументы:
const char *dir
Строка с именем отправной точки иерархии для обработки.
int (*fn)(const char *file, const struct stat *sb, int flag, struct FTW *s)
Указатель на функцию с данными аргументами. Эта функция вызывается для каждого объекта в иерархии. Подробности ниже.
int depth
Этот аргумент назван неверно. Чтобы избежать выхода за пределы дескрипторов файлов, nftw() держит открытыми не более, чем depth одновременно открытых каталогов. Это не препятствует обработке nftw() иерархий, которые глубже уровня depth, но меньшие значения depth означают, что nftw() придется делать больше работы.