time_t st_ctime; /* время последнего изменения
индексного дескриптора или файла */
}
Большинство полей поясняются примечаниями. Типы вроде
dev_t
и ino_t
определены в
, как отмечено выше. Поле st_mode
содержит множество флагов, описывающих файл; для удобства определения флагов также являются частью файла
:#define S_IFMT 0170000 /* тип файла */
#define S_IFDIR 0040000 /* каталог */
#define S_IFCHR 0020000 /* байт-ориентированный */
#define S_IFBLK 0060000 /* блок-ориентированный */
#define S_IFREG 0100000 /* регулярный */
#define S_SUID 0004000 /* установка идентификатора пользователя при
выполнении */
#define S_ISGID 0002000 /* установка идентификатора группы
при выполнении */
#define S_ISVTX 0001000 /* сохранить выгруженный текст даже после
использования */
#define S_IREAD 0000400 /* разрешение читать, владелец */
#define S_IWRITE 0000200 /* разрешение писать, владелец */
#define S_IEXEC 0000100 /* разрешение на выполнение/поиск, владелец */
Индексный дескриптор для файла доступен двум системным вызовам
stat
и fstat
. При вызове stat
параметром является имя файла, а результатом — информация из индексного дескриптора для этого файла (или — 1 при наличии ошибки). Fstat
выполняет те же функции в отношении дескриптора открытого файла (не в отношении указателя на FILE
). Иными словами,char *name;
int fd;
struct stat stbuf;
stat(name, &stbuf);
fstat(fd, &stbuf);
заполняет структуру
stbuf
информацией из индексного дескриптора для имени файла или дескриптора файла fd
.Зная все это, мы можем приступить к написанию некоторой полезной программы. Начнем с версии
checkmail
— программы на Си, которая следит за содержимым вашего почтового ящика. Если файл увеличивается, checkmail
выдает сообщение: "У вас есть корреспонденция" и включает звонок. (При уменьшении файла, видимо, из-за того, что вы успели прочитать и сбросить некоторую почтовую корреспонденцию, сообщение не требуется.) Для первого шага вы сделали вполне достаточно, а когда ваша программа заработает, вы станете знатоком./* checkmail: watch user's mailbox */
#include
#include
#include
char *progname;
char *maildir = "/usr/spool/mail"; /* system dependent */
main(argc, argv)
int argc;
char *argv[];
{
struct stat buf;
char *name, *getlogin();
int lastsize = 0;
progname = argv[0];
if ((name = getlogin()) == NULL)
error("can't get login name", (char*)0);
if (chdir(maildir) == -1)
error("can't cd to %s", maildir);
for (;;) {
if (stat(name, &buf) == -1) /* no mailbox */
buf.st_size = 0;
if (buf.st_size > lastsize)
fprintf(stderr, "\nYou have mail\007\n");
lastsize = buf.st_size;
sleep(60);
}
}
Функция
getlogin(3)
возвращает ваше регистрационное имя или NULL
, если это невозможно, checkmail
переходит к почтовому каталогу с помощью системного вызова chdir
, так что последующие вызовы stat
не должны будут "добираться" до почтового каталога через все каталоги, начиная от корневого. Возможно, вы должны адаптировать maildir
для своей системы. Мы написали checkmail
так, чтобы она работала, даже если нет почтового ящика, поскольку большинство версий mail убирают почтовый ящик в том случае, когда он пуст.Мы приводили эту программу в гл. 5 для иллюстрации циклов
shell
. Всякий раз при проверке почтового ящика она создает несколько процессов и загружает систему больше, чем хотелось бы. Версия на Си — единственный процесс, который выполняет stat
для файла каждую минуту. Сколько времени требуется на то, чтобы checkmail
постоянно выполнялась как фоновая задача? Как показали наши измерения, это время составляет меньше секунды в час, так что им вполне можно пренебречь.sv
: иллюстрация обработки ошибокСледующей мы собираемся написать похожую на
cp
программу sv
, которая будет копировать множество файлов в каталог, заменяя каждый файл лишь в том случае, если его нет в каталоге или он "старше" копируемого с тем же именем (имя sv
означает "сохранять"). Суть действия программы состоит в том, что она не переписывает новую версию файла, sv
использует больше информации из индексного дескриптора, чем checkmail
. Вызов sv
будет иметь такую конструкцию:$ sv file1 file2 ... dir
Она копирует
file1
в dir/file1
, file2
в dir/file2
и т.д., если только целевой файл не новее, чем файл-источник; в этой ситуации копирование не происходит и печатается соответствующее предупреждение. Во избежание создания большого числа копий или связанных файлов sv
не допускает применения символов '/'
в любом исходном имени файла./* sv: save new files */
#include
#include
#include
#include
char *progname;
main(argc, argv)
int argc;
char *argv[];
{
int i;
struct stat stbuf;
char *dir = argv[argc-1];
progname = argv[0];
if (argc <= 2)
error("Usage: %s files... dir", progname);
if (stat(dir, &stbuf) == -1)
error("can't access directory %s", dir);
if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
error("%s is not a directory", dir);
for (i = 1; i < argc-1; i++)
sv(argv[i], dir);
exit(0);
}
Значения времени, хранящиеся в индексных дескрипторах, исчисляются в секундах (за начало отсчета принято время 0:00 по Гринвичу, 1 января 1970 г.), так что более старые файлы имеют меньшие значения в поле
st_mtime
.sv(file, dir) /* save file in dir */
char *file, *dir;