Листинг 58.3. Переключение терминала между режимом без обработки и cbreak
tty/tty_functions.c
#include
#include
#include "tty_functions.h" /* Объявляет определяемые здесь функции */
/* Переводим терминал, на который ссылается 'fd', в режим cbreak
(неканонический, с выключенным эхо-контролем). Мы исходим из того,
что терминал пребывает в режиме с обработкой (то есть мы не должны
вызывать эту функцию, если терминал находится в режиме без обработки,
так как она не сбрасывает все изменения, вносимые функцией ttySetRaw(),
приведенной ниже). Возвращает 0 при успешном завершении или -1 в случае
ошибки. Если аргумент 'prevTermios' не равен NULL, то должен указывать
на буфер с предыдущими параметрами терминала. */
int
ttySetCbreak(int fd, struct termios *prevTermios)
{
struct termios t;
if (tcgetattr(fd, &t) == -1)
return -1;
if (prevTermios!= NULL)
*prevTermios = t;
t. c_lflag &= ~(ICANON | ECHO);
t. c_lflag |= ISIG;
t. c_iflag &= ~ICRNL;
t. c_cc[VMIN] = 1; /* Посимвольный ввод */
t. c_cc[VTIME] = 0; /* с блокировкой */
if (tcsetattr(fd, TCSAFLUSH, &t) == -1)
return -1;
return 0;
}
/* Переводим терминал, на который ссылается 'fd', в режим без обработки
(неканонический режим с отключением любой обработки ввода и вывода).
Возвращает 0 при успешном завершении или -1 в случае ошибки. Если
аргумент 'prevTermios' не равен NULL, он должен указывать на буфер
с предыдущими параметрами терминала. */
int
ttySetRaw(int fd, struct termios *prevTermios)
{
struct termios t;
if (tcgetattr(fd, &t) == -1)
return -1;
if (prevTermios!= NULL)
*prevTermios = t;
t. c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
/* Неканонический режим, отключаем сигналы,
расширенную обработку ввода и эхо-контроль */
t. c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR |
INPCK | ISTRIP | IXON | PARMRK);
/* Отключаем интерпретацию символов CR, NL и BREAK. Урезание до 8 бит
и проверка на ошибки соответствия отсутствуют. Отключаем управление
потоком с помощью символов START/STOP. */
t. c_oflag &= ~OPOST; /* Полностью отключаем обработку вывода */
t. c_cc[VMIN] = 1; /* Посимвольный ввод */
t. c_cc[VTIME] = 0; /* с блокировкой */
if (tcsetattr(fd, TCSAFLUSH, &t) == -1)
return -1;
return 0;
}
tty/tty_functions.c
Программа, переводящая терминал в режим без обработки или cbreak, должна позаботиться о возвращении в нормальный режим после своего завершения. Помимо прочего, это подразумевает обработку всех сигналов, которые ей могут отправить, чтобы завершение работы не оказалось преждевременным (сигналы управления заданиями можно генерировать с помощью клавиатуры и в режиме cbreak).
Пример того, как это делается, показан в листинге 58.4. Данная программа выполняет следующие шаги.
• Переключает терминал либо в режим cbreak
• Если терминал был помещен в режим cbreak, то из него могут генерироваться сигналы. Их нужно обрабатывать, чтобы в случае приостановки или завершения программы терминал был возвращен в нормальный режим, привычный для пользователя. Программа устанавливает один и тот же обработчик для сигналов SIGQUIT и SIGINT
• Устанавливает обработчик для сигнала SIGTERM, который по умолчанию генерируется командой kill
• Входит в цикл, посимвольно считывающий стандартный ввод, и направляет его в стандартный вывод
• перед выводом все буквы переводятся в нижний регистр;
• символы новой строки (\n) и разрыва строки (\r) экранируются без изменений;
• управляющие символы, кроме \n и \r, экранируются в виде последовательностей из двух символов: знака ^ и соответствующей буквы в верхнем регистре (например, Ctrl+A выводится как ^A);
• остальные символы экранируются в виде звездочек (*);
• буква q приводит к завершению цикла
• При выходе из цикла программа восстанавливает состояние терминала, установленное пользователем ранее, и завершает свою работу
Программа устанавливает для сигналов SIGQUIT, SIGINT и SIGTERM один и тот же обработчик, который возвращает терминал в предыдущее состояние, и завершает работу
Обработчик сигнала SIGTSTP
• Во время ввода он сохраняет текущие параметры терминала (в переменной ourTermios)