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

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

实验要求

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

实验步骤

#ifndef _menu
#define _menu
int MenuConfig(char* cmd, char* desc, int (*handler)());
int ExecuteMenu();
#endif
copy
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linktable.h"
#define CMD_MAX_LEN 128
#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 'l':
                ch_prom = optarg;
                printf("This is \"-l\" mode help with %s\n", ch_prom);
                ShowAllCmd(head);
                break;
            case '?':
                printf("Wrong argument!\n");
        }
    }
    optind = 1;
    return 0; 
}
copy

从实验5中拷贝 linktable.h 和 linktable.c就不贴出展示了


编写 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

编写 Makefile,使得输入 make即可编译文件,输入 make clean 清除编译后的文件

GCC_PTHREAD        = -lpthread
GCC_FLAGS        = -c
GCC_OUTPUT_FLAGS    = -o
GCC            = gcc
RM            = rm
RM_FLAGS        = -f
TARGET            = test
OBJS            = linktable.o menu.o test.o
all:    $(OBJS)
    $(GCC) $(GCC_OUTPUT_FLAGS) $(TARGET) $(OBJS)
.c.o:
    $(GCC) $(GCC_FLAGS) $<
clean:
    $(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak
copy

实验结果

make/make clean 操作
图片描述
执行结果

图片描述

实验总结

  1. 本次实验学习到,将menu.c从主函数中独立出来后,会极大的降低menu.c和其它文件的耦合程度,从而提高menu 程序的可重用性和拓展性
  2. 编写 Makefile 来简化编译的不中,从而提升效率,另外在 Makefile 中规定了使用一个 tab符隔开
  3. gets()方法有缓冲区溢出攻击的隐患,我们在实践中要使用fgets()方法加以替换
最新评论
暂无评论~