printf("done.\n");
exit(0);
}
Программа просматривает исходные каталоги и формирует вывод, похожий на приведенный далее (отредактированный для краткости). Для того чтобы заглянуть в каталоги других пользователей, вам могут понадобиться права доступа суперпользователя.
$ ./printdir
Directory scan of /home:
neil/
.Xdefaults
.Xmodmap
.Xresources
.bash_history
.bashrc
.kde/
share/
apps/
konqueror/
dirtree/
public_html.desktop
toolbar/
bookmarks.xml
konq_history
kdisplay/
color-schemes/
BLP4e/
Gnu_Public_License
chapter04/
argopt.с
args.с
chapter03/
file.out
mmap.с
printdir
done.
Как это работает
Большинство операций сосредоточено в функции printdir. После некоторой начальной проверки ошибок с помощью функции opendir, проверяющей наличие каталога, printdir выполняет вызов функции chdir для заданного каталога. До тех пор пока элементы, возвращаемые функцией readdir, не нулевые, программа проверяет, не является ли очередной элемент каталогом. Если нет, она печатает элемент-файл с отступом, равным depth.
Если элемент — каталог, вы встречаетесь с рекурсией. После игнорирования элементов . и .. (текущего и родительского каталогов) функция printdir вызывает саму себя и повторяет весь процесс снова. Как она выбирается из этих повторений? Как только цикл while заканчивается, вызов chdir("..") возвращает программу вверх по дереву каталогов, и предыдущий перечень можно продолжать. Вызов closedir(dp) гарантирует, что количество открытых потоков каталогов не больше того, которое должно быть.
Для того чтобы составить представление об окружении в системе Linux, обсуждаемом вmain могли бы превратить эту программу в полезный обозреватель каталогов:
int main(int argc, char* argv[]) {
char *topdir = ".";
if (argc >= 2) topdir = argv[1];
printf("Directory scan of %s\n", topdir);
printdir(topdir, 0);
printf("done.\n");
exit(0);
}
Три строки изменены и пять добавлено, но это уже универсальная утилита с необязательным параметром, содержащим имя каталога, по умолчанию равным текущему каталогу. Вы можете выполнять ее с помощью следующей командной строки:
$ ./printdir2 /usr/local | more
Вывод будет разбит на страницы, и пользователь сможет листать их. Таким образом, у него появится маленький удобный универсальный обозреватель дерева каталогов. Приложив минимум усилий, вы могли бы добавить статистический показатель использования пробелов, предельную глубину отображения и т.д.
Ошибки
Как вы видели, многие системные вызовы и функции, описанные в этой главе, могут завершиться аварийно по ряду причин. Когда это происходит, они указывают причину сбоя, задавая значение внешней переменной errno. Многие стандартные библиотеки используют эту переменную как стандартный способ оповещения о возникших проблемах. Стоит повторить, что программа должна проверять переменную errno сразу же после возникновения проблемы в функции, поскольку errno может быть изменена следующей вызванной функцией, даже если она завершилась нормально.
Имена констант и варианты ошибок перечислены в заголовочном файле errno.h. К ним относятся следующие:
□ EPERM — Operation not permitted (операция не разрешена);
□ ENOENT — No such file or directory (нет такого файла или каталога);
□ EINTR — Interrupted system call (прерванный системный вызов);
□ EIO — I/O Error (ошибка ввода/вывода);
□ EBUSY — Device or resource busy (устройство или ресурс заняты);
□ EEXIST — File exists (файл существует);
□ EINVAL — Invalid argument (неверный аргумент);
□ EMFILE — Too many open files (слишком много открытых файлов);
□ ENODEV — No such device (нет такого устройства);
□ EISDIR — Is a directory (это каталог);
□ ENOTDIR — Isn't a directory (это не каталог).
Есть пара полезных функций, сообщающих об ошибках при их возникновении: strerror и perror.
Функция strerror преобразует номер ошибки в строку, описывающую тип возникшей ошибки. Она может быть полезна для регистрации условий, вызывающих ошибку.
Далее приведена ее синтаксическая запись:
#include
char *strerror(int errnum);