Readline C:强制返回readline()中的某些文本

时间:2018-11-06 04:23:29

标签: c readline

我试图允许中断使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”。

4 个答案:

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

虽然这看起来比其他方法更为复杂,但回调接口更适合需要处理各种事件的现实程序。