"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 на линии, сопровождаемой кареткой.

Мне нужно одно из двух возможных решений:

  1. Это похоже на ошибку, теперь, когда я понял, что это зависит от того, сколько символов напечатано в строке, где происходит ошибка. Любое исправление / обходной путь?

  2. В качестве альтернативы, есть ли 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 выбирает самый короткий, который зависит от количества набранных символов и длины вашего приглашения.

Другие вопросы по тегам