Элемент st_mode содержит набор флажков, составляющих дополнительную информацию о файле. Определения флажков также содержатся в ‹sys/stat.h› нам потребуется только та его часть, которая имеет дело с типом файла
#define S_IFMT 0160000 /* тип файла */
#define S_IFDIR 0040000 /* каталог */
#define S_IFCHR 0020000 /* символьно-ориентированный */
#define S_IFBLK 0060000 /* блочно-ориентированный */
#define S_IFREG 0100000 /* обычный */
Теперь мы готовы приступить к написанию программы fsize . Если режимные биты ( st_mode ), полученные от stat , указывают, что файл не является каталогом, то можно взять его размер ( st_size ) и напечатать. Однако если файл - каталог, то мы должны обработать все его файлы, каждый из которых в свою очередь может быть каталогом. Обработка каталога - процесс рекурсивный.
Программа main просматривает параметры командной строки, передавая каждый аргумент функции fsize .
#include ‹stdio.h›
#include ‹string.h›
#include "syscalls.h"
#include ‹fcntl.h› /* флажки чтения и записи */
#include ‹sys/types.h› /* определения типов */
#include ‹sys/stat.h› /* структура, возвращаемая stat */
#include "dirent.h"
void fsize(char *);
/* печатает размер файлов */
main(int argc, char **argv) {
if (argc == 1) /* по умолчанию берется текущий каталог */
fsize(".");
else
while (--argc › 0)
fsize(*++argv);
return 0;
}
Функция fsize печатает размер файла. Однако, если файл - каталог, она сначала вызывает dirwalk , чтобы обработать все его файлы. Обратите внимание на то, как используются имена флажков S_IFMT и S_IFDIR из ‹sys/stat.h› при проверке, является ли файл каталогом. Здесь нужны скобки, поскольку приоритет оператора & ниже приоритета оператора ==.
int stat(char *, struct stat *);
void dirwalk(char *, void (*fcn)(char *));
/* fsize: печатает размер файла "name" */
void fsize(char *name)
{
struct stat stbuf;
if (stat(name, &stbuf) == -1) {
fprintf(stderr, "fsize: нет доступа к %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
printf("%8ld%s\n", stbuf.st_size, name);
}
Функция dirwalk - это универсальная программа, применяющая некоторую функцию к каждому файлу каталога. Она открывает каталог, с помощью цикла перебирает содержащиеся в нем файлы, применяя к каждому из них указанную функцию, затем закрывает каталог и осуществляет возврат. Так как fsize вызывает dirwalk на каждом каталоге, в этих двух функциях заложена косвенная рекурсия.
#define MAX_PATH 1024
/* dirwalk: применяет fcn ко всем файлам из dir */
void dirwalk(char *dir, void (*fcn)(char *))
{
char name[MAX_PATH];
Dirent *dp;
DIR *dfd;
if ((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: не могу открыть %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp-›name, ".") == 0 || strcmp(dp-›name, "…") == 0)
continue; /* пропустить себя и родителя */
if (strlen(dir)+strlen(dp-›name) + 2 › sizeof(name))
fprintf(stderr, "dirwalk: слишком длинное имя %s/%s\n", dir, dp-›name);
else {
sprintf(name, "%s/%s", dir, dp-›name);
(*fcn) (name);
}
}
closedir(dfd);
}
Каждый вызов readdir возвращает указатель на информацию о следующем файле или NULL, если все файлы обработаны. Любой каталог всегда хранит в себе информацию о себе самом в файле под именем "." и о своем родителе в файле под именем "…": их нужно пропустить, иначе программа зациклится. Обратите внимание: код программы этого уровня не зависит от того, как форматированы каталоги. Следующий шаг - представить минимальные версии opendir , readdir и closedir для некоторой конкретной системы. Здесь приведены программы для систем Version 7 и System V UNIX. Они используют информацию о каталоге, хранящуюся в заголовочном файле ‹sys/dir.h›, который выглядит следующим образом:
#ifndef DIRSIZ
#define DIRSIZ 14
#endif
struct direct /* элемент каталога */
{
ino_t d_ino; /* номер inode */
char d_name[DIRSIZ]; /* длинное имя не имеет '\0' */
};
Некоторые версии системы допускают более длинные имена и имеют более сложную структуру каталога.
Тип ino_tзадан с помощью typedef и описывает индекс списка узлов node . В системе, которой пользуемся мы, этот тип есть unsigned short , но в других системах он может быть иным, поэтому его лучше определять через typedef . Полный набор "системных" типов находится в ‹sys/types.h›.
Функция opendir открывает каталог, проверяет, является ли он действительно каталогом (в данном случае это делается с помощью системного вызова fstat , который аналогичен stat , но применяется к дескриптору файла), запрашивает пространство для структуры каталога и записывает информацию.
Читать дальше