Иллюстрацией изложенного может служить упрощенная версия
cp
. Ее главный недостаток состоит в том, что она копирует только один файл и не разрешает использовать в качестве второго аргумента каталог. Кроме того, наша версия не сохраняет права доступа файла-источника; в дальнейшем мы покажем, как это исправить.
/* cp: minimal version */
#include <stdio.h>
#define PERMS 0644 /* RW for owner, R for group, others */
char *progname;
main(argc, argv) /* cp: copy f1 to f2 */
int argc;
char *argv[];
{
int f1, f2, n;
char buf[BUFSIZ];
progname = argv[0];
if (argc != 3)
error("Usage: %s from to", progname);
if ((f1 = open(argv[1], 0)) == -1)
error("can't open %s", argv[1]);
if ((f2 = creat(argv[2], PERMS)) == -1)
error("can't create %s", argv[2]);
while ((n = read(f1, buf, BUFSIZ)) > 0)
if (write(f2, buf, n) != n)
error("write error", (char*)0);
exit(0);
}
error
мы обсудим ниже.
Число файлов, которые одновременно могут быть открыты программой, ограничено (обычно порядка 20; см.
NOFILE
в
<SYS/param.h>
). Поэтому любая программа, которой предстоит обрабатывать много файлов, должна быть готова неоднократно использовать одни и те же дескрипторы файлов. Системный вызов
close
разрывает связь между именем и дескриптором файла, освобождая дескриптор для использования с некоторым другим файлом. Завершение программы посредством
exit
и возврат из основной программы закрывают все открытые файлы. Вызов системы unlink удаляет файл из файловой системы.
Обсуждаемые здесь системные вызовы, а по сути все системные вызовы, могут вызывать ошибки. Обычно они сигнализируют об ошибке, возвращая значение -1. Иногда полезно знать, какая именно ошибка произошла, поэтому системные вызовы, когда это приемлемо, оставляют номер ошибки во внешней целой переменной, называемой
errno
. (Значение различных номеров ошибок объясняется во введении к разд. 2 справочного руководства по UNIX.) С помощью
errno
ваша программа может определить, например, чем вызвана неудача при открытии файла — тем, что он не существует, или тем, что у вас нет разрешения на его чтение. Кроме того, есть массив символьных строк
sys_errlist
, индексируемый
errno
, который переводит число в строку, передающую смысл ошибки. Наша версия error использует эти структуры данных:
error(s1, s2) /* print error message and die */
char *s1, *s2;
{
extern int errno, sys_nerr;
extern char *sys_errlist[], *progname;
if (progname)
fprintf(stderr, "%s: ", progname);
fprintf(stderr, s1, s2);
if (errno > 0 && errno < sys_nerr)
fprintf (stderr, " (%s)", sys_errlist[errno]);
fprintf(stderr, "\n");
exit(1);
}
Errno
первоначально равна нулю и всегда должна быть меньше, чем
sys_herr.
Она не становится нулевой вновь при нормальной работе, поэтому вы должны обнулять ее после каждой ошибки, если ваша программа будет продолжать выполняться. Сообщения об ошибках в нашей версии
cp
появляются следующим образом:
$ cp foo bar
cp: can't open foo
(Нет такого файла или каталога)
$ date >foo; chmod 0 foo
Создать нечитаемый файл
$ cp too bar
cp: can't open foo
(В разрешении отказано)
$
Произвольный доступ:
lseek
Файл ввода-вывода обычно последовательный: каждый
read
или
write
занимает место в файле непосредственно после использованного при предыдущем вызове. Однако при необходимости файл может быть прочитан или записан в произвольном порядке. Системный вызов
lseek
позволяет перемещаться по файлу, не осуществляя ни чтения, ни записи:
int fd, origin;
long offset, pos, lseek();
pos = lseek(fd, offset, origin);
Текущая позиция в файле с дескриптором
fd
перемещается к позиции
offset
, которая отсчитывается относительно места, определяемого
origin
. Последующие процессы чтения или записи будут начинаться с этой позиции.
Origin
может иметь значения 0, 1, 2, задавая тем самым начало отсчета значения
offset
— от начала, от текущей позиции или от конца файла соответственно.
Возвращаемое значение есть новая абсолютная позиция или -1 при ошибке. Например, при добавлении информации в файл нужно дойти до его конца, а затем выполнить запись:
lseek(fd, 0L, 2);
Чтобы вернуться обратно к началу ("перемотать"), необходимо вызвать
lseek(fd, 0L, 0);
Для определения текущей позиции следует выполнить
pos = lseek(fd, 0L, 1);
Обратите внимание на аргумент
0L
: смещение есть длинное целое. ('l' в lseek означает 'long' — длинный, чтобы отличить его от системного вызова
seek
в шестой версии, где используются короткие целые.)