zoukankan      html  css  js  c++  java
  • GNU Readline库函数的应用示例

    摘自:https://www.cnblogs.com/clover-toeic/p/3892688.html

    说明

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

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

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

     

     

     

    一  函数介绍

         本节简要介绍后文将要用到的几个Readline库函数,如用来替代fgets()的readline()函数。关于库函数的更多描述可参考http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html

         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 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 
      5 #define __READLINE_DEBUG
      6 
      7 
      8 
      9 // 首先,自定义用户命令结构及其执行函数(为简化实现,执行函数仅打印函数名):
     10 //命令结构体
     11 typedef int (*CmdProcFunc)(void);
     12 typedef struct{
     13     char         *pszCmd;
     14     CmdProcFunc  fpCmd;
     15 }CMD_PROC;
     16 
     17 //命令处理函数定义
     18 #define MOCK_FUNC(funcName) 
     19     int funcName(void){printf("  Enter "#funcName"!
    "); return 0;}
     20 
     21 MOCK_FUNC(ShowMeInfo);
     22 MOCK_FUNC(SetLogCtrl);
     23 MOCK_FUNC(TestBatch);
     24 MOCK_FUNC(TestEndianOper);
     25 
     26 
     27 
     28 
     29 // 基于上述定义,创建命令列表如下:
     30 
     31 //命令表项宏,用于简化书写
     32 #define CMD_ENTRY(cmdStr, func)     {cmdStr, func}
     33 #define CMD_ENTRY_END               {NULL,   NULL}
     34 
     35 //命令表
     36 static CMD_PROC gCmdMap[] = {
     37     CMD_ENTRY("ShowMeInfo",       ShowMeInfo),
     38     CMD_ENTRY("SetLogCtrl",       SetLogCtrl),
     39     CMD_ENTRY("TestBatch",        TestBatch),
     40     CMD_ENTRY("TestEndian",       TestEndianOper),
     41 
     42     CMD_ENTRY_END
     43 };
     44 #define CMD_MAP_NUM     (sizeof(gCmdMap)/sizeof(CMD_PROC)) - 1/*End*/
     45 
     46 
     47 
     48 
     49 
     50 
     51 // 然后,提供两个检索命令列表的函数:
     52 
     53 //返回gCmdMap中的CmdStr列(必须为只读字符串),以供CmdGenerator使用
     54 static char *GetCmdByIndex(unsigned int dwCmdIndex)
     55 {
     56     if(dwCmdIndex >=  CMD_MAP_NUM)
     57         return NULL;
     58     return gCmdMap[dwCmdIndex].pszCmd;
     59 }
     60 
     61 //执行命令
     62 static int ExecCmd(char *pszCmdLine)
     63 {
     64     if(NULL == pszCmdLine)
     65         return -1;
     66 
     67     unsigned int dwCmdIndex = 0;
     68     for(; dwCmdIndex < CMD_MAP_NUM; dwCmdIndex++)
     69     {
     70         if(!strcmp(pszCmdLine, gCmdMap[dwCmdIndex].pszCmd))
     71             break;
     72     }
     73     if(CMD_MAP_NUM == dwCmdIndex)
     74         return -1;
     75     gCmdMap[dwCmdIndex].fpCmd(); //调用相应的函数
     76 
     77     return 0;
     78 }
     79 
     80 
     81 
     82 
     83 // 以上代码独立于Readline库,接下来将编写与该库相关的代码。
     84 // 考虑到实际应用中,程序运行平台不一定包含Readline库,因此需要用__READLINE_DEBUG条件编译控制库的使用。
     85 
     86 
     87 
     88 #ifdef __READLINE_DEBUG
     89 #include <readline/readline.h>
     90 #include <readline/history.h>
     91 
     92 static const char * const pszCmdPrompt = "clover>>";
     93 
     94 //退出交互式调测器的命令(不区分大小写)
     95 static const char *pszQuitCmd[] = {"Quit", "Exit", "End", "Bye", "Q", "E", "B"};
     96 static const unsigned char ucQuitCmdNum = sizeof(pszQuitCmd) / sizeof(pszQuitCmd[0]);
     97 static int IsUserQuitCmd(char *pszCmd)
     98 {
     99     unsigned char ucQuitCmdIdx = 0;
    100     for(; ucQuitCmdIdx < ucQuitCmdNum; ucQuitCmdIdx++)
    101     {
    102         if(!strcasecmp(pszCmd, pszQuitCmd[ucQuitCmdIdx]))
    103             return 1;
    104     }
    105 
    106     return 0;
    107 }
    108 
    109 //剔除字符串首尾的空白字符(含空格)
    110 static char *StripWhite(char *pszOrig)
    111 {
    112     if(NULL == pszOrig)
    113         return "NUL";
    114 
    115     char *pszStripHead = pszOrig;
    116     while(isspace(*pszStripHead))
    117         pszStripHead++;
    118 
    119     if('' == *pszStripHead)
    120         return pszStripHead;
    121 
    122     char *pszStripTail = pszStripHead + strlen(pszStripHead) - 1;
    123     while(pszStripTail > pszStripHead && isspace(*pszStripTail))
    124         pszStripTail--;
    125     *(++pszStripTail) = '';
    126 
    127     return pszStripHead;
    128 }
    129 
    130 static char *pszLineRead = NULL;  //终端输入字符串
    131 static char *pszStripLine = NULL; //剔除前端空格的输入字符串
    132 char *ReadCmdLine()
    133 {
    134      //若已分配命令行缓冲区,则将其释放
    135     if(pszLineRead)
    136     {
    137         free(pszLineRead);
    138         pszLineRead = NULL;
    139     }
    140     //读取用户输入的命令行
    141     pszLineRead = readline(pszCmdPrompt);
    142 
    143     //剔除命令行首尾的空白字符。若剔除后的命令不为空,则存入历史列表
    144     pszStripLine = StripWhite(pszLineRead);
    145     if(pszStripLine && *pszStripLine)
    146         add_history(pszStripLine);
    147 
    148     return pszStripLine;
    149 }
    150 
    151 static char *CmdGenerator(const char *pszText, int dwState)
    152 {
    153     static int dwListIdx = 0, dwTextLen = 0;
    154     if(!dwState)
    155     {
    156         dwListIdx = 0;
    157         dwTextLen = strlen(pszText);
    158     }
    159 
    160     //当输入字符串与命令列表中某命令部分匹配时,返回该命令字符串
    161     const char *pszName = NULL;
    162     while((pszName = GetCmdByIndex(dwListIdx)))
    163     {
    164         dwListIdx++;
    165 
    166         if(!strncmp (pszName, pszText, dwTextLen))
    167             return strdup(pszName);
    168     }
    169 
    170     return NULL;
    171 }
    172 
    173 static char **CmdCompletion (const char *pszText, int dwStart, int dwEnd)
    174 {
    175     //rl_attempted_completion_over = 1;
    176     char **pMatches = NULL;
    177     if(0 == dwStart) {
    178         // pMatches = rl_completion_matches(pszText, rl_filename_completion_function);
    179         // pMatches = rl_completion_matches(pszText, rl_username_completion_function);
    180         pMatches = rl_completion_matches(pszText, CmdGenerator);
    181     }
    182 
    183     return pMatches;
    184 }
    185 
    186 //初始化Tab键能补齐的Command函数
    187 static void InitReadLine(void)
    188 {
    189     rl_attempted_completion_function = CmdCompletion;
    190 }
    191 
    192 #endif
    193 
    194 // 自动补全后的命令字符串结尾多出一个空格,故需调用StripWhite将该空格剔除。
    195 // 最后,可编写交互式调测函数如下:
    196 int main(void)
    197 {
    198 #ifndef __READLINE_DEBUG
    199     printf("Note: Macro __READLINE_DEBUG is Undefined, thus InteractiveCmd is Unavailable!!!
    
    ");
    200 #else
    201     printf("Note: Welcome to Interactive Command!
    ");
    202     printf("      Press 'Quit'/'Exit'/'End'/'Bye'/'Q'/'E'/'B' to quit!
    
    ");
    203     InitReadLine();
    204     while(1)
    205     {//也可加入超时机制以免忘记退出
    206         char *pszCmdLine = ReadCmdLine();
    207         if(IsUserQuitCmd(pszCmdLine))
    208         {
    209             free(pszLineRead);
    210             break;
    211         }
    212 
    213         ExecCmd(pszCmdLine);
    214     }
    215 #endif
    216 
    217     return 0;
    218 }

    该函数用法类似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!!!
  • 相关阅读:
    MySQL8安装及使用当中的一些注意事项
    设计模式-观察者模式
    在线教育项目-day12【完善后端整合前端】
    在线教育项目-day12【微信扫码登录】
    在线教育项目-day12【OAuth2】
    在线教育项目-day12【完善登陆页面】
    在线教育项目-day12【整合前端登陆注册页面(2)】
    在线教育项目-day12【整合前端登陆注册页面(1)】
    在线教育项目-day12【解析oken信息】
    在线教育项目-day12【注册接口】
  • 原文地址:https://www.cnblogs.com/LiuYanYGZ/p/14806463.html
Copyright © 2011-2022 走看看