我试图允许中断使readline返回某个值。这是一个最小的示例:
#include <stdio.h>
#include <signal.h>
#include <readline/readline.h>
void handler (int status)
{
rl_replace_line("word",0);
rl_redisplay();
rl_done = 1;
}
int main (int argc, char** argv)
{
char* entry;
signal(SIGINT,handler);
entry = readline("");
printf("\nEntry was: %s\n", entry);
return 0;
}
如果我运行此代码并按Control-C,则在按ENTER键之后,请确保足够打印“ Entry was:word”。但我希望这样做,而无需用户按Enter。我基本上只是想在接收到中断信号时将条目设置为“ word”,从而结束readline功能。我一直找不到任何有关如何结束readline循环并返回某个值的文档(我确定它在那里,但我没有找到它)。
我尝试添加的一件事
(*rl_named_function("accept-line"))(1,0);
在处理程序的末尾,但是它没有立即将文本发送到“ entry”。
答案 0 :(得分:2)
我不相信您可以从异步信号处理程序中回调回readline函数。 (它“似乎”起作用的事实并不能保证它不会不时地失败。)通常,您应该在信号处理程序中执行绝对最小值,例如设置一个标志以指示信号已已收到。
readline库提供变量rl_signal_event_hook
,其值是一个函数,当readline调用被信号中断时将调用该函数。将任何修改readline状态的代码放入这样的函数中可能是明智的。
但是,似乎最安全的解决方案是安排将Control-C字符直接传递到readline而不触发SIGINT。您可以基于tcgetattr
返回的termios
结构创建自定义终端设置,这可以通过取消设置ISIG
标志(这将关闭Ctrl-C到INTR函数的映射)来完成。也可以关闭其他中断字符,包括Ctrl-Z)或将c_cc[VINTR]
更改为_POSIX_VDISABLE
(或其他键)。
如果您使用的是Windows,并且没有使用包含termios
仿真的Cygwin,则可以使用native APIs启用和禁用Control-C处理。
然后,您可以使用rl_bind_key
将Ctrl-C(为3)绑定到您自己的函数。该功能需要匹配rl_command_func_t
的{{1}} typedef。该函数应返回0;否则,返回0。在简单的情况下,您可能会忽略参数,但是对于记录而言,第一个是“ count”(数字参数,按住Alt键的同时输入数字即可输入),第二个是键本身
在修改int(*)(int, int)
结构之前,您可能应该复制它,以便在完成后可以重置终端设置。通常,您会希望在每次调用termios
时安装和恢复终端设置(这也是readline
本身所做的事情)。
答案 1 :(得分:1)
我想我要在这里跑步。
#include <stdio.h>
#include <signal.h>
#include <readline/readline.h>
int event(void) { }
void handler (int status)
{
rl_replace_line("word",0);
rl_redisplay();
rl_done = 1;
}
int main (int argc, char** argv)
{
char* entry;
rl_event_hook=event;
signal(SIGINT,handler);
entry = readline("");
printf("\nEntry was: %s\n", entry);
return 0;
}
秘密是rl_done
仅在事件循环中检查。当您为它提供空事件挂钩函数时,它将检查rl_done
并退出。
答案 2 :(得分:0)
CTRL + C应该将SIGINT或类似的中断信号传递给您的程序。应该有一些方法可以覆盖该处理,例如,请参见here。
答案 3 :(得分:0)
您可以使用alternate interface来实现此目的,其中代码将进行事件循环,并在每次需要从终端读取字符时调用libreadline
函数。在事件循环中,您可以处理所有额外的异步事件,例如信号(但不仅限于此,请考虑终端聊天应用程序,其中消息从网络异步到达)。
这是它的样子:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <readline/readline.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
static volatile sig_atomic_t my_signal_flag = 0;
static int done_loop = 0;
void my_signal_handler (int status)
{
my_signal_flag = 1; // set a volaatile sig-atomic_t var
// and exit, just as the standard says
}
void my_rlhandler(char* line) // all your app is in this function
// called each time a line is ready
{
if (line && strcmp(line, "quit"))
printf("Entry was: %s\n", line);
else
{
done_loop = 1;
rl_set_prompt("");
}
free(line);
}
void my_event_loop() // event loop
// handle all async events here
// signals, network, threads, whatever
{
rl_callback_handler_install("w00t>", my_rlhandler);
do
{
signal(SIGINT, my_signal_handler); // readline may override this
// better do it here each time
fd_set readfds; // prepare the select
FD_ZERO(&readfds);
FD_SET(0, &readfds);
if (select(1, &readfds, NULL, NULL, NULL) > 0)
{
rl_callback_read_char(); // character ready, let readline eat it
}
else if (my_signal_flag )
{
my_signal_flag = 0; // can get here only after a signal
rl_replace_line("word",0);
rl_done = 1;
rl_redisplay();
rl_pending_input = '\n'; // not sure why it's needed
rl_callback_read_char();
}
}
while (!done_loop);
rl_callback_handler_remove();
}
int main (int argc, char** argv)
{
char* entry;
signal(SIGINT, my_signal_handler);
my_event_loop();
return 0;
}
虽然这看起来比其他方法更为复杂,但回调接口更适合需要处理各种事件的现实程序。