Обратите внимание, что программное управление потоком включает три флага.
IXON
Прекратить пересылать выходные данные при получении символа STOP (обычно ^S) и начать заново при получении символа START (обычно ^Q).
IXOFF
Передать символ STOP, когда во входящем буфере накопится слишком много данных. В случае прочтения достаточного количества данных отправите символ START.
IXANY
Позволить любому принятому символу, не только START, перезапустить вывод. (Этот флаг обычно реализован в системах Unix, но не определен в POSIX.)
Когда другая программа использует
robin
как вспомогательную, может помешать обработка спецсимволов (
robin
обычно интерпретирует последовательность
^\
), поэтому во избежание такой обработки устанавливается неформатируемый режим. В строке 120 мы предоставляем переменную, определяющую, включен ли неформатируемый режим; по умолчанию этот режим не включается. В строке 140 мы сообщаем popt о том, как информировать, когда в командной строке была указана опция
– r
или
– raw
, включающая неформатируемый режим.
Некоторые системы требуют передачи им символа возврата каретки для представления новой строки. Слово "системы" здесь следует понимать буквально; например, это применимо ко многим интеллектуальным периферийным устройствам, например, источникам бесперебойного питания (UPS) с последовательными портами, поскольку они предназначены для функционирования в DOS, где пара "возврат каретки/перевод строки" всегда используется для обозначения новой строки. В строке 228 определяется это DOS-ориентированное поведение.
228: if (crnl) {
229: /* послать CR с NL */
230: pts.c_oflag |= ONLCR;
231: }
Последняя часть обработки опций управляет скоростью передачи в битах в секунду [111] . Вместо включения огромного вложенного оператора выбора мы вызываем уже описанную ранее функцию
symbolic_speed
, чтобы получить
speed_t
, понимаемый
termios
, как показано в строке 233.
111
Обратите внимание: "бит в секунду" ("бит/с"), а не "бод". Бит в секунду определяет интенсивность передачи информации. Бод является техническим термином, описывающим смены фаз в течение секунды. Бод не соответствует
termios
, но слово бод, к сожалению, попало в названия некоторых флагов
termios
, не рассматриваемых в этой книге.
233: /*
скорость не изменяется, пока не будет указано -b */
234: if (speed) {
235: cfsetospeed(&pts, symbolic_speed(speed));
236: cfsetispeed(&pts, symbolic_speed(speed));
237: }
Перед тем, как зафиксировать изменения наших копий структур
termios
, в строке 241 регистрируются обработчики для важных сигналов, которые могут в противном случае вызвать уничтожение процесса и оставить tty в неформатируемом состоянии. Более подробно обработчики сигналов рассматриваются в главе 12.
241: sact.sa_handler = cleanup_termios;
242: sigaction(SIGHUP, &sact, NULL);
243: sigaction(SIGINT, &sact, NULL);
244: sigaction(SIGPIPE, &sact, NULL);
245: sigaction(SIGTERM, &sact, NULL);
Как только обработчик сигналов окажется на месте для восстановления старых настроек
termios
в случае уничтожения
robin
, мы можем благополучно обновить установки
termios
.
248: tcsetattr(pf, TCSANOW, &pts);
249: tcsetattr(STDIN_FILENO, TCSANOW, &sts);
На этом этапе программа
robin
готова читать и записывать символы. У
robin
есть два файловых дескриптора для чтения: данные с последовательного порта и данные с клавиатуры. Для мультиплексирования ввода-вывода между четырьмя файловыми дескрипторами используется
poll
(см. главу 13).
Цикл
poll
делает упрощенческое предположение о том, что он всегда может записать столько, сколько в состоянии прочитать. Это почти всегда верно и не вызывает проблем на практике, поскольку блокирование на короткие периоды незаметно при нормальных условиях. Цикл никогда не считывает из файлового дескриптора, пока
poll
не сообщит, что файловый дескриптор ожидает считывания данных, чтобы мы знали, что блокировки во время чтения нет.
Для данных, поступающих с клавиатуры, может понадобиться обработка управляющих последовательностей перед записью, если пользователь не выбрал неформатируемый режим при запуске
robin
. Вместо включения этого кода в цикл мы вызываем функцию
cook_buf
(строка 78), которая при необходимости обращается к
send_escape
(строка 58). Обе эти функции просты. Единственный трюк состоит в том, что
cook_buf
может быть вызвана один раз с управляющим символом, а затем второй раз с интерпретируемым символом, а также в оптимизации количества вызовов функции
write
.
Функция
cook_buf
вызывает функцию
send_escape
один раз для каждого символа, которому предшествует неотменяемый управляющий символ
^\
. Символ
q
восстанавливает исходные установки
termios
и завершается вызовом обработчика сигнала (с фальшивым номером сигнала 0), что восстанавливает настройки
termios
перед выходом. Символ
b
генерирует состояние разрыва, которое является длинной строкой, состоящей из нулей. Любой другой символ, включая второй управляющий символ
^\
, передается в последовательный порт без изменений.