【zhanghughsw + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006 】
本文为网易云课堂课程《软件工程(C编码实践)》的实验报告,本次实验为在前几次实验(命令行菜单小程序)的基础上,将命令行小程序实现为可重用的子系统
附上一实验的链接:用callback增强链表模块来实现命令行菜单小程序V2.8
一.实验思路
本次实验的目的是将命令行小程序实现为可重用的子系统,增加两个menu 的接口,使得在任何程序中调用这两个接口都可以实现命令行小程序的调用,同时尝试实现带参数的复杂命令指令的编写以及使用Makefile工程文件替代输入,实现程序编译和编译文件清理。
附小程序功能列表:
- time:获取当前的日期与时间
- calculation:进行加减乘除四则运算
- notepad :在当前文件夹创建并使用vim打开一个文本文档*.txt
- version:打印程序的版本号
- mac : 打开文件管理器nautilus
- explore :打开浏览器
- help :列出所有指令及解释
- help :列出所有指令及解释
二、代码实现
本次实现新增加了一个menu.h文件来实现menu的接口,其他文件不变。分别为menu.c linktable.c linktable.h menu.h
menu.h
/* add cmd to menu*/
int MenuConfig(char * cmd, char * desc, int (*handler)());
/* Menu Engine Execute*/
int ExecuteMenu();
menu.c
在本文件中增加接口函数的实现
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;
}
int ExecuteMenu()
{
while(1)
{
int argc = 0;
char *argv[CMD_MAX_ARGV_NUM];
char cmd[CMD_MAX_LEN];
char *pcmd = NULL;
printf("Input a amd number > ");
pcmd = fgets(cmd,CMD_MAX_LEN, stdin);
if(pcmd == NULL)
{
continue;
}
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 = (tDataNode*)SearchLinkTableNode(head,SearchCondition,(void*)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);
}
}
}
linktable.h
本文件用于编写链表以及链表节点的数据结构以及声明在其上的一系列操作
#define _LINK_TABLE_H_
#include <pthread.h>
#define SUCESS 0
#define FAILURE (-1)
typedef struct LinkTableNode
{
struct LinkTableNOde * pNext;
}tLinkTableNode;
typedef struct LinkTable
{
tLinkTableNode *pHead;
tLinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
}tLinkTable;
tLinkTable * CreateLinkTable();
int DeleteLinkTable(tLinkTable *pLinkTable);
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
int DelLinkTable(tLinkTable *pLinkTable,tLinkTableNode * pNode);
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
linktable.c
本文件具体定义了链表的各种操作以及指令所对应的操作
CreateTable()——创建链表
tLinkTable * CreateLinkTable()
{
tLinkTable *p = (tLinkTable*)malloc(sizeof(tLinkTable));
tLinkTableNode * pNode = (tLinkTableNode *)malloc(sizeof(tLinkTableNode));
pNode->pNext = NULL;
p->pHead = pNode;
p->pTail = pNode;
p->SumOfNode = 0;
return p;
}
AddLinkTableNode()——以头插的方式将节点插入链表
int AddLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode * pNode)
{
if (pLinkTable->SumOfNode == 0)
{
pNode->pNext = NULL;
pLinkTable->pTail = pNode;
pLinkTable->pHead = pNode;
pLinkTable->SumOfNode += 1;
return 1;
}
pNode->pNext = pLinkTable->pHead;
pLinkTable->pHead = pNode;
pLinkTable->SumOfNode ++;
return 0;
}
GetLinkTableHead()——返回头结点
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode * pNode)
{
return pNode->pNext;
}
GetNextLinkTableNode()——返回下一节点
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode * pNode)
{
return pNode->pNext;
}
以及各项指令的功能函数
int Time()
{
time_t now;
struct tm *tm_now;
time(&now);
tm_now = localtime(&now);
printf("now datetime:%d-%d-%d %d:%d:%d\n", tm_now->tm_year + 1900, tm_now->tm_mon + 1, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);
}
int Calculation()
{
char ch;
int num1, num2;
printf("which calculations: + - * /:");
scanf("%c", &ch);
while (1)
{
scanf("%c", &ch);
setbuf(stdin, NULL);
if (ch == 'Q')
{
return 1;
}
else if (ch != '+' && ch != '-' && ch != '*' && ch != '/')
{
printf("Input Wrong!please input the one of '+','-','*','/'\n");
}
else
{
printf("You want to have a %c calculate,input the two number,separate by comma!\n", ch);
break;
}
}
scanf("%d,%d", &num1, &num2);
if (ch == '+')
{
printf("%d+%d=%d\n", num1, num2, num1 + num2);
}
else if (ch == '-')
{
printf("%d-%d=%d\n", num1, num2, num1 - num2);
}
else if (ch == '*')
{
printf("%d*%d=%d\n", num1, num2, num1 * num2);
}
else if (ch == '/')
{
printf("%d/%d=%f\n", num1, num2, (float)num1 / num2);
}
else
{
printf("Input Wrong!");
return -1;
}
return 0;
}
int Quit()
{
exit(0);
}
int Notepad()
{
system("touch ./*.txt");
system("vim ./*.txt");
return 0;
}
int Mgc()
{
system("nautilus");
return 0;
}
int Explore()
{
system("firefox");
return 0;
}
使用Makefile工程文件来替代输入
Makefile
1 CC_PTHREAD_FLAGS = -lpthread
2 CC_FLAGS = -c
3 CC_OUTPUT_FLAGS = -o
4 CC = gcc
5 RM = rm
6 RM_FLAGS = -f
7
8 TARGET = menu
9 OBJS = linktable.o menu.o
10
11 all: $(OBJS)
12 $(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS)
13
14 .c.o:
15 $(CC) $(CC_FLAGS) $<
16
17 clean:
18 $(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak
执行结果
三、实验总结
本次实验完成了命令行小程序的外部接口,实现了小程序的外部可重用性,最为重要的是,学会使用makefile工程文件来代替编译输入以及编译文件清理,收获颇丰。
复审代码
通过如下命令可以从Git版本库中拉取代码并编译运行
git clone https://github.com/zhanghughsw/zswlab.git
cd zswlab
cd lab6
//使用以下gcc命令或者make命令实现编译执行
gcc menu.c linktable.c -o menu
make
./menu