sv(file, dir) /* save file in dir */
char *file, *dir;
{
struct stat sti, sto;
int fin, fout, n;
char target[BUFSIZ], buf[BUFSIZ], *index();
sprintf(target, "%s/%s", dir, file);
if (index(file, '/') != NULL) /* strchr() in some systems */
error("won't handle /'s in %s", file);
if (stat(file, &sti) == -1)
error("can't stat %s", file);
if (stat(target, &sto) == -1) /* target not present */
sto.st_mtime = 0; /* so make it look old */
if (sti.st_mtime < sto.st_mtime) /* target is newer */
fprintf(stderr, "%s: %s not copied\n", progname, file);
else if ((fin = open(file, 0)) == -1)
error("can't open file %s", file);
else if ((fout = creat(target, sti.st_mode)) == -1)
error("can't create %s", target);
else
while ((n = read(fin, buf, sizeof buf)) > 0)
if (write(fout, buf, n) != n)
error("error writing %s", target);
close(fin);
close(fout);
}
Мы заменили стандартные функции ввода-вывода функцией
creat
, так что
sv
может сохранять режим работы входного файла. (Заметьте, что
index
и
strchr
— разные имена одной и той же процедуры; посмотрите в справочном руководстве по
string(3)
, какое имя использует ваша система.)
Хотя программа
sv
довольно специфична, в ней отражены некоторые важные идеи. Многие программы не являются системными, но тем не менее используют информацию, поддерживаемую операционной системой и доступную через системные вызовы. Для таких программ существенно, что представление информации хранится только в стандартных файлах макроопределений типа
<stat.h>
и
<dir.h>
и что в программы включены эти файлы вместо действительных описаний. Подобные программы с большей степенью вероятности переносимы с одной системы на другую.
Отметим, что по крайней мере две трети кода
sv
составляет контроль ошибок. На ранних этапах написания программы было искушение сэкономить на обработке ошибок, поскольку это отвлекает от основной задачи. Когда же программа уже работает, трудно решиться на то, чтобы вернуться назад и вставить в нее процедуры проверки, которые превращают специальную программу в унифицированную.
Программа
sv
не защищена от возможных сбоев. Она, например, не обрабатывает прерывания в неподходящие моменты, но более аккуратна, чем большинство других программ. Хотелось бы обратить ваше внимание на финальный оператор
write
. Программа редко сбивается на этом операторе, поэтому многие программы игнорируют такую возможность. Однако переполнение дискового пространства, неполадки в линии связи или иные нарушения могут вызвать ошибки в
write
, и вы гораздо лучше справитесь с ними, если программа выдает вам соответствующее сообщение.
Дело в том, что контроль ошибок весьма утомителен, но тем не менее важен. Из-за ограниченного объема книги и обширности излагаемого в ней материала мы не уделяли должного внимания этому вопросу. Но в настоящих, "производственных" программах не следует позволять себе игнорировать ошибки.
Упражнение 7.10
Модифицируйте
checkmail
так, чтобы идентифицировать посылающего сообщение: "У вас есть почта".
Подсказка:
sscanf
,
lseek
.
Упражнение 7.11
Модифицируйте
checkmail
так, чтобы она не переходила к каталогу почты перед входом в цикл. Окажет ли это ощутимое влияние на ее производительность? Более трудный вопрос: можете ли вы написать версию
checkmail
, которая обходится только одним процессом для оповещения всех пользователей?
Упражнение 7.12
Напишите программу
watchfile
, которая управляет файлом и печатает его с начала всякий раз, как он изменится. Когда бы вы ее использовали?
Упражнение 7.13
Программа
sv
очень "прямолинейна" при обработке ошибок. Модифицируйте ее так, чтобы она продолжала выполняться, даже если не удается обработать некоторый файл.
Упражнение 7.14
Сделайте
sv
рекурсивной: если один из исходных файлов — каталог, то этот каталог и все его файлы обрабатываются таким же образом. Сделайте рекурсивной
cp
. Подумайте, следует ли
cp
и
sv
объединить в одну программу, чтобы
cp -v
не создавала копию, если целевой файл новее файла-источника.
Упражнение 7.15
Напишите программу
random
.
$ random filename
должна выдавать одну строку, произвольно выбранную из файла. Если есть файл
people
, содержащий имена,
random
можно использовать в программе
scapegoat
("козел отпущения"), полезной при случайном определении виновных:
$ cat scapegoat
echo "В этом виноват `random people`!"
$ scapegoat
В этом виноват Кен!
$
Убедитесь в том, что
random
хороша независимо от распределения длины строк.
Упражнение 7.16
Помимо прочего в индексном дескрипторе указаны адреса размещения блоков файла на диске. Рассмотрите файл
<sys/into.h>
, а затем напишите программу
icat
, которая должна читать файлы, описываемые номером записи каталога и устройством диска. (Она, конечно, будет работать только в том случае, если требуемый диск открыт на чтение.) При каких обстоятельствах
icat
полезна?