将menu设计为可重用的子系统

 网易云课堂昵称:风清扬pty +《软件工程(C编码实践篇)》MOOC课程作业//mooc.study.163.com/course/USTC-1000002006


实验要求

  • 为menu子系统设计接口,并写用户范例代码来实现原来的功能;
  • 使用make和make clean来编译程序和清理自动生成的文件; 
  • 使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令; 
  • 可以使用getopt函数获取命令行参数。

实验主要步骤及代码

  • 创建Makefile文件,实现make指令编译功能。解决当项目中文件数目较多时,使用gcc 命令会很繁琐的问题。
CC_FLAGS        =    -c
CC_OUTPUT_FLAGS        =    -o
CC            =    gcc
TARGET            =    test
OBJS            =    linktable.o menu.o test.o
RM            =    rm
RM_FLAGS        =    -f
all:    $(OBJS)
    $(CC)    $(CC_OUTPUT_FLAGS)    $(TARGET)    $(OBJS)
.c.o:
    $(CC)    $(CC_FLAGS)    $<
clean:
    $(RM)    $(RM_FLAGS)    $(OBJS)    $(TARGET)    *.bak
  • menu.c可以使menu程序的升级和维护比较容易,具有重用性。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linktable.h"
#include "menu.h"
#define CMD_MAX_LEN 128
#define DESC_LEN    1024
#define CMD_NUM     10
#define CMD_MAX_ARGV_NUM 32
tLinkTable * head = NULL;
int Help(int argc, char **argv);

typedef struct DataNode
{
    tLinkTableNode * pNext;
    char*   cmd;
    char*   desc;
    int     (*handler)(int argc, char **argv);
} tDataNode;
int SearchCondition(tLinkTableNode * pLinkTableNode, void* args)
{
    char* cmd = (char*)args;
    tDataNode * pNode = (tDataNode *)pLinkTableNode;
    if(strcmp(pNode->cmd, cmd) == 0)
   {
        return  SUCCESS;
    }
    return FAILURE;
}

tDataNode* FindCmd(tLinkTable * head, char* cmd)
{
    return  (tDataNode*)SearchLinkTableNode(head, SearchCondition, cmd);
}
/* show all cmd in listlist */
int ShowAllCmd(tLinkTable * head)
{
    tDataNode * pNode = (tDataNode*)GetLinkTableHead(head);
    while(pNode != NULL)
    {
        printf("%s\t - \t%s\n", pNode->cmd, pNode->desc);
        pNode = (tDataNode*)GetNextLinkTableNode(head,(tLinkTableNode *)pNode);
    }
    return 0;
}
int MenuConfig(char *cmd, char *desc, int (*handler)(int argc, char **argv))
{
    tDataNode* pNode = NULL;
    if (head == NULL)
    {
        head = CreateLinkTable();
        pNode = (tDataNode*)malloc(sizeof(tDataNode));
        pNode->cmd = "help";
        pNode->desc = "Menu List:";
        pNode->handler = Help;
        AddLinkTableNode(head, (tLinkTableNode*)pNode);
    }    pNode = (tDataNode*)malloc(sizeof(tDataNode));
    pNode->cmd = cmd;
    pNode->desc = desc;
    pNode->handler = handler;
    AddLinkTableNode(head, (tLinkTableNode*)pNode);
    return 0;

}
/* menu program */
int ExcuteMenu()
{
    while(1)
    {
        // init argc
        int argc = 0;
        char *argv[CMD_MAX_ARGV_NUM];
        char cmd[CMD_MAX_LEN];        
        char *pcmd = NULL;

        // input cmd
        printf("Input a cmd > ");
        pcmd = fgets(cmd, CMD_MAX_LEN, stdin);
           if (pcmd == NULL)
            {
              continue;
            }

        // convert cmd line to argc/argv;
        pcmd = strtok(pcmd, " ");
        while (pcmd != NULL && argc < CMD_MAX_ARGV_NUM)
        {
            argv[argc] = pcmd;
            ++argc;
            pcmd = strtok(NULL, " ");
        }
        if (argc == 1)
        {
            int len = strlen(argv[0]);
            *(argv[0] + len - 1) = '\0';
        }
        tDataNode *p = FindCmd(head, argv[0]);
        if( p == NULL)
        {
            printf("This is a wrong cmd!\n ");
            continue;
        }
        printf("%s\n", p->desc);
        if(p->handler != NULL)       {
           p->handler(argc, argv);
        }
    }
}
int Help(int argc, char **argv)
{
    printf("-------------------------------------------\n");
    ShowAllCmd(head);
    printf("-------------------------------------------\n");
    return 0;
}
  • menu.h
int MenuConfig(char *cmd, char *desc, int (*handler)(int argc, char* argv[]));
int ExecuteMenu();
  • test.c文件使用getopt函数获取命令行参数,使menu子系统支持带参数的复杂命令,可以丰富命令的内容。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "menu.h"
int Quit(int argc, char **argv)
{
    exit(0);
}
int argtest(int argc, char **argv)
{
    const char *optString = "lah";
    opterr = 0;
    int opt;
    while ((opt = getopt(argc, argv, optString)) != -1)
    {
        switch (opt)
        {
            case 'l':
                printf("this -l option\n");
                break;            
            case 'a':
                printf("this -a option\n");
                break;
            case 'h':
                printf("in this cmd, you have 3 option can use:\n");
                printf("-l\n");
                printf("-a\n");
                printf("-h\n");
                break;
            default:
                break;
        }
    }
    // reset global valuable optind
    optind = 0;
    return 0;
}
int main(int argc, char **argv)
{
    MenuConfig("version", "xxx v1.0(Menu program v1.0 inside)", NULL);
    MenuConfig("argtest", "test arg option", argtest);
    MenuConfig("quit", "quit from xxx", Quit);
    ExcuteMenu();
    return 0;
}

实验结果






实验总结

  • 学会了使用Makefile文件来简化多个文件的编译命令,但是一定注意使用Makefile时$开头的字符串之间要使用tab符号分开,不能使用空格,因为这个的疏忽前面好几次编译一直不通过。
  • 学习了menu程序的可重用性。
  • 编写代码时一定要认真,不要再因为粗心导致的错误检查修改好多次。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值