print(stdin, PAGESIZE);
else
for (i = 1; i < argc; i++) {
fp = efopen(argv[i], "r");
print(fp, PAGESIZE);
fclose(fp);
}
exit(0);
}
Функция efopen
реализует весьма общую операцию: пытается открыть файл. Если же это невозможно, она выводит на печать сообщение об ошибке, и ее выполнение завершается. Чтобы обеспечить выдачу сообщений об ошибках, идентифицирующих программу, в которой происходит (или произошла) ошибка, efopen
ссылается на внешнюю строку progname
, где содержится имя программы, устанавливаемое в main
:
FILE *efopen(file, mode) /* fopen file, die if can't */
char *file, *mode;
{
FILE *fp, *fopen();
extern char *progname;
if ((fp = fopen(file, mode)) != NULL)
return fp;
fprintf(stderr, "%s: can't open file %s mode %s\n",
progname, file, mode);
exit(1);
}
Мы испытали две версии программы efopen
, прежде чем остановиться на данной. Одна из них должна была после печати сообщения завершиться, возвратив нулевой указатель, свидетельствующий о неудаче. Это позволяет вызвавшей программе продолжить свое выполнение или завершиться. Другая версия снабжала efopen
третьим аргументом, указывающим, следует ли возвращаться после того, как файл открыть не удалось. Почти во всех наших примерах, однако, нет смысла продолжать работу, если файл недоступен, так что текущая версия efopen
является для нас наилучшей.
Непосредственное выполнение команды p
осуществляется в print
:
print(fp, pagesize) /* print fp in pagesize chunks */
FILE *fp;
int pagesize;
{
static int lines = 0; /* number of lines so far */
char buf[BUFSIZ];
while (fgets(buf, sizeof buf, fp) != NULL)
if (++lines < pagesize)
fputs(buf, stdout);
else {
buf[strlen(buf)-1] = '\0';
fputs(buf, stdout);
fflush(stdout);
ttyin();
lines = 0;
}
}
Мы использовали здесь BUFSIZ
, который определен в как размер буфера входного потока. Функция fgets(buf, size, fp)
выбирает следующую строку входного потока из fp
до символа перевода строки (включая его) в буфер и добавляет завершающий символ \0
. Копируется на более size - 1
символов. По достижении конца файла возвращается NULL
. (Конструкция fgets
оставляет желать лучшего: она возвращает buf
вместо счетчика символов и, кроме того, выдает предупреждение о том, что входная строка была слишком длинной. Символы не потеряны, но вы должны взглянуть на buf
, чтобы понять, что в самом деле случилось.)
Функция strlen
возвращает длину строки, поэтому мы можем отбросить завершающий символ перевода строки последней входной строки. После вызова fputs(buf, fp)
строка buf
записана в файл fp
. При вызове fflush
в конце страницы происходит вывод буферизованного выходного текста.
Считывание ответа пользователя в конце каждой страницы возложено на функцию ttyin
. Функция ttyin
не может читать стандартный входной поток, тогда как p
должна выполняться, даже если входной поток поступает из файла или конвейера. Чтобы справиться с этим, программа открывает файл /dev/tty
, которому поставлен в соответствие пользовательский терминал при любом переключении стандартного входного потока. Приведенная ниже функция ttyin
возвращает первую букву ответа, но здесь это свойство не используется.
ttyin() /* process response from /dev/tty (version 1) */
{
char buf[BUFSIZ];
FILE *efopen();
static FILE *tty = NULL;
if (tty == NULL)
tty = efopen("/dev/tty", "r");
if (fgets(buf, BUFSIZ, tty) == NULL || buf[0] == 'q')
exit(0);
else /* ordinary line */
return buf[0];
}
Указатель на файл devtty
описан как статический, так что его значение сохраняется от одного вызова ttyin
до другого; файл /dev/tty
открывается только при первом вызове.
Очевидно, есть дополнительные средства, которые без особых усилий можно ввести в p
, однако наша первая версия этой программы только печатает 22 строки и ждет следующей порции. Прошло немало времени, прежде чем в нее были добавлены другие средства, но в настоящее время ими мало кто пользуется. В частности, весьма простое дополнение ввод переменной pagesize
для хранения числа строк на странице. Значение переменной можно установить из командной строки
$ p -n...
Она печатает порции по n
строк. Для этого требуется лишь добавить несколько знакомых вам операторов в начале main
:
/* p: print input in chunks (version 2) */
Читать дальше
Конец ознакомительного отрывка
Купить книгу