«Readline», когда вывод идет в начале строки

Я использую библиотеку readline (версия 6.3, режим по умолчанию [non-vi], Ubuntu 14.04) из собственной программы, работающей в окне терминала (на ПК). Проблема возникает, когда предыдущий вывод не завершается символом новой строки при вызове 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() перемещает курсор обратно сразу после его собственного приглашения, т.е. удаление 5 символов. Однако попробуйте ввести, скажем:

123456 <Ctrl+U>

Теперь он удаляет обратно в Hello, оставляя в строке только Hell, за которым следует каретка, т.е. удаляет 6 + 6 == 12. Итак, вы видите:

Hello OK> 123456 <Ctrl+U>
Hell<caret here>

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

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

  2. В качестве альтернативы, есть ли вызов библиотеки readline, который я мог бы сделать, который сказал бы мне, в какой позиции / столбце находится курсор, прежде чем я вызову readline()? Тогда, по крайней мере, я смог бы распознать тот факт, что я нахожусь в конце существующей строки, и вывести \n, чтобы сначала позиционировать себя в начале новой строки.

Я думаю, что могу догадаться, что до 5 набранных символов он делает до 5 пробелов, но вместо этого он выбирает сделать что-то еще, что испортит, если оно не началось в начале строки?

Я вижу GNU Readline: как очистить строку ввода? . Это такая же ситуация? Решения кажутся довольно сложными. Разве нельзя спросить, в каком столбце вы находитесь при запуске readline(), или сказать ему, чтобы он не пытался так умно удалять и придерживаться удаления только того количества символов, которое было фактически введено в него?


person JonBrave    schedule 17.12.2016    source источник
comment
Можете ли вы проверить еще раз с помощью readline 7.0? Просто установил его (по умолчанию это последняя версия) и не может воспроизвести это.   -  person Jongware    schedule 17.12.2016
comment
Ой! Я немного новичок в Ubuntu. Я использую версию 6, которая была установлена ​​с 14.04 LTS, и сделал sudo apt-get install libreadline-dev, который также сделал 6 и теперь говорит libreadline-dev is already the newest version. Извините, но как мне получить / обновить до 7, пожалуйста?   -  person JonBrave    schedule 17.12.2016
comment
Извините, я использую Mac, на котором есть свои специфические способы установки пакетов - я использовал brew для загрузки, и он автоматически выбрал самый последний источник. Достаточно ли вы уверены, чтобы попытаться выполнить загрузку и сборку вручную? Вы также можете найти самые последние журналы изменений и проверить, не было ли замечено что-то связанное с этим.   -  person Jongware    schedule 17.12.2016
comment
На самом деле я не в состоянии это сделать. Однако я думаю, что в версии 7.0 все равно было бы неправильно, потому что readline не может знать, с какого места в строке он начинается. Я верю, что это работает для вас, потому что вы используете терминал Mac, который ведет себя иначе, чем мой. В любом случае я публикую ниже код, который делает то, что я хочу, в любом readline.   -  person JonBrave    schedule 24.12.2016


Ответы (1)


Оказывается, 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
person JonBrave    schedule 24.12.2016