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) */
...
int i, pagesize = PAGESIZE;
progname = argv[0];
if (argc > 1 && argv[1][0] == '-') {
pagesize = atoi(&argv[1][1]);
argc--;
argv++;
}
Функция atoi
превращает строку символов в целое число (см. справочное руководство по atoi(3)
).
Еще одно средство временно остановить вывод на экран в конце каждой страницы, чтобы выполнить какую-либо иную команду. По аналогии с ed
и многими другими программами, если пользователь печатает строку, начинающуюся восклицательным знаком, остальная часть строки воспринимается как команда и передается shell
для выполнения. Данное средство также тривиально, поскольку для этой цели предусмотрена функция system(3)
, речь о которой пойдет ниже. Модифицированная версия ttyin
такова:
ttyin() /* process response from /dev/tty (version 2) */
{
char buf[BUFSIZ];
FILE *efopen();
static FILE *tty = NULL;
if (tty == NULL)
tty = efopen("/dev/tty", "r");
for (;;) {
if (fgets(buf,BUFSIZ,tty) == NULL || buf[0] == 'q')
exit(0);
else if (buf[0] == '!') {
system(buf+1); /* BUG here */
printf("!\n");
else /* ordinary line */
return buf[0];
}
}
К сожалению, эта версия ttyin
имеет серьезный недостаток. Команда, запущенная с помощью system
, получает стандартный входной поток от p
, так что если p
читает из программного канала или файла, их входные потоки могут мешать друг другу:
$ cat /etc/passwd | p -1