В следующем и дальнейших разделах мы проиллюстрируем модель, написав очень простую версию
cat
. Она так проста, что даже не имеет опций; все, что она делает, — объединяет содержимое двух именованных файлов и выводит его в стандартный вывод. Она выводит минимум сообщений об ошибках.
Написав, мы сравним ее с V7
cat
.
Мы представим программу сверху вниз, начав с командной строки. В последующих разделах мы представим обработку ошибок, а затем перейдем к сущностным задачам, показав, каким образом осуществляется реальный файловый ввод/вывод.
4.2. Представление базовой структуры программы
Наша версия cat следует структуре, которая обычно является полезной. Первая часть начинается с комментариев, заголовочных файлов, объявлений и функции main:
6 #include <stdio.h> /* для fprintf, stderr, BUFSIZ */
7 #include <errno.h> /* объявление errno */
8 #include <fcntl.h> /* для flags для open */
9 #include <string.h> /* объявление strerror */
10 #include <unistd.h> /* для ssize_t */
11 #include <sys/types.h>
12 #include <sys/stat.h> /* для mode_t */
13
14 char *myname;
15 int process(char *file);
16
17 /* main --- перечислить аргументы файла */
18
19 int
20 main(int argc, char **argv)
21 {
22 int i;
23 int errs = 0;
24
25 myname = argv[0];
26
27 if (argc == 1)
28 errs = process("-");
29 else
30 for (i = 1; i < argc; i++)
31 errs += process(argv[i]);
32
33 return (errs != 0);
34 }
…продолжение далее в главе.
Переменная
myname
(строка 14) используется далее для сообщений об ошибках;
main
первым делом устанавливает в ней имя программы (
argv[0]
). Затем
main
в цикле перечисляет аргументы. Для каждого аргумента она вызывает функцию
process
.
Когда в качестве имени файла дано
–
(простая черточка, или знак минус),
cat
Unix
вместо попытки открыть файл с именем читает стандартный ввод. Вдобавок,
cat
читает стандартный ввод, когда нет аргументов.
ch04-cat
реализует оба этих поведения. Условие '
argc == 1
' (строка 27) истинно, когда нет аргументов имени файла; в этом случае
main
передает «
–
» функции
process
. В противном случае,
main
перечисляет аргументы, рассматривая их как файлы, которые необходимо обработать. Если один из них окажется «
–
», программа обрабатывает стандартный ввод.
Если
process
возвращает ненулевое значение, это означает, что случилась какая- то ошибка. Ошибки подсчитываются в переменной
errs
(строки 28 и 31). Когда
main
завершается, она возвращает 0, если не было ошибок, и 1, если были (строка 33). Это довольно стандартное соглашение, значение которого более подробно обсуждается в разделе 9.1.5.1 «Определение статуса завершения процесса».
Структура, представленная в
main
, довольно общая:
process
может делать с файлом все, что мы захотим. Например (игнорируя особый случай «
–
»), process также легко могла бы удалять файлы вместо их объединения!
Прежде чем рассмотреть функцию
process
, нам нужно описать, как представлены ошибки системных вызовов и как осуществляется ввод/вывод. Сама функция
process
представлена в разделе 4.4.3 «Чтение и запись».
4.3. Определение ошибок
«Если неприятность может произойти, она случается»
– Закон Мерфи -
«Будь готов»
– Бойскауты -
Ошибки могут возникнуть в любое время. Диски могут заполниться, пользователи могут ввести неверные данные, сетевой сервер, с которого осуществляется чтение, может отказать, сеть может выйти из строя и т.д. Важно всегда проверять успешность завершения каждой операции.
Основные системные вызовы Linux почти всегда возвращают при ошибке -1 и 0 или положительное значение при успехе. Это дает возможность узнать, была операция успешной или нет:
int result;
result = some_system_call(param1, param2);
if (result < 0) {
/* ошибка, что-нибудь сделать */
} else
/* все нормально, продолжить */
Знания того, что произошла ошибка, недостаточно. Нужно знать, какая произошла ошибка. Для этого у каждого процесса есть предопределенная переменная с именем
errno
. Всякий раз, когда системный вызов завершается ошибкой,
errno
устанавливается в один из набора предопределенных значений ошибок
errno
и предопределенные значения ошибок определены в файле заголовка