Упражнение 5.5. Считывание каждого символа
Применяя только что полученные знания, вы можете изменить программу menu. Приведенная далее программа menu4.c базируется на программе menu3.c и использует большую часть кода из файла password.с, включенного в нее. Внесенные изменения выделены цветом и объясняются в пунктах описания.
1. Прежде всего, вам следует, включить новый заголовочный файл в начало программы:
#include
#include
#include
#include
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/tty\n");
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 attributes\n");
}
4. Перед завершением вы также должны вернуть первоначальные значения:
do {
choice = getchoice("Please select an action", menu, input, output);
printf("You have chosen: %c\n", 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: %s\n", greet);
option = choices;
while (*option) {
fprintf(but, "%s\n", *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 again\n");
}
} while(!chosen);
return selected;
}
Пока вы не устроите все иначе, теперь, если пользователь нажмет в вашей программе комбинацию клавиш +, программа завершится. Вы можете отключить обработку этих специальных символов, очистив флаг 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 $
Если вы нажмете комбинацию клавиш +, символ будет передан прямо в программу и будет истолкован, как неверный выбор.
С помощью структуры типа termios
вы управляли вводом с клавиатуры, но было бы хорошо иметь такой же уровень управления выходными данными, отображаемыми на экране терминала. В начале главы вы применяли функцию printf
для вывода символов на экран, не имея при этом возможности помещать их в определенное место экрана.
Во многих системах UNIX применяются терминалы, несмотря на то, что сегодня во многих случаях "терминал" может на самом деле быть ПК, выполняющим программу эмуляции терминала или терминальным приложением в оконной среде, таким как xterm в графической оболочке X11.
Читать дальше