Выбрать главу

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 {

38  DIR *dp;

39  struct dirent *ent;

40

41  if ((dp = opendir(dir)) == NULL) {

42   fprintf(stderr, "%s: %s: cannot open for reading: %s\n",

43   myname, dir, strerror(errno));

44   return 1;

45  }

46

47  errno = 0;

48  while ((ent = readdir(dp)) != NULL)

49   printf("%8ld %s\n", ent->d_ino, ent->d_name);

50

51  if (errno != 0) {

52   fprintf(stderr, "%s: %s: reading directory entries: %s\n",

53   myname, dir, strerror(errno));

54   return 1;

55  }

56

57  if (closedir(dp) != 0) {

58   fprintf(stderr, "%s: %s: closedir: %s\n",

59    myname, dir, strerror(errno));

60   return 1;

61  }

62

63  return 0;

64 }

Функция process() делает всю работу и большую часть кода проверки ошибок. Основой функции являются строки 48 и 49:

while ((ent = readdir(dp)) != NULL)

printf("%8ld %s\n", ent->d_ino, ent->d_name);

Этот цикл читает элементы каталога, по одной за раз, до тех пор, пока readdir() не возвратит NULL. Тело цикла отображает для каждого элемента номер индекса и имя файла. Вот что происходит при запуске программы:

$ ch05-catdir /* По умолчанию текущий каталог */

639063 .

639062 ..

639064 proposal.txt

639012 lightsabers.url

688470 code

638976 progex.texi

639305 texinfo.tex

639007 15-processes.texi

639011 00-preface.texi

639020 18-tty.texi

638980 Makefile

639239 19-i18n.texi

...

Вывод никаким образом не сортируется; он представляет линейное содержимое каталога. (Как сортировать содержимое каталога мы опишем в разделе 6.2 «Функции сортировки и поиска»).

5.3.1.1. Анализ переносимости

Есть несколько соображений по переносимости. Во-первых, не следует предполагать, что двумя первыми элементами, возвращаемыми readdir(), всегда будут '.' и '..'. Многие файловые системы используют организацию каталогов, которые отличаются от первоначального дизайна Unix, и '.' и '..' могут быть в середине каталога или даже вовсе не присутствовать[55].

Во-вторых, стандарт POSIX ничего не говорит о возможных значениях d_info. Он говорит, что возвращенные структуры представляют элементы каталогов для файлов; это предполагает, что readdir() не возвращает пустые элементы, поэтому реализация GNU/Linux readdir() не беспокоится с возвратом элементов, когда 'd_ino == 0'; она переходит к следующему действительному элементу.

Поэтому по крайней мере на системах GNU/Linux и Unix маловероятно, что d_ino когда-нибудь будет равен нулю. Однако, лучше по возможности вообще избегать использования этого поля.

Наконец, некоторые системы используют d_fileno вместо d_ino в struct dirent. Знайте об этом, когда нужно перенести на такие системы код, читающий каталоги.

Косвенные системные вызовы

«Не пробуйте это дома, дети!»

- М-р Wizard -

Многие системные вызовы, такие, как open(), read() и write(), предназначены для вызова непосредственно из кода пользователя: другими словами, из кода, который пишете вы как разработчик GNU/Linux.

вернуться

54

То есть прочитаны все элементы каталога — Примеч. науч. ред.

вернуться

55

В системах GNU/Linux могут монтироваться файловые системы многих операционных систем, не относящихся к Unix. Во многих коммерческих системах Unix также можно смонтировать файловые системы MS-DOS. В таких случаях предположения относительно файловых систем Unix неприменимы — Примеч. автора.