(Тест '
!strcmp(s1, s2)
' является более короткой формой '
strcmp(s1, s2) == 0
', т.е. проверкой совпадения строк. Стоит заметить, что мы рассматриваем '
!strcmp(s1, s2)
' как плохой стиль. Как сказал однажды Генри Спенсер (Henry Spencer), «
strcmp()
это не boolean!».)
Когда 4.2 BSD представило новый формат файловой системы, который допускал длинные имена файлов и обеспечивал лучшую производительность, были также представлены несколько новых функций для абстрагирования чтения каталогов. Этот набор функций можно использовать независимо от того, какова лежащая в основе файловая система и как организованы каталоги. Основная ее часть стандартизована POSIX, а программы, использующие ее, переносимы между системами GNU/Linux и Unix.
5.3.1. Базовое чтение каталогов
Элементы каталогов представлены
struct dirent
(
не то же самое, что V7
struct direct
!):
struct dirent {
...
ino_t d_ino; /* расширение XSI --- см. текст */
char d_name[...]; /* О размере этого массива см. в тексте */
...
};
Для переносимости POSIX указывает лишь поле
d_name
, которое является завершающимся нулем массивом байтов, представляющим часть элемента каталога с именем файла. Размер
d_name
стандартом не указывается, кроме того, что там перед завершающим нулем может быть не более
NAME_MAX
байтов. (
NAME_MAX
определен в
<limits.h>
.) Расширение XSI POSIX предусматривает поле номера индекса
d_ino
.
На практике, поскольку имена файлов могут быть различной длины, a
NAME_MAX
обычно довольно велико (подобно 255),
struct dirent
содержит дополнительные члены, которые помогают вести на диске учет элементов каталогов с переменными длинами. Эти дополнительные члены не существенны для обычного кода.
Следующие функции предоставляют интерфейс чтения каталогов:
#include <sys/types.h> /* POSIX */
#include <dirent.h>
DIR *opendir(const char *name); /* Открыть каталог для чтения */
struct dirent *readdir(DIR *dir); /* Вернуть struct dirent за раз */
int closedir(DIR *dir); /* Закрыть открытый каталог */
void rewinddir(DIR *dirp); /* Вернуться в начало каталога */
Тип
DIR
является аналогом типа
FILE
в
<stdio.h>
. Это
непрозрачный тип, что означает, что код приложения не должен знать, что находится внутри него; его содержимое предназначено для использования другими процедурами каталогов. Если
opendir()
возвращает
NULL
, именованный каталог не может быть открыт для чтения, а errno содержит код ошибки.
Открыв переменную
DIR*
, можно использовать ее для получения указателя на
struct dirent
, представляющего следующий элемент каталога.
readdir()
возвращает
NULL
, если достигнут конец каталога
[54] или произошла ошибка.
Наконец,
closedir()
является аналогичной функции
fclose()
в
<stdio.h>
; она закрывает открытую переменную
DIR*
. Чтобы начать с начала каталога, можно использовать функцию
rewinddir()
.
Имея в распоряжении (или по крайней мере в библиотеке С) эти функции, мы можем написать небольшую программу
catdir
, которая «отображает» содержимое каталога. Такая программа представлена в
ch05-catdir.с
:
1 /* ch05-catdir.с - Демонстрация opendir(), readdir(), closedir(). */
2
3 #include <stdio.h> /* для printf() и т.д. */
4 #include <errno.h> /* для errno */
5 #include <sys/types.h> /* для системных типов */
6 #include <dirent.h> /* для функций каталога */
7
8 char *myname;
9 int process(char *dir);
10
11 /* main --- перечисление аргументов каталога */
12
13 int main(int argc, char **argv)
14 {
15 int i;
16 int errs = 0;
17
18 myname = argv[0];
19
20 if (argc == 1)
21 errs = process("."); /* по умолчанию текущий каталог */
22 else
23 for (i = 1; i < argc; i++)
24 errs += process(argv[i]);
25
26 return (errs != 0);
27 }
Эта программа вполне подобна
ch04-cat.c
(см. раздел 4.2 «Представление базовой структуры программы»); функция
main()
почти идентична. Главное различие в том, что по умолчанию используется текущий каталог, если нет аргументов (строки 20–21).
29 /*
30 * process --- сделать что-то с каталогом, в данном случае,
31 * вывести пары индекс/имя в стандартный вывод.
32 * Возвращает 0, если все OK, иначе 1.
33 */
34
35 int
36 process(char *dir)
37 {