libreadline __more__分页状态下Ctrl+C无法正常退出问题
问题说明
应用基于libreadline实现timezone命令的Tab自动补全功能,补全候选项较多时会走内置__more__分页展示。分页状态下按q可以正常退出,但按Ctrl+C会触发异常:程序不会立刻退出,要等缓冲区所有条目打印完才停止,且输出乱码,复现效果如下:
$ timezone <tab> Display all 200 possibilities? (y or n) y Africa/Maputo America/Moncton Asia/Kuwait Europe/Stockholm Africa/Maseru America/Monterrey Asia/Macau Europe/Tallinn Africa/Mbabane America/Montevideo Asia/Magadan Europe/Tirane Africa/Mogadishu America/Montreal Asia/Makassar Europe/Uzhgorod Africa/Monrovia America/Montserrat Asia/Manila Europe/Vaduz --More--^C �"^TxU ^P�"^TxU ��"^TxU ��"^TxU ^P^?"^TxU ^P�"^TxU ��"^TxU ��"^TxU 0�"^TxU ^P�"^TxU ��"^TxU ��"^TxU 0�"^TxU ^P�"^TxU ��"^TxU ��"^TxU 0�"^TxU ^P�"^TxU ��"^TxU ��"^TxU 0�"^TxU ��"^TxU ��"^TxU ^P�"^TxU 0�"^TxU ��"^TxU ��"^TxU @�"^TxU
当前通过rl_attempted_completion_function注册自定义补全逻辑返回候选项列表,初始化代码如下:
rl_initialize(); printf("\n"); rl_forced_update_display();
需要实现分页状态下按Ctrl+C和按q行为一致,正常退出分页。
解决方案
异常根因:libreadline内置分页逻辑默认不处理SIGINT信号,Ctrl+C触发信号后只会中断等待用户按键的系统调用,不会终止补全项打印流程,信号打断导致的内部状态错乱、缓冲区未处理数据共同引发乱码和打印不停止问题。
两种可落地的实现方式,按需选择即可:
方式1:自定义SIGINT处理逻辑(改动最小)
直接接管SIGINT信号,检测到当前处于分页状态时,模拟q键的退出逻辑,代码如下:
#include <signal.h> #include <readline/readline.h> static void sigint_handler(int sig) { // 判断当前是否处于补全分页展示状态 if (RL_ISSTATE(RL_STATE_MORE)) { // 注入'q'字符,直接复用readline内置的q键退出分页逻辑 rl_stuff_char('q'); rl_done = 1; } else { // 非分页状态走默认Ctrl+C逻辑:清空当前输入行 rl_replace_line("", 0); rl_crlf(); rl_redisplay(); rl_done = 1; } } // 修改原有初始化逻辑 void init_readline_env() { rl_initialize(); // 关闭readline默认信号处理,避免和自定义逻辑冲突 rl_catch_signals = 0; // 注册自定义SIGINT处理 signal(SIGINT, sigint_handler); printf("\n"); rl_forced_update_display(); }
方式2:自定义补全展示回调(兼容性最好)
通过rl_completion_display_matches_hook注册自定义的补全列表打印和分页逻辑,在自己实现的分页输入捕获逻辑里,把Ctrl+C(对应ASCII码0x03)和q做相同的退出处理即可。这种方式不修改全局信号处理,不会影响程序其他模块的信号逻辑,适合复杂项目使用。
注意事项:不要在信号处理函数里调用printf、malloc这类非异步信号安全的函数,上面的实现里用到的readline接口和标记设置都是异步安全的,不会引发二次异常。
内容的提问来源于stack exchange,提问作者Harish Reddy
相关产品推荐
相关产品推荐

