Example where this is implemented: type in sleep 2
command line and press Enter, now while the sleep command is running type something, but do not press enter. When bash wakes up, it displays an invitation and then your input. A simpler dash interpreter doesn't do that. The secret is to turn off the ECHO
terminal mode, which repeats input characters, enable character input mode and implement the display of input characters on your own or using a library, such as the GNU Readline. In the character input mode, read()
will return the result when at least one key was pressed (although it also depends on the size of the buffer and the parameters of VMIN and VTIME, but in any case at least one byte is expected).
See the man tcsetattr
.
#include <termios.h> #include <poll.h> ... 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 первый символ следует игнорировать ...
This program updates the input line on the screen once a second and each time after entering the next character.
The editor is designed so that stderr (aka descriptor 2) is connected to the same terminal as stdin (aka descriptor 0). If this is not the case (the user decided to redirect the stderr output), he most likely will not see our characters when typing (unless he used a pipe in tee
or something like that). Those. in the case of stderr redirection, we may lose the echo emulation. Therefore, I used the same descriptor for tcgetattr/tcsetattr
: in the case of redirection to a file or pipe, these functions will not work and the usual line-by-line mode with echo will remain.
In the usual command line, all three descriptors are actually accessible both to read and to write (perhaps in some systems this is not the case). Full emulation will be, if you use the descriptor 0 for all operations in the editor ( read
, write
, poll
, tcgetattr
, tcsetattr
), this will allow the user to redirect the output as he pleases, if he redirects input, write
and terminal control will not work. It seems to be what you need, but it seems to me based on undocumented features.