【问题标题】:Readline C: force return of certain text in readline()Readline C:强制返回 readline() 中的某些文本
【发布时间】:2018-11-06 04:23:29
【问题描述】:

我试图允许中断导致 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);

在处理程序的末尾,但它没有立即将文本发送到“条目”。

【问题讨论】:

  • 我认为你可以通过使用getch()实现你自己的readLine()来实现这一点。
  • 没错,但我希望继续使用该库的所有其他功能(简单的历史记录、行编辑等)。
  • 如果你得到 ^C,你想终止读取,这样你就可以返回而不用回车?
  • @Chris 为什么不实现我们自己的编译器和操作系统呢?
  • 没错。我希望 readline() 返回一些预先指定的值。

标签: c readline


【解决方案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并退出。

【讨论】:

  • 函数应该返回int
  • 测试了这个答案,它也可以工作(一旦你将 void 事件更改为 int 事件)
【解决方案2】:

我不相信可以保证您可以从异步信号处理程序回调到 readline 函数。 (它“似乎”工作的事实并不能保证它不会不时发生灾难性的失败。)一般来说,您应该在信号处理程序中做绝对最小值,例如设置一个标志以指示信号有已收到。

readline 库提供了变量rl_signal_event_hook,其值是一个函数,当readline 调用被信号中断时将调用该函数。将任何修改 readline 状态的代码放入这样的函数中可能是明智的。

但这里似乎最安全的解决方案是安排将 Control-C 字符直接传递到 readline 而不触发 SIGINT。您可以根据 tcgetattr 返回的 termios 结构创建自定义终端设置,该结构关闭 Ctrl-C 到 INTR 函数的映射,方法是取消设置 ISIG 标志(这也将关闭其他中断字符, 包括 Ctrl-Z) 或将 c_cc[VINTR] 更改为 _POSIX_VDISABLE (或其他键)。

如果您在 Windows 上并且没有使用 Cygwin,其中包括 termios 仿真,您可以使用 native APIs 来启用和禁用 Control-C 处理。

然后您可以使用rl_bind_key 将Ctrl-C(即3)绑定到您自己的函数。该函数需要匹配rl_command_func_t typedef,即int(*)(int, int)。该函数应返回 0;在您的简单情况下,您可能可以忽略参数,但为了记录,第一个是“计数”(数字参数,通过在按住 Alt 键的同时键入数字输入),第二个是键本身.

您可能应该在修改 termios 结构之前对其进行复制,以便在完成后重置终端设置。通常,您需要在每次调用 readline 时安装和恢复终端设置(readline 本身也是如此)。

【讨论】:

  • 不简单且不可扩展。特米奥斯?我在 Windows 上,我该怎么办? :) 如果您需要处理其他信号,而不是来自键盘的信号怎么办?如果您的用户更喜欢将 INTR 映射到 Ctrl+Break 怎么办?
  • @n.m.:readline 在 Windows 上工作吗?这个问题是关于 readline,而不是一般的信号处理。
  • 是的,有一个 Windows 端口,大部分 API 都在工作(不是全部)。
  • 我的用户不使用 Windows,而是使用默认键发送中断,就像一个好的用户:)。所以这个答案对我有用 - 谢谢!
【解决方案3】:

CTRL+C 应该将 SIGINT 或类似的中断信号传递给您的程序。应该有办法覆盖处理,例如参见here

【讨论】:

  • 是的(请参阅我在问题中使用“signal(SIGINT,handler);”的示例),我正在寻找具体如何让 readline() 在中断时返回特定文本。
【解决方案4】:

您可以通过使用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;
}

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

【讨论】:

    猜你喜欢
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-16
    • 1970-01-01
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多