fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
Четыре флага termios контролируют четыре отдельных части управления вводом и выводом. Флаг входных данных, с_iflag, определяет, каким образом интерпретируются и обрабатываются принятые символы. Флаг выходных данных, c_oflag, определяет, каким образом интерпретируются и обрабатываются символы, записываемые вашим процессом в tty. Управляющий флаг, c_cflag, определяет характеристики последовательного протокола устройства и полезен лишь для физических устройств. Локальный флаг, c_lflag, определяет, каким образом символы собираются и обрабатываются перед отправкой на обработку выходных данных. На рис. 16.1 показана упрощенная схема того, какое место занимает каждый флаг в общей схеме обработки символов.
Рис. 16.1. Упрощенная схема обработки tty
Сначала мы продемонстрируем способы применения termios, а затем представим короткую справку о нем.
16.3. Примеры использования termios
16.3.1. Пароли
Самой распространенной причиной модификации установок termios является чтение пароля без эхо-контроля символов. Для этого следует отключить локальное эхо во время чтения пароля. Ваш код должен выглядеть следующим образом:
struct termios ts, ots;
Первая структура хранит оригинальные установки для восстановления, а вторая является копией для модификации.
tcgetattr(STDIN_FILENO, &ts);
Обычно пароли читаются со стандартного устройства ввода.
ots = ts;
Сохраните копию оригинальных установок termios, чтобы позже восстановить их.
ts.c_lflag &= ~ECHO;
ts.c_lflag |= ECHONL;
tcsetattr(STDIN_FILENO, TCSAFLUSH, fits);
Отключите эхо-контроль символов (кроме символов новой строки) после завершения обработки всех выходных данных. (Первая l в c_lflag означает локальную (local) обработку.)
read_password();
Теперь вы читаете пароль. Это может быть простой вызов fgets() или read(), либо же более сложная обработка, в зависимости от режима tty (неформатируемый режим или режим обработки) и от требований программы.
tcsetattr(STDIN_FILENO, TCSANOW, &ots);
Это немедленно восстанавливает исходные установки termios. (Остальные опции объясняются позже, в справочном разделе далее в главе.)
Полный код программы-примера, readpass, показан ниже.
1: /* readpass.с */
2:
3: #include <stdio.h>
4: #include <stdlib.h>
5: #include <termios.h>
6: #include <unistd.h>
7:
8: int main (void) {
9: struct termios ts, ots;
10: char passbuf[1024];
11:
12: /* получить и сохранить текущие настройки termios */
13: tcgetattr(STDIN_FILENO, &ts);
14: ots = ts;
15:
16: /* изменить и установить новые настройки termios */
17: ts.c_lflag & = ~ECHO;
18: ts.c_lflag |= ECHONL;
19: tcsetattr(STDIN_FILENO, TCSAFLUSH, &ts);
20:
21: /*хоть это и параноидально, но проверить, возымели ли эффект новые настройки*/
22: tcgetattr(STDIN_FILENO, &ts);
23: if (ts.c_lflag & ECHO) {
24: fprintf(stderr, "Сбой при отключении эхо-контроляn");
25: tcsetattr(STDIN_FILENO, TCSANOW, &ots);
26: exit(1);
27: }
28:
29: /* получить и вывести пароль */
30: printf("введите пароль:");
31: fflush(stdout);
32: fgets(passbuf, 1024, stdin);
33: printf("прочитан пароль: %s", passbuf);
34: /* в passbuf был завершающий символ n */
35:
36: /* восстановить старые настройки termios */
37: tcsetattr(STDIN_FILENO, TCSANOW, &ots);
38:
39: exit(0);
40: }
16.3.2. Последовательные коммуникации
В качестве примера программирования обоих концов tty рассмотрим программу, подключающую текущий терминал к последовательному порту. На одном tty программа под названием robin сообщается с вами во время набора. На другом tty она взаимодействует с последовательным портом. С целью мультиплексирования вводных и выходных данных на локальном tty и последовательном порте программа использует системный вызов poll(), описанный в главе 13.
Ниже приведен полный код программы robin.с, за которым даны объяснения.
1: /* robin.с */
2:
3: #include <sys/poll.h>
4: #include <errno.h>
5: #include <fcntl.h>
6: #include <popt.h>
7: #include <stdio.h>
8: #include <stdlib.h>
9: #include <signal.h>
10: #include <string.h> /* для strerror() */
11: #include <termios.h>
12: #include <unistd.h>
13:
14: void die(int exitcode, const char *error, const char *addl) {
15: if (error) fprintf(stderr, "%s: %sn", error, addl);
16: exit(exitcode);
17: }
18:
19: speed_t symbolic_speed(int speednum) {
20: if (speednum >= 460800) return B460800;
21: if (speednum >= 230400) return B230400;
22: if (speednum >= 115200) return B115200;
23: if (speednum >= 57600) return B57600;
24: if (speednum >= 38400) return B38400;
25: if (speednum >= 19200) return B19200;
26: if (speednum >= 9600) return B9600;
27: if (speednum >= 4800) return B4800;
28: if (speednum >= 2400) return B2400;
29: if (speednum >= 1800) return B1800;
30: if (speednum >= 1200) return B1200;
31: if (speednum >= 600) return B600;
32: if (speednum >= 300) return B300;
33: if (speednum >= 200) return B200;
34: if (speednum >= 150) return B150;
35: if (speednum >= 134) return B134;
36: if (speednum >= 110) return B110;
37: if (speednum >= 75) return B75;
38: return B50;
39: }
40:
41: /* Это нужно для области видимости в пределах файла, так что
42: * их можно будет использовать в обработчиках сигналов */
43: /* старые настройки порта termios для восстановления */
44: static struct termios pots;
45: /* старые настройки stdout/stdin termios для восстановления */
46: static struct termios sots;
47: /* файловый дескриптор порта */
48: int pf;
49:
50: /* восстановить первоначальные настройки терминала при выходе */
51: void cleanup_termios(int signal) {
52: tcsetattr(pf, TCSANOW, &pots);
53: tcsetattr(STDIN_FILENO, TCSANOW, &sots);
54: exit(0);
55: }
56:
57: /* обработать одиночный управляющий символ */
58: void send_escape(int fd, char c) {
59: switch (c) {
60: case 'q':
61: /* восстановить настройки termios и выйти */
62: cleanup_termios(0);
63: break;
64: case 'b':
65: /* послать символ разрыва*/
66: tcsendbreak(fd, 0);
67: break;
68: default:
69: /* пропустить символ */
70: /* "C- C-" sends "C-" */
71: write(fd, &c, 1);
72: break;
73: }
74: return;
75: }
76:
77: /* обработать управляющие символы, записывая их в вывод */
78: void cook_buf(int fd, char * buf, int num) {
79: int current = 0;
80: static int in_escape = 0;
81:
82: if (in_escape) {
83: /* cook_buf последний раз вызывался с незавершенной
84: управляющей последовательностью */
85: send_escape(fd, buf[0]);
86: num--;
87: buf++;
88: in_escape = 0;
89: }
90: while (current < num) {
91: # define CTRLCHAR(c) ((c)-0x40)
92: while ((current < num) && (buf[current] != CTRLCHAR('W')))
93: current++;
94: if (current) write (fd, buf, current);
95: if (current < num) {
96: /* найден управляющий символ */
97: current++;
98: if (current >= num) {
99: /*интерпретировать первый символ следующей последовательности*/
100: in_escape = 1;
101: return;
102: }
103: send_escape(fd, buf[current]);
104: }
105: num -= current;
106: buf += current;
107: current = 0;
108: }
109: return;
110: }
111:
112: int main(int argc, const char * argv[]) {
113: char с; /* используется для разбора аргументов */
114: struct termios pts; /* настройки termios для порта */
115: struct termios sts; /* настройки termios для stdout/stdin */
116: const char *portname;
117: int speed = 0; /* используется при разборе аргументов для скорости */
118: struct sigaction sact; /* используется для инициализации обработчика сигналов */