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
,然后是插入符号。
我需要以下两种可能的解决方案之一:
这看起来确实像是一个错误,现在我意识到这取决于出错行上输入了多少个字符。有什么修复/解决方法吗?
或者,
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