snprintf(errText, BUF_SIZE, " [%s %s]",
(err > 0 && err <= MAX_ENAME)?
ename[err]: "?UNKNOWN?", strerror(err));
else
snprintf(errText, BUF_SIZE, ":");
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
if (flushStdout)
fflush(stdout); /* Сброс всего ожидающего стандартного вывода */
fputs(buf, stderr);
fflush(stderr); /* При отсутствии построчной буферизации в stderr */
}
void
errMsg(const char *format…)
{
va_list argList;
int savedErrno;
savedErrno = errno; /* В случае ее изменения на следующем участке */
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
errno = savedErrno;
}
void
errExit(const char *format…)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
void
err_exit(const char *format…)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, FALSE, format, argList);
va_end(argList);
terminate(FALSE);
}
void
errExitEN(int errnum, const char *format…)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errnum, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
void
fatal(const char *format…)
{
va_list argList;
va_start(argList, format);
outputError(FALSE, 0, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
void
usageErr(const char *format…)
{
va_list argList;
fflush(stdout); /* Сброс всего ожидающего стандартного вывода */
fprintf(stderr, "Usage: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* При отсутствии построчной буферизации в stderr */
exit(EXIT_FAILURE);
}
void
cmdLineErr(const char *format…)
{
va_list argList;
fflush(stdout); /* Сброс всего ожидающего стандартного вывода */
fprintf(stderr, "Command-line usage error: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* При отсутствии построчной буферизации в stderr */
exit(EXIT_FAILURE);
}
lib/error_functions.c
Файл ename.c.inc, подключенный в листинге 3.3, показан в листинге 3.4. В этом файле определен массив строк ename, содержащий символьные имена, соответствующие каждому возможному значению errno. Наши функции обработки ошибок используют этот массив для вывода символьного имени, соответствующего конкретному номеру ошибки. Это выход из ситуации, при которой, с одной стороны, строка, возвращенная strerror(), не идентифицирует символьную константу, соответствующую ее сообщению об ошибке, в то время как, с другой стороны, на страницах руководства дается описание ошибок с использованием их символьных имен. По символьному имени на страницах руководства можно легко найти причину возникновения ошибки.
Содержимое файла ename.c.inc конкретизировано под архитектуру, поскольку значения errno в различных аппаратных архитектурах Linux несколько различаются. Версия, показанная в листинге 3.4, предназначена для системы Linux 2.6/x86-32. Этот файл был создан с использованием сценария (lib/Build_ename.sh), включенного в исходный код дистрибутива для данной книги. Сценарий можно использовать для создания версии ename.c.inc, которая должна подойти для конкретной аппаратной платформы и версии ядра.
Обратите внимание, что некоторые строки в массиве ename не заполнены. Они соответствуют неиспользуемым значениям ошибок. Кроме того, отдельные строки в ename состоят из двух названий ошибок, разделенных слешем. Они соответствуют тем случаям, когда у двух символьных имен ошибок имеется одно и то же числовое значение.
В файле ename.c.inc мы можем увидеть, что у ошибок EAGAIN и EWOULDBLOCK одно и то же значение. (В SUSv3 на этот счет есть явно выраженное разрешение, и значения этих констант одинаковы в большинстве, но не во всех других системах UNIX.) Эти ошибки возвращаются системным вызовом в тех случаях, когда он должен быть заблокирован (то есть вынужден находиться в режиме ожидания, прежде чем завершить свою работу), но вызывающий код потребовал, чтобы системный вызов вместо входа в режим блокировки вернул ошибку. Ошибка EAGAIN появилась в System V и возвращалась системными вызовами, выполняющими ввод/вывод, операции с семафорами, операции с очередями сообщений и блокировку файлов (fcntl()). Ошибка EWOULDBLOCK появилась в BSD и возвращалась блокировкой файлов (flock()) и системными вызовами, связанными с сокетами.
В SUSv3 ошибка EWOULDBLOCK упоминается только в спецификациях различных интерфейсов, связанных с сокетами. Для этих интерфейсов в SUSv3 разрешается возвращение при неблокируемых вызовах либо EAGAIN, либо EWOULDBLOCK. Для всех других неблокируемых вызовов в SUSv3 указана только ошибка EAGAIN.
Листинг 3.4. Имена ошибок Linux (для версии x86-32)
lib/ename.c.inc
static char *ename[] = {
/* 0 */ "",
/* 1 */ "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", "E2BIG",
/* 8 */ "ENOEXEC", "EBADF", "ECHILD", "EAGAIN/EWOULDBLOCK", "ENOMEM",
/* 13 */ "EACCES", "EFAULT", "ENOTBLK", "EBUSY", "EEXIST", "EXDEV",