Книга: Основы программирования в Linux

Дополнительные функции

Дополнительные функции

Есть небольшое число дополнительных функций для управления терминалами. Они работают непосредственно с дескрипторами файлов без необходимости считывания и записывания структур типа termios.

#include <termios.h>
int tcdrain(int fd);
int tcflow(int fd, int flowtype);
int tcflush(int fd, int in_out_selector);

Функции предназначены для следующих целей:

tcdrain — заставляет вызвавшую программу ждать до тех пор, пока не будет отправлен весь поставленный в очередь вывод;

tcflow — применяется для приостановки или возобновления вывода;

tcflush — может применяться для отказа от входных или выходных данных либо и тех, и других.

Теперь, когда мы уделили довольно много внимания структуре termios, давайте рассмотрим несколько практических примеров. Возможно, самый простой из них — отключение отображения при чтении пароля (упражнение 5.4). Это делается сбрасыванием флага echo.

Упражнение 5.4. Программа ввода пароля с применение termios

1. Начните вашу программу password.с со следующих определений:

#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#define PASSWORD_LEN 8
int main() {
 struct termios initialrsettings, newrsettings;
 char password[PASSWORD_LEN + 1];

2. Далее добавьте строку, считывающую текущие установки из стандартного ввода и копирующую их в только что созданную вами структуру типа termios:

 tcgetattr(fileno(stdin), &initialrsettings);

3. Создайте копию исходных установок, чтобы восстановить их в конце. Сбросьте флаг ECHO в переменной newrsettings и запросите у пользователя его пароль:

 newrsettings = initialrsettings;
 newrsettings.с_lflag &= ~ЕСНО;
 printf("Enter password: ");

4. Далее установите атрибуты терминала в newrsettings и считайте пароль. И наконец, восстановите первоначальные значения атрибутов терминала и выведите пароль на экран, чтобы свести на нет все предыдущие усилия по обеспечению безопасности:

 if (tcsetattr(fileno(stdin), TCSAFLUSH, &newrsettings) != 0) {
  fprintf(stderr, "Could not set attributesn");
 } else {
  fgets(password, PASSWORD_LEN, stdin);
  tcsetattr(fileno(stdin), TCSANOW, &initialrsettings);
  fprintf(stdout, "nYou entered %sn", password);
 }
 exit(0);
}

Когда вы выполните программу, то увидите следующее:

$ ./password
Enter password: You entered hello
$
 

Как это работает

В этом примере слово hello набирается на клавиатуре, но не отображается на экране в строке приглашения Enter password:. Никакого вывода нет до тех пор, пока пользователь не нажмет клавишу <Enter>.

Будьте осторожны и изменяйте с помощью конструкции X&=~FLAG (которая очищает бит, определенный флагом FLAG в переменной X) только те флаги, которые вам нужно изменить. При необходимости можно воспользоваться конструкцией X|=FLAG для установки одиночного бита, определяемого FLAG, хотя в предыдущем примере она не понадобилась.

Для установки атрибутов применяется действие TCSAFLUSH для очистки буфера клавиатуры, символов, которые пользователи вводили до того, как программа подготовилась к их считыванию. Это хороший способ заставить пользователей не начинать ввод своего пароля, пока не отключено отображение. Перед завершением программы вы также восстанавливаете первоначальные установки.

Другой распространенный пример использования структуры termios — перевод терминала в состояние, позволяющее вам считывать каждый набранный символ (упражнение 5.5). Для этого отключается канонический режим и используются параметры MIN и TIME.

Упражнение 5.5. Считывание каждого символа

Применяя только что полученные знания, вы можете изменить программу menu. Приведенная далее программа menu4.c базируется на программе menu3.c и использует большую часть кода из файла password.с, включенного в нее. Внесенные изменения выделены цветом и объясняются в пунктах описания.

1. Прежде всего, вам следует, включить новый заголовочный файл в начало программы:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
char *menu[] = {
 "a — add new record",
 "d — delete record",
 "q - quit",
 NULL,
};

2. Затем нужно объявить пару новых переменных в функции main:

int getchoice(char *greet, char *choices[], FILE *in, FILE *out);
int main() {
 int choice = 0;
 FILE *input;
 FILE *output;
 struct termios initial_settengs, new_settings;

3. Перед вызовом функции getchoice вам следует изменить характеристики терминала, этим определяется место следующих строк:

 if (!isatty(fileno(stdout))) {
  fprintf(stderr, "You are not a terminal, OK.n");
 }
 input = fopen("/dev/tty", "r");
 output = fopen("/dev/tty", "w");
 if (!input || !output) {
  fprintf(stderr, "Unable to open /dev/ttyn");
  exit(1);
 }
 tcgetattr(fileno(input), &initial_settings);
 new_settings = initial_settings;
 new_settings.c_lfag &= ~ICANON;
 new_settings.c_lflag &= ~ECHO;
 new_settings.c_cc[VMIN] = 1;
 new_settings.c_cc[VTIME] = 0;
 new_settings.c_lflag &= ~ISIG;
 if (tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {
  fprintf(stderr, "could not set attributesn");
 }

4. Перед завершением вы также должны вернуть первоначальные значения:

 do {
  choice = getchoice("Please select an action", menu, input, output);
  printf("You have chosen: %cn", choice);
 } while (choice != 'q');
 tcsetattr(fileno(input), TCSANOW, &initial_settings);
 exit(0);
}

5. Теперь, когда вы в неканоническом режиме, необходимо проверить на соответствие возвраты каретки, поскольку стандартное преобразование CR (возврат каретки) в LF (переход на новую строку) больше не выполняется:

int getchoice (char *greet, char *choices[], FILE *in, FILE *out) {
 int chosen = 0;
 int selected;
 char **option;
 do {
  fprintf(out, "Choice: %sn", greet);
  option = choices;
  while (*option) {
   fprintf(but, "%sn", *option);
   option++;
  }
  do {
   selected = fgetc(in);
  } while (selected == 'n' || selected == 'r');
  option = choices;
  while (*option) {
   if (selected == *option[0]) {
    chosen = 1;
    break;
   }
   option++;
  }
  if (!chosen) {
   fprintf(out, "Incorrect choice, select againn");
  }
 } while(!chosen);
 return selected;
}

Пока вы не устроите все иначе, теперь, если пользователь нажмет в вашей программе комбинацию клавиш <Ctrl>+<C>, программа завершится. Вы можете отключить обработку этих специальных символов, очистив флаг ISIG в локальных режимах. Для этого в функцию main включается следующая строка:

new_settings.c_lflag &= ~ISIG;

Если вы внесете эти изменения в вашу программу меню, то будете получать немедленный отклик, и вводимый вами символ не будет отображаться на экране.

$ ./menu4
Choice: Please select an action
a — add new record
d — delete record
q — quit
You have chosen: a
Choice: Please select an action
a — add new record
d — delete record
q — quit
You have chosen: q $

Если вы нажмете комбинацию клавиш <Ctrl>+<C>, символ будет передан прямо в программу и будет истолкован, как неверный выбор.

Оглавление книги


Генерация: 0.369. Запросов К БД/Cache: 2 / 0
поделиться
Вверх Вниз