当行首有输出时“readline”

当行首有输出时“readline”

readline我在自己的程序中使用 (excellent) (版本 6.3,默认 [非 vi] 模式) 库,在终端窗口 (PC 上) 中运行。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:这是个可以询问 Ubuntu 编程问题的地方吗readline?还是我应该发帖到stackoverflow.com

答案1

[对于此类编程问题,Stackoverflow 可能更合适]

这不是一个错误,而是预期的行为。readline 不知道之前在终端上写了什么,也不知道它在什么位置写入。想想“基本串行终端”。此外,其他后台进程(您的程序不知道)也可能写入终端。

因此,readline 假定它从终端行的开头开始写入。当您按 Ctrl-U (unix-line-discard) 时,readline 会返回到它认为您开始输入字符的位置,即提示符之后。您的提示符“OK>”有四个字符长,因此 readline 将插入符号放在第 5 位并删除该行,只留下“Hell”。

解决方法可能是在调用 readline 之前跳过一行,或者用 CR 字符(即\r)开始提示,这将强制在行首提示,覆盖“Hello”(但较长的文本只会被部分覆盖)。

[更新]

至于为什么有时 Ctrl-U 只会删除最后输入的字符,而有时它会删除(几乎)整行,这是 readline 优化。

readline 可以发出两个不同的字符序列来删除整个输入:

  • 或者:n × <BS>(退格键) + <用于删除整行的控制序列> (例如 ANSI <ESC> [ K),其中 n 是迄今为止输入的字符数。
  • 或者:<CR>+ m × <将光标向右移动的控制序列> (例如 ANSI <ESC> [ C)+ <删除整行的控制序列>,其中 m 是提示的长度。

readline 选择最短的,这取决于输入的字符数以及提示的长度。

答案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

相关内容