“软件工程(C编码实践篇)”实验报告

实验七:将menu设计为可重用的子系统

实验要求


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

实验过程


  1. 在课程目录下创建lab7文件夹。

  2. 给整个命令行菜单menu程序定义一套接口,提供两个方法。在menu.h文件中声明MenuConfig函数和ExcuteMenu函数。其中,MenuConfig函数用于向链表中添加命令结点,若原链表头结点为空,则先添加默认的help命令结点。ExcuteMenu函数改自原menu.c的main函数,用于获取并执行用户输入的命令。 menu.h

    #ifndef _menu
    #define _menu
    int MenuConfig(char* cmd, char* desc, int (*handler)());
    int ExecuteMenu();
    #endif
    
    copy
  3. menu.c文件 实现menu.h文件中定义的MenuConfig函数和ExcuteMenu函数。使用getopt函数,使menu子系统支持带参数的命令。 修改ExcuteMenu函数,将输入的命令行解析为int argc ,char *agrv[]的形式。没有使用fgets获得一行数据而是定义了一个ReceiveCMD。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include "linktable.h"
    int Help(int argc, char* argv[]);
    extern char* optarg;
    extern int optind;
    #define CMD_MAX_LEN 128
    #define CMD_MAX_ARGV 128
    #define DESC_LEN    1024
    #define CMD_NUM     10
    /* data struct and its operations */
    tLinkTable* head = NULL;
    typedef struct DataNode
    {
     tLinkTableNode * pNext;
     char*   cmd;
     char*   desc;
     int     (*handler)(int argc, char* argv[]);
    } tDataNode;
    int SearchCondition(tLinkTableNode * pLinkTableNode, void * args)
    {
     tDataNode * pNode = (tDataNode *)pLinkTableNode;
     char * tempchar = args;
     if(strcmp(pNode->cmd, args) == 0)
     {
         return  SUCCESS;  
     }
     return FAILURE;           
    }
    /* find a cmd in the linklist and return the datanode pointer */
    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);
     printf("---------------------------------------------\n");
     while(pNode != NULL)
     {
         printf("%s - %s\n", pNode->cmd, pNode->desc);
         pNode = (tDataNode*)GetNextLinkTableNode(head,(tLinkTableNode *)pNode);
     }
     printf("---------------------------------------------\n");
     return 0;
    }
    int ReceiveCMD(char* cmd, int* argc, char* argv[])
    {
     char* pcmd = NULL;
     pcmd = fgets(cmd, CMD_MAX_LEN, stdin);
     int len = strlen(cmd);
     *(cmd + len - 1) = '\0';
     pcmd = strtok(pcmd, " ");
     while(pcmd == NULL)
     {
         return -1;
     }
     while(pcmd != NULL && (*argc) < CMD_MAX_ARGV)
     {
         argv[*argc] = pcmd;
         (*argc)++;
         pcmd = strtok(NULL, " ");
     }
     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 = "list all cmds";
         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 ExecuteMenu()
    {
    /* cmd line begins */
     while(1)
     {
     int argc = 0;
     char* argv[CMD_MAX_ARGV];
     char cmd[CMD_MAX_LEN];
         printf("Input a cmd number > ");
         if(ReceiveCMD(cmd, &argc, argv) == -1)
     {
         continue;
     }
     //scanf("%s", cmd);
         tDataNode *p = FindCmd(head, argv[0]);
         if( p == NULL)
         {
             printf("This is a wrong cmd!\n ");
             continue;
         }
         printf("%s - %s\n", p->cmd, p->desc); 
         if(p->handler != NULL) 
         { 
             p->handler(argc, argv);
         }
    
     }
    }
    int Help(int argc, char* argv[])
    {
     int ch;
     char* ch_prom;
     if(argc == 1)
     {
         ShowAllCmd(head);
         return 0;
     }
     while((ch = getopt(argc, argv, "shl:")) != -1)
     {
         switch(ch)
         {
             case 's':
                 printf("This is \"-s\" mode help\n");
                 ShowAllCmd(head);
                 break;
             case 'h':
                 printf("This is \"-h\" mode help\n");
                 ShowAllCmd(head);
                 break;
             case '?':
                 printf("Wrong argument!\n");
         }
     }
     optind = 1;
     return 0; 
    }
    
    copy
  4. test.c文件

    #include "menu.h"
    #include <stdio.h>
    #include <stdlib.h>
    int version(int argc, char* argv);
    int quit(int argc, char* argv);
    int main()
    {
     char* cmd = "version";
     char* desc = "version information";
     MenuConfig(cmd, desc, version);
     cmd = "quit";
     desc = "type \"quit\" to exit";
     MenuConfig(cmd, desc, quit);
     ExecuteMenu();
     return 0;
    }
    int version(int argc, char* argv)
    {
     printf("v1.0\n");
     return 0;
    }
    int quit(int argc, char* argv)
    {
     exit(0);
     return 0;
    }
    
    copy
  5. 编写Makefile文件,使用户可以使用make来编译程序。

    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
    
    copy

    运行试验


实验楼

实验心得


  • 本次实验过程较为曲折...但学会了将menu.c从主函数中提取出来,降低menu.c和其它文件的耦合程度。
  • 当要编译的文件较多的时候,可以使makefile,执行make就可以对文件进行编译,Makefile中的Tab键 不能用空格代替。
最新评论
暂无评论~