博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
GNU Readline库函数的应用示例
阅读量:6941 次
发布时间:2019-06-27

本文共 7801 字,大约阅读时间需要 26 分钟。

 

说明

     GNU Readline是一个跨平台开源程序库,提供交互式的文本编辑功能。应用程序借助该库函数,允许用户编辑键入的命令行,并提供自动补全和命令历史等功能。Bash(Bourne Again Shell)、GDB、ftp 和mail等程序就使用Readline库提供其命令行界面。

     Readline是GNU通用公共许可证版本3(GNU GPLv3)下的自由软件。这意味着若发布或分发的程序使用Readline库,则该程序必须是自由软件,并持有GPL-兼容的许可证。当然,用户也可使用自己的实现替代库的行为。

     本文将基于若干典型的Readline库函数,给出一个简单的交互式调测器示例。示例代码的运行环境如下:

 

 

 

一  函数介绍

     本节简要介绍后文将要用到的几个Readline库函数,如用来替代fgets()的readline()函数。关于库函数的更多描述可参考。

     readline()函数的ANSI C声明如下:

char *readline(const char *prompt);

     该函数打印参数prompt 所指的提示字符串,然后读取并返回用户输入的单行文本(剔除换行符)。若prompt 为空指针或指向空字符串,则不显示任何提示。若尚未读取到字符就遇到EOF,则该函数返回NULL;否则返回由malloc()分配的命令行内存,故调用结束后应通过free()显式地释放内存

     读取命令行后,可调用add_history()函数将该行存入命令行历史列表中,以便后续重取。

void add_history(const char *string);

     rl_completion_matches()函数用于自动补全:

typedef char *rl_compentry_func_t(const char *text, int state);

char **rl_completion_matches(const char *text, rl_compentry_func_t *entry_func);

     该函数返回一个字符串数组,即参数text的补全列表;若没有补全项,则函数返回NULL。该数组以空指针结尾,首个条目将替换text,其余条目为可能的补全项。函数指针entry_func指向一个具有两个参数的函数。其中参数state在首次调用时为零,后续调用时为非零。未匹配到text时,entry_func返回空指针。

     函数指针rl_completion_entry_function指向rl_completion_matches()所使用的补全生成函数:

rl_compentry_func_t *rl_completion_entry_function;

     若其值为空,则使用默认的文件名补全生成函数,即rl_filename_completion_function()。

     应用程序可通过函数指针rl_attempted_completion_function自定义补全函数:

typedef char **rl_completion_func_t(const char *text, int start, int end);

rl_completion_func_t * rl_attempted_completion_function;

     该变量指向创建匹配补全的替代函数。函数参数 start和 end为字符串缓冲区rl_line_buffer的下标,以定义参数text的边界。若该函数存在且返回NULL,或者该变量值被设置为NULL,则rl_complete()将调用rl_completion_entry_function指向的函数来生成匹配结果;除此之外,程序使用该变量所指函数返回的字符串数组。若该变量所指函数将变量rl_attempted_completion_over设置为非零值,则Readline将不执行默认的文件名补全(即使自定义补全函数匹配失败)。

 

 

二  示例代码

     首先,自定义用户命令结构及其执行函数(为简化实现,执行函数仅打印函数名):

1 //命令结构体 2 typedef int (*CmdProcFunc)(void); 3 typedef struct{ 4     char         *pszCmd; 5     CmdProcFunc  fpCmd; 6 }CMD_PROC; 7  8 //命令处理函数定义 9 #define MOCK_FUNC(funcName) \10     int funcName(void){printf("  Enter "#funcName"!\n"); return 0;}11 12 MOCK_FUNC(ShowMeInfo);13 MOCK_FUNC(SetLogCtrl);14 MOCK_FUNC(TestBatch);15 MOCK_FUNC(TestEndianOper);

     基于上述定义,创建命令列表如下:

1 //命令表项宏,用于简化书写 2 #define CMD_ENTRY(cmdStr, func)     {cmdStr, func} 3 #define CMD_ENTRY_END               {NULL,   NULL} 4  5 //命令表 6 static CMD_PROC gCmdMap[] = { 7     CMD_ENTRY("ShowMeInfo",       ShowMeInfo), 8     CMD_ENTRY("SetLogCtrl",       SetLogCtrl), 9     CMD_ENTRY("TestBatch",        TestBatch),10     CMD_ENTRY("TestEndian",       TestEndianOper),11 12     CMD_ENTRY_END13 };14 #define CMD_MAP_NUM     (sizeof(gCmdMap)/sizeof(CMD_PROC)) - 1/*End*/

     然后,提供两个检索命令列表的函数:

1 //返回gCmdMap中的CmdStr列(必须为只读字符串),以供CmdGenerator使用 2 static char *GetCmdByIndex(unsigned int dwCmdIndex) 3 { 4     if(dwCmdIndex >=  CMD_MAP_NUM) 5         return NULL; 6     return gCmdMap[dwCmdIndex].pszCmd; 7 } 8  9 //执行命令10 static int ExecCmd(char *pszCmdLine)11 {12     if(NULL == pszCmdLine)13         return -1;14 15     unsigned int dwCmdIndex = 0;16     for(; dwCmdIndex < CMD_MAP_NUM; dwCmdIndex++)17     {18         if(!strcmp(pszCmdLine, gCmdMap[dwCmdIndex].pszCmd))19             break;20     }21     if(CMD_MAP_NUM == dwCmdIndex)22         return -1;23     gCmdMap[dwCmdIndex].fpCmd(); //调用相应的函数24 25     return 0;26 }
     以上代码独立于Readline库,接下来将编写与该库相关的代码。

     考虑到实际应用中,程序运行平台不一定包含Readline库,因此需要用__READLINE_DEBUG条件编译控制库的使用。

1 #ifdef __READLINE_DEBUG  2 #include 
3 #include
4 5 static const char * const pszCmdPrompt = "clover>>"; 6 7 //退出交互式调测器的命令(不区分大小写) 8 static const char *pszQuitCmd[] = {
"Quit", "Exit", "End", "Bye", "Q", "E", "B"}; 9 static const unsigned char ucQuitCmdNum = sizeof(pszQuitCmd) / sizeof(pszQuitCmd[0]); 10 static int IsUserQuitCmd(char *pszCmd) 11 { 12 unsigned char ucQuitCmdIdx = 0; 13 for(; ucQuitCmdIdx < ucQuitCmdNum; ucQuitCmdIdx++) 14 { 15 if(!strcasecmp(pszCmd, pszQuitCmd[ucQuitCmdIdx])) 16 return 1; 17 } 18 19 return 0; 20 } 21 22 //剔除字符串首尾的空白字符(含空格) 23 static char *StripWhite(char *pszOrig) 24 { 25 if(NULL == pszOrig) 26 return "NUL"; 27 28 char *pszStripHead = pszOrig; 29 while(isspace(*pszStripHead)) 30 pszStripHead++; 31 32 if('\0' == *pszStripHead) 33 return pszStripHead; 34 35 char *pszStripTail = pszStripHead + strlen(pszStripHead) - 1; 36 while(pszStripTail > pszStripHead && isspace(*pszStripTail)) 37 pszStripTail--; 38 *(++pszStripTail) = '\0'; 39 40 return pszStripHead; 41 } 42 43 static char *pszLineRead = NULL; //终端输入字符串 44 static char *pszStripLine = NULL; //剔除前端空格的输入字符串 45 char *ReadCmdLine() 46 { 47 //若已分配命令行缓冲区,则将其释放 48 if(pszLineRead) 49 { 50 free(pszLineRead); 51 pszLineRead = NULL; 52 } 53 //读取用户输入的命令行 54 pszLineRead = readline(pszCmdPrompt); 55 56 //剔除命令行首尾的空白字符。若剔除后的命令不为空,则存入历史列表 57 pszStripLine = StripWhite(pszLineRead); 58 if(pszStripLine && *pszStripLine) 59 add_history(pszStripLine); 60 61 return pszStripLine; 62 } 63 64 static char *CmdGenerator(const char *pszText, int dwState) 65 { 66 static int dwListIdx = 0, dwTextLen = 0; 67 if(!dwState) 68 { 69 dwListIdx = 0; 70 dwTextLen = strlen(pszText); 71 } 72 73 //当输入字符串与命令列表中某命令部分匹配时,返回该命令字符串 74 const char *pszName = NULL; 75 while((pszName = GetCmdByIndex(dwListIdx))) 76 { 77 dwListIdx++; 78 79 if(!strncmp (pszName, pszText, dwTextLen)) 80 return strdup(pszName); 81 } 82 83 return NULL; 84 } 85 86 static char **CmdCompletion (const char *pszText, int dwStart, int dwEnd) 87 { 88 //rl_attempted_completion_over = 1; 89 char **pMatches = NULL; 90 if(0 == dwStart) 91 pMatches = rl_completion_matches(pszText, CmdGenerator); 92 93 return pMatches; 94 } 95 96 //初始化Tab键能补齐的Command函数 97 static void InitReadLine(void) 98 { 99 rl_attempted_completion_function = CmdCompletion;100 }101 102 #endif

     自动补全后的命令字符串结尾多出一个空格,故需调用StripWhite将该空格剔除。

     最后,可编写交互式调测函数如下:

1 int main(void) 2 { 3 #ifndef __READLINE_DEBUG 4     printf("Note: Macro __READLINE_DEBUG is Undefined, thus InteractiveCmd is Unavailable!!!\n\n"); 5 #else 6     printf("Note: Welcome to Interactive Command!\n"); 7     printf("      Press 'Quit'/'Exit'/'End'/'Bye'/'Q'/'E'/'B' to quit!\n\n"); 8     InitReadLine(); 9     while(1)10     {
//也可加入超时机制以免忘记退出11 char *pszCmdLine = ReadCmdLine();12 if(IsUserQuitCmd(pszCmdLine))13 {14 free(pszLineRead);15 break;16 }17 18 ExecCmd(pszCmdLine);19 }20 #endif21 22 return 0;23 }

     该函数用法类似Shell,便于定制调测命令的随机执行。命令中首个参数(本文参数唯一)支持自动补全,但参数区分大小写。

     编译链接时需加载readline库和termcap(或ncurses)库。ncurses库通常使用terminfo(终端信息),少数实现会使用termcap(终端能力)。启用Readline库时,执行结果如下:

1 [wangxiaoyuan_@localhost test1]$ gcc -Wall -o ReadLine ReadLine.c -D__READLINE_DEBUG -lreadline -lncurses 2 [wangxiaoyuan_@localhost test1]$ ./ReadLine 3 Note: Welcome to Interactive Command! 4       Press 'Quit'/'Exit'/'End'/'Bye'/'Q'/'E'/'B' to quit! 5  6 clover>>ShowMeInfo(完整输入) 7   Enter ShowMeInfo! 8 clover>>ShowMeInfo(UP键调出历史命令) 9   Enter ShowMeInfo!10 clover>>SetLogCtrl (输入'Se'自动补全)11   Enter SetLogCtrl!12 clover>>   TestEndianOper(错误输入)13 clover>>TestEndian  (输入'T'自动补全为"Test",再输入'E'自动补全为"TestEndian ")14   Enter TestEndianOper!15 clover>>  TestBatch   (命令首尾加空格,无法自动补全)16   Enter TestBatch!17 clover>>ReadLine (输入'R'自动补全文件名)18 clover>>quit19 [wangxiaoyuan_@localhost test1]$

     不启用Readline库时,执行结果如下:

1 [wangxiaoyuan_@localhost test1]$ gcc -Wall -o ReadLine ReadLine.c                                        2 ReadLine.c:41: warning: 'GetCmdByIndex' defined but not used3 ReadLine.c:49: warning: 'ExecCmd' defined but not used4 [wangxiaoyuan_@localhost test1]$ ./ReadLine5 Note: Macro __READLINE_DEBUG is Undefined, thus InteractiveCmd is Unavailable!!!

 

转载地址:http://fzinl.baihongyu.com/

你可能感兴趣的文章
React Native 的默认单位和自适应布局方案
查看>>
ReactNative安卓首屏白屏优化
查看>>
设计模式(Design Pattern)系列之.NET专题
查看>>
为 Nginx 添加 HTTP 基本认证(HTTP Basic Authentication)
查看>>
gulp使用ES6
查看>>
ECUG Con 邀您共议服务端开发最深度实践
查看>>
关于优惠券功能设计之我的见解
查看>>
JavaScript中的函数式编程二(翻译)
查看>>
Javascript的异步编程:Promise
查看>>
荣誉,还是苦逼?| 也议全栈工程师和DevOps
查看>>
gulp详细基础教程
查看>>
CSS基础篇-- position属性讲解
查看>>
Python2.x的编码问题
查看>>
开源编辑器 Atom 简化代码审查过程
查看>>
每秒聚合5亿个指标,Uber 开源大规模指标平台 M3
查看>>
中国航天局向荷兰、德国等移交嫦娥四号载荷数据,并同时发布其他项目合作机会公告 ...
查看>>
Spring MVC原理
查看>>
图灵奖得主长文报告:是什么开启了计算机架构的新黄金十年?(上) ...
查看>>
pseudo tty破除无法自动输入密码的限制
查看>>
阿里云财务软件好会计-好会计财务管理系统介绍 ...
查看>>