"readline", когда есть выход в начале строки
Я использую (отлично) readline
(версия 6.3, режим по умолчанию [не-vi]) из моей собственной программы, запущенный в окне терминала (на ПК). Существует проблема, когда предыдущий вывод не завершен переводом строки, когда readline()
называется.
#include <stdio.h>
#include <readline/readline.h>
void main(void)
{
// Previous output from some other part of application
// which *may* have output stuff *not* terminated with a '\n'
printf("Hello ");
fflush(stdout);
char *in = readline("OK> ");
}
Итак, линия выглядит так:
Hello OK> <caret here>
Если вы введете небольшое количество символов (до 5?), А затем, скажем, Ctrl+U
(может быть и другие), чтобы удалить ваш вклад, пока все выглядит хорошо --- readline()
перемещает курсор назад сразу после его собственной подсказки. Однако попробуйте набрать, скажем:
123456 <Ctrl+U>
Теперь он удаляет обратно в Hello
оставив всего Hell
на линии, сопровождаемой кареткой.
Мне нужно одно из двух возможных решений:
Это похоже на ошибку, теперь, когда я понял, что это зависит от того, сколько символов напечатано в строке, где происходит ошибка. Любое исправление / обходной путь?
В качестве альтернативы, есть ли
readline
вызов библиотеки, который мог бы сказать мне, в какой позиции / столбце находится каретка перед вызовомreadline()
? Тогда, по крайней мере, я мог бы признать тот факт, что я нахожусь в конце существующей строки и вывести\n
чтобы сначала позиционировать себя в начале новой строки.
PS Это нормальное место, чтобы спросить о readline
программирование под Ubuntu или я должен публиковать на stackoverflow.com?
2 ответа
Оказывается, что readline
не может распознать, если он не начинается в столбце #1, и, таким образом, не позволяет ему испортить предыдущий вывод в строке.
Единственный способ справиться с этим - это самостоятельно распознать начальный столбец и перейти к началу следующей строки, если текущая позиция не является столбцом № 1. Тогда он всегда будет начинаться с самого левого столбца, без вывода ненужного символа новой строки, когда он уже находится в столбце № 1.
Мы можем сделать это для стандартного "терминала", потому что он понимает управляющую последовательность ANSI для запроса текущей строки и столбца терминала. Запрос отправляется через символы stdout
и ответ читается через символы, в которые терминал вставляет stdin
, Мы должны перевести терминал в "необработанный" режим ввода, чтобы ответные символы могли быть прочитаны немедленно и не отображались.
Итак, вот код:
rl_prep_terminal(1); // put the terminal into "raw" mode
fputs("\033[6n", stdout); // <ESC>[6n is ANSI sequence to query terminal position
int row, col; // terminal will reply with <ESC>[<row>;<col>R
fscanf(stdin, "\033[%d;%dR", &row, &col);
rl_deprep_terminal(); // restore terminal "cooked" mode
if (col > 1) // if beyond the first column...
fputc('\n', stdout); // output '\n' to move to start of next line
in = readline(prompt); // now we can invoke readline() with our prompt
[Stackoverflow был бы более подходящим местом для такого вопроса программирования]
Это не ошибка, а ожидаемое поведение. readline не знает о том, что было написано на терминале ранее и на какой позиции он пишет. Подумайте "основной последовательный терминал". Более того, другие фоновые процессы (о которых ваша программа не знает) также могут записывать в терминал.
Итак, readline предполагает, что начинает писать в начале строки терминала. Когда вы нажимаете Ctrl-U (unix-line-discard), readline возвращается туда, где он думает, что вы начали вводить символы, т.е. сразу после его запроса. Ваше приглашение "OK> " имеет длину четыре символа, поэтому readline поместит курсор на 5-е место и сотрет строку, оставив только "Ад".
Обходной путь может состоять в том, чтобы пропустить строку перед вызовом readline или начать ваше приглашение с символа CR (т.е. \r
), что приведет к появлению приглашения в начале строки, перезаписывая "Hello" (но более длинный текст будет перезаписан только частично).
[Обновить]
Что касается того, почему иногда Ctrl-U стирает только последние напечатанные символы, а иногда стирает (почти) всю строку, это оптимизация readline.
readline может выдавать две разные последовательности символов, чтобы стереть весь ввод:
- либо: n ×
<BS>
(возврат) + <контрольная последовательность для удаления всей строки> (например, ANSI<ESC> [ K
), где n - количество набранных символов. - или же:
<CR>
+ m × <управляющая последовательность для перемещения курсора вправо> (например, ANSI<ESC> [ C
) + <управляющая последовательность для удаления всей строки>, где m - длина приглашения.
readline выбирает самый короткий, который зависит от количества набранных символов и длины вашего приглашения.