Обробка помилок у cat не є ідеальною. Проблема в тому, що якщо неможливо дістатися до одного з файлів з якоїсь причини, діагностичне повідомлення буде виведене в кінці зчепленого виводу. Це, можливо, допустимо, якщо вивід направляється на екран, але не тоді, коли він надходить у файл або іншу програму через конвеєр.
Щоб краще справлятися з подібними ситуаціями, програмам надається другий поток виводу під назвою stderr, поряд із stdin і stdout. Вивід, записаний до stderr, як правило, з'являється на екрані навіть якщо стандартний вивід перенаправлено.
Давайте виправимо cat так, щоб писати його повідомлення про помилки до стандартної помилки.
#include <stdio.h>
/* cat: зчеплює файли, 2-а версія */
main(int argc, char *argv[])
{
FILE *fp;
void filecopy(FILE *, FILE *);
char *prog = argv[0]; /* назва програми, для помилок */
if (argc == 1 ) /* немає аргументів; копіює *
* стандартний ввід */
filecopy(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: can't open %s\n",
prog, *argv);
exit(1);
} else {
filecopy(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: error writing stdout\n", prog);
exit(2);
}
exit(0);
}
Програма сигналізує помилки в два способи. Перший: діагностичний вивід, спричиненийfprintf надходить до stderr, тож він знаходить свій шлях на екран замість того, щоб зникнути кудись через конвеєр або в файлі виводу. Ми включили в повідомлення назву програми з argv[0], тож, якщо програма використовується разом з іншими, джерело помилки буде ідентифіковано.
Другий: програма використовує функцію стандартної бібліотеки exit, яка завершує виконання програми, якщо її викликано. Аргумент exit стане доступний будь-якому процесові, який викликав даний, тож успіх чи невдача програми може перевірятись іншою програмою, яка використовує першу як дочірній процес. Традиційно, повернене значення 0 сигналізує, що все успішно; ненульові значення, звично, означають анормальні ситуації. exit викликає fclose для кожного відкритого файла виводу для того, щоб очистити будь-який буферований вивід.
Всередині main, return expr еквівалентне exit(expr). Перевага використання exitполягає в тому, що її можна викликати з інших функцій і її виклики можна знаходити за допомогою програм пошуку за шаблоном як ті, які ви знайдете у Розділі 5.
Функція ferror повертає ненульове значення, якщо відбулася помилка при обробці потоку fp.
int ferror(FILE *fp)
Хоча помилки виводу являються рідкістю, вони теж відбуваються (наприклад, якщо диск заповнено до кінця), тож виробнича програма також повинна це перевірити.
Функція feof(FILE *) є аналогічною ferror; вона повертає ненульове значення, якщо досягнуто кінця файла.
int feof(FILE *fp)
Загалом, нас не цікавив статус виходу наших маленьких ілюстративних програм, але будь-яка серйозна програма повинна піклуватися щодо повернення зрозумілих і корисних значень статусу.