[高级软件工程实验]将menu设计为可重用的子系统

版本库URL:https://github.com/swagnhen/Advanced-Software-Engineering-Exercise.git

实验要求

1)为menu子系统设计接口,并写用户范例代码来实现原来的功能;

2)使用make和make clean来编译程序和清理自动生成的文件;

3)使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;

4)可以使用getopt函数获取命令行参数。

实验内容

callback增强可重用链表实现包含于linkedlist.h与linkedlist.c中
shell中各命令功能的实现包含在cmdopt.h与cmdopt.c中
main函数包含于shelllet.c中
编译时请使用

make

1.子系统设计接口

//用于用户向命令行添加新的命令功能
int ShellletConfig(LinkedList *head, char * cmd, int (*handler)());

//用于启动命令行程序
int ExecuteShelllet(LinkedList *head);

具体实现

int ShellletConfig(LinkedList *l, char *cmd, int (*handler)())
{
    CmdNode *p = NULL;
    if (l == NULL)
    {
        return -1;
    }
    p = (CmdNode *)malloc(sizeof(CmdNode));
    p->cmd = cmd;
    p->handler = handler;
    addNode(l, (Node *)p);
    return 0;
}

int ExecuteShelllet(LinkedList *l)
{
    int argc = 0;
    char *argv[32];
    printf("**********Shelllet Running**********\n");
    while (1)
    {
        printf(">>>");
        //cmd2arg函数用于将输入的命令转化为argc与argv形式
        cmd2arg(&argc, argv);
        CmdNode *p = findCmd(l, argv[0]);
        if (p != NULL && p->handler != NULL)
            p->handler(argc, argv);
        argc = 0;
    }
    return 0;
}

2.Makefile编译文件

#
# Makefile for Shelllet Program
#

CC_PTHREAD_FLAGS             = -lpthread
CC_FLAGS                     = -c 
CC_OUTPUT_FLAGS              = -o
CC                           = gcc
RM                           = rm
RM_FLAGS                     = -f

TARGET  =   test
OBJS    =   linkedlist.o  cmdopt.o shelllet.o

all:    $(OBJS)
    $(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS) 
.c.o:
    $(CC) $(CC_FLAGS) $<

clean:
    $(RM) $(RM_FLAGS)  $(OBJS) $(TARGET) *.bak

3.复杂指令解析

void cmd2arg(int *argc, char **argv)
{
    char cmd[1024];
    char *pcmd = NULL;
    pcmd = fgets(cmd, 1024, stdin);
    if (pcmd == NULL)
        return;
    pcmd = strtok(pcmd, " ");
    while (pcmd != NULL && *argc < 32)
    {
        argv[*argc] = pcmd;
        (*argc)++;
        pcmd = strtok(NULL, " ");
    }
    if (*argc == 1)
    {
        int len = strlen(argv[0]);
        *(argv[0] + len - 1) = '\0';
    }
}

用户实现的复杂指令函数

int test(int argc, char *argv[]){
    printf(">>>Test success\n");
    return 0;
}

4.获取命令行参

经cmd2arg处理后argv[1]开始均为命令行参数

void cmd2arg(int *argc, char **argv)
{
    char cmd[1024];
    char *pcmd = NULL;
    pcmd = fgets(cmd, 1024, stdin);
    if (pcmd == NULL)
        return;
    pcmd = strtok(pcmd, " ");
    while (pcmd != NULL && *argc < 32)
    {
        argv[*argc] = pcmd;
        (*argc)++;
        pcmd = strtok(NULL, " ");
    }
    if (*argc == 1)
    {
        int len = strlen(argv[0]);
        *(argv[0] + len - 1) = '\0';
    }
}

运行效果
这里写图片描述

实验总结

我对老师的写法做了一些修改,还是去掉了全局链表,这样的话每次用户(程序员)在使用这个程序的时候都得自己先建立一个链表,初始化之后再将链表传入ExecuteShelllet函数中执行命令行程序。
这样应该算是面向对象的实现思路吧,毕竟C中没有类,不能定义一个Menu类再向其添加start方法。
其实这个程序有bug,不论输什么命令第一次都会失效。
我调试之后发现是在第一次解析命令时字符串比对函数strcmp出现错误,没有返回正确结果的同时还改变了形参cmd的值。
就很神秘,这个函数的原型不是加了const么,怎么还能改变形参值的,还只出现一次。
感觉可能是头文件include的问题,之前在没加string.h的时候函数strtok也出现了一样的问题。
试了很多没有解决,就搁置了。
还有就是像加减乘除这样有指令执行中含有scanf调用的命令2,运行结束后程序会执行一个空指令(即“”)。
这个肯定是fget和scanf混用的问题,估计是add运行结束之后有个换行符\n还在输入流里,下一轮开始的时候被fget读到了。
虽然在每个scanf后面单独加个fget应该能解决,但是感觉这种方式非常蠢,也没想到更好的方式,就算了。
这程序再拓展几次估计就要不能维护了。

【Swegnhan + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值