Chinaunix首页 | 论坛 | 博客
  • 博客访问: 304963
  • 博文数量: 68
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 706
  • 用 户 组: 普通用户
  • 注册时间: 2015-08-13 14:58
文章分类

全部博文(68)

文章存档

2021年(4)

2020年(6)

2018年(5)

2017年(13)

2016年(8)

2015年(32)

我的朋友

分类: C/C++

2017-02-15 14:59:49

一  函数介绍

本节简要介绍后文将要用到的几个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_function设置为非零值,则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_END 13 }; 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 #endif 21 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>>quit 19 [wangxiaoyuan_@localhost test1]$

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

1 [wangxiaoyuan_@localhost test1]$ gcc -Wall -o ReadLine ReadLine.c 2 ReadLine.c:41: warning: 'GetCmdByIndex' defined but not used 3 ReadLine.c:49: warning: 'ExecCmd' defined but not used 4 [wangxiaoyuan_@localhost test1]$ ./ReadLine 5 Note: Macro __READLINE_DEBUG is Undefined, thus InteractiveCmd is Unavailable!!!
阅读(1668) | 评论(0) | 转发(0) |
0

上一篇:portaudio持续录音

下一篇:dbus

给主人留下些什么吧!~~