#linux #c
Пользователь вводит информацию в терминале, в это время с другого процесса программы приходит сообщение и затирает данные, которые пользователь уже ввёл, но не отправил. Как после этого восстановить затертые данные и поместить их на следующую строку за пришедшим сообщением? #include#include #include #include #define MAX_LEN 25 int main(int argc, char *argv[]) { char message[MAX_LEN]; char answer[MAX_LEN] = "test\n"; pid_t pid; pid = fork(); if (pid < 0) { perror("fork()"); exit(EXIT_FAILURE); } if (pid == 0) { read(STDIN_FILENO, message, MAX_LEN); } if (pid > 0) { while (1) { sleep(2); write(STDOUT_FILENO, "\r", 1); write(STDOUT_FILENO, answer, strlen(answer)); } } return 0; }
Ответы
Ответ 1
Пример где это реализовано: наберите в командной строке sleep 2 и нажмите Enter, теперь пока команда sleep выполняется наберите что-то, но enter не нажимайте. Когда bash проснётся, он выводит приглашение а потом ваш ввод. Более простой интерпретатор dash так не умеет. Секрет состоит в отключении режима терминала ECHO, который повторяет вводимые символы, включении режима посимвольного ввода и реализации отображения вводимых символов своими силами либо с помощью библиотеки, например GNU Readline. В режиме посимвольного ввода read() будет возвращать результат, когда была нажата хотя бы одна клавиша (правда ещё зависит от размера буфера и параметров VMIN и VTIME, но в любом случае ожидается хотя бы один байт). Смотрите документацию в man tcsetattr. #include#include ... struct termios t; tcgetattr(2, &t); tcflag_t oldtcflags = t.c_lflag; t.c_lflag &= ~( ECHO | ICANON ); tcsetattr(2, TCSANOW, &t); // буфер ввода и позиция "курсора" в нём, первый символ - возврат в начало строки char input[80]="\r", i=1; // структура для poll, который используем перед read чтобы ждать ввода не более 1 секунды struct pollfd pollev= {fd:0, events:POLLIN}; // самый тупой редактор тупо заполняет буфер и повторяет его на вывод while(i<79) { char b; if(poll(&pollev, 1, 1000)) { if(read(0, &b, 1)!=1) break; input[i++]= b; } // если ничего не введено за одну секунду, всё равно обновим строку, вдруг сообщение испортило экран write(2, input, i); if(b=='\n') break; } input[i]=0; // Восстановление старого режима терминала t.c_lflag = oldtcflags; tcsetattr(2, TCSANOW, &t); // Далее в буфере input первый символ следует игнорировать ... Данная программа обновляет вводимую строку на экране раз в секунду и каждый раз после ввода очередного символа. Редактор рассчитан на то что stderr (он же дескриптор 2) связан с тем же терминалом, что и stdin (он же дескриптор 0). Если это не так (пользователь решил перенаправить вывод stderr), он вероятнее всего не увидит наши символы при вводе (если только он не использовал трубу в tee или что-то подобное). Т.е. в случае перенаправления stderr мы можем потерять эмуляцию эха. Поэтому я использовал тот же дескриптор для tcgetattr/tcsetattr: в случае перенаравления в файл или трубу эти функции не сработают и останется обычный построчный режим с эхом. В обычной командной строке все три дескриптора на самом деле доступны и на чтение и на запись (возможно в каких-то системах это не так). Полная эмуляция будет, если использовать дескриптор 0 для всех операций в редакторе (read, write, poll, tcgetattr, tcsetattr), это позволит пользователю перенаправлять вывод как угодно, если же он перенаправит ввод, write и управление терминалом не сработают. Вроде бы то, что нужно, но как мне кажется основано на недокументированных возможностях.
Комментариев нет:
Отправить комментарий