实验七:将menu设计为可重用的子系统
咸蛋超级咸 + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006
实验要求:
- 为menu子系统设计接口,并写用户范例代码来实现原来的功能
- 使用make和make clean来编译程序和清理自动生成的文件;
- 使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;
- 可以使用getopt函数获取命令行参数。
实验过程:
使用github建立远程仓库,并下载到本地,github的地址:github
编码
- linktable.h
#ifndef _LINK_TABLE_H_
#define _LINK_TABLE_H_
#include <pthread.h>
#define SUCCESS 0
#define FAILURE (-1)
/*
* LinkTable Node Type
*/
typedef struct LinkTableNode tLinkTableNode;
/*
* LinkTable Type
*/
typedef struct LinkTable tLinkTable;
/*
* Create a LinkTable
*/
tLinkTable * CreateLinkTable();
/*
* Delete a LinkTable
*/
int DeleteLinkTable(tLinkTable *pLinkTable);
/*
* Add a LinkTableNode to LinkTable
*/
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
* Delete a LinkTableNode from LinkTable
*/
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
* Search a LinkTableNode from LinkTable
* int Conditon(tLinkTableNode * pNode);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable,
int Conditon(tLinkTableNode * pNode, void * args),void * args);
/*
* get LinkTableHead
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
/*
* get next LinkTableNode
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
#endif /* _LINK_TABLE_H_ */
- linktable.c
这里写代码片#include<stdio.h>
#include<stdlib.h>
#include"linktable.h"
typedef struct LinkTableNode
{
struct LinkTableNode * pNext;
}tLinkTableNode;
typedef struct LinkTable{
tLinkTableNode *pHead;
tLinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
}tLinkTable;
/*
* Create a LinkTable
*/
tLinkTable * CreateLinkTable()
{
tLinkTable * pLinkTable = (tLinkTable *)malloc(sizeof(tLinkTable));
if(pLinkTable == NULL)
{
return NULL;
}
pLinkTable->pHead = NULL;
pLinkTable->pTail = NULL;
pLinkTable->SumOfNode = 0;
pthread_mutex_init(&(pLinkTable->mutex), NULL);
return pLinkTable;
}
/*
* Delete a LinkTable
*/
int DeleteLinkTable(tLinkTable *pLinkTable)
{
if(pLinkTable == NULL)
{
return FAILURE;
}
while(pLinkTable->pHead != NULL)
{
tLinkTableNode * p = pLinkTable->pHead;
pthread_mutex_lock(&(pLinkTable->mutex));
pLinkTable->pHead = pLinkTable->pHead->pNext;
pLinkTable->SumOfNode -= 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
free(p);
}
pLinkTable->pHead = NULL;
pLinkTable->pTail = NULL;
pLinkTable->SumOfNode = 0;
pthread_mutex_destroy(&(pLinkTable->mutex));
free(pLinkTable);
return SUCCESS;
}
/*
* Add a LinkTableNode to LinkTable
*/
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return FAILURE;
}
pNode->pNext = NULL;
pthread_mutex_lock(&(pLinkTable->mutex));
if(pLinkTable->pHead == NULL)
{
pLinkTable->pHead = pNode;
}
if(pLinkTable->pTail == NULL)
{
pLinkTable->pTail = pNode;
}
else
{
pLinkTable->pTail->pNext = pNode;
pLinkTable->pTail = pNode;
}
pLinkTable->SumOfNode += 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
/*
* Delete a LinkTableNode from LinkTable
*/
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return FAILURE;
}
pthread_mutex_lock(&(pLinkTable->mutex));
if(pLinkTable->pHead == pNode)
{
pLinkTable->pHead = pLinkTable->pHead->pNext;
pLinkTable->SumOfNode -= 1 ;
if(pLinkTable->SumOfNode == 0)
{
pLinkTable->pTail = NULL;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
tLinkTableNode * pTempNode = pLinkTable->pHead;
while(pTempNode != NULL)
{
if(pTempNode->pNext == pNode)
{
pTempNode->pNext = pTempNode->pNext->pNext;
pLinkTable->SumOfNode -= 1 ;
if(pLinkTable->SumOfNode == 0)
{
pLinkTable->pTail = NULL;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
pTempNode = pTempNode->pNext;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return FAILURE;
}
/*
* Search a LinkTableNode from LinkTable
* int Condition(tLinkTableNode * pNode);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Condition(tLinkTableNode * pNode, void * args), void * args)
{
if(pLinkTable == NULL || Condition == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != NULL)
{
if(Condition(pNode, args) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
/*
* get LinkTableHead
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable)
{
if(pLinkTable == NULL)
{
return NULL;
}
return pLinkTable->pHead;
}
/*
* get next LinkTableNode
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return NULL;
}
tLinkTableNode * pTempNode = pLinkTable->pHead;
while(pTempNode != NULL)
{
if(pTempNode == pNode)
{
return pTempNode->pNext;
}
pTempNode = pTempNode->pNext;
}
return NULL;
}
- menu.h
#ifndef _menu
#define _menu
int MenuConfig(char * cmd, char * desc, void (*handler)(int argc, char *argv[]));
int ExecuteMenu();
#endif
- menu.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linktable.h"
#include "menu.h"
#define CMD_MAX_LEN 128
#define CMD_MAX_ARGV_LEN 128
#define DESC_LEN 1024
#define CMD_NUM 10
tLinkTable * head = NULL;
void Help(int argc, char *argv[]);
/* data struct and its operations */
typedef struct DataNode
{
tLinkTableNode * pNext;
char* cmd;
char* desc;
void (*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;
}
/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void *)cmd);
}
/* show all cmd in listlist */
int ShowAllCmd(tLinkTable * head)
{
tDataNode *pNode = (tDataNode*)GetLinkTableHead(head);
printf("--------------------------------------------\n");
while(pNode != NULL)
{
printf("%s: \t %s\n", pNode->cmd, pNode->desc);
pNode= (tDataNode*)GetNextLinkTableNode(head, (tLinkTableNode*)pNode);
}
printf("--------------------------------------------\n");
return 0;
}
int MenuConfig(char * cmd, char * desc, void (*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);
}
int ExecuteMenu()
{
while(1)
{
int argc = 0;
char *argv[CMD_MAX_ARGV_LEN];
char cmd[CMD_MAX_LEN];
char *pcmd = NULL;
printf("Input command > ");
pcmd = fgets(cmd, CMD_MAX_LEN, stdin);
if (pcmd == NULL)
{
continue;
}
pcmd = strtok(pcmd, " ");
while (pcmd != NULL && argc < CMD_MAX_ARGV_LEN)
{
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 - %s\n", p->cmd, p->desc);
if(p->handler != NULL)
{
p->handler(argc, argv);
}
}
}
void Help(int argc, char *argv[])
{
ShowAllCmd(head);
}
- mymenu.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "menu.h"
void version(int argc, char *argv[]);
void showtime(int argc, char *argv[]);
void pwd(int argc, char *argv[]);
void add(int argc, char *argv[]);
void sub(int argc, char *argv[]);
void mul(int argc, char *argv[]);
void div(int argc, char *argv[]);
void quit(int argc, char *argv[]);
int main(int argc,char* argv[])
{
MenuConfig("version","Menu Program V1.0", version);
MenuConfig("time", "Show system time", showtime);
MenuConfig("pwd", "Show current working directory", pwd);
MenuConfig("add", "make a add of two numbers", add);
MenuConfig("sub", "make a sub of two numbers", sub);
MenuConfig("mul", "make a mul of two numbers", mul);
MenuConfig("div", "make a div of two numbers", div);
MenuConfig("quit", "Exit menu program", quit);
ExecuteMenu();
}
void version(int argc, char *argv[])
{
printf("Menu Program V1.0\n");
}
void showtime(int argc, char *argv[])
{
struct tm *ptr;
time_t it;
it = time(NULL);
ptr = localtime(&it);
printf("%4d年%02d月%02d日 %d:%d:%d\n", ptr->tm_year + 1900, ptr->tm_mon + 1, ptr->tm_mday,
ptr->tm_hour, ptr->tm_min,ptr->tm_sec);
}
void pwd(int argc, char *argv[])
{
char buf[256];
getcwd(buf, sizeof(buf));
printf("当前路径: %s\n", buf);
}
void add(int argc, char *argv[])
{
if(argc != 3)
{
printf("error count of calculate\n");
}
int num1 = atoi(argv[1]), num2 = atoi(argv[2]);
printf("%d + %d = %d\n", num1, num2, num1 + num2);
}
void sub(int argc, char *argv[])
{
if(argc != 3)
{
printf("error count of calculate\n");
}
int num1 = atoi(argv[1]), num2 = atoi(argv[2]);
printf("%d - %d = %d\n", num1, num2, num1 - num2);
}
void mul(int argc, char *argv[])
{
if(argc != 3)
{
printf("error count of calculate\n");
}
int num1 = atoi(argv[1]), num2 = atoi(argv[2]);
printf("%d * %d = %d\n", num1, num2, num1 * num2);
}
void div(int argc, char *argv[])
{
if(argc != 3)
{
printf("error count of calculate\n");
}
int num1 = atoi(argv[1]), num2 = atoi(argv[2]);
if(num2 != 0)
{
printf("%d / %d = %d\n", num1, num2, num1 / num2);
}
else
{
printf("ERROR: don't division 0\n");
}
}
void quit(int argc, char *argv[])
{
exit(0);
}
- makefile
CC_PTHREAD_FLAGS = -lpthread
CC_FLAGS = -c
CC_OUTPUT_FLAGS = -o
CC = gcc
RM = rm
RM_FLAGS = -f
TARGET = mymenu
OBJS = linktable.o menu.o mymenu.o
all: $(OBJS)
$(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS)
.c.o:
$(CC) $(CC_FLAGS) $<
clean:
$(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak
调试运行
- 使用make命令
- 运行并使用命令
- 上传github
实验总结
(1)Makefile实现对几个.h和.c文件进行编译的工作,即只需make一下就能完成该功能,无需输入复杂的命令;删除编译过后的文件以及备份文件只需make clean一下即可。
(2)liktable.c和linktable.h分别声明定义了可重用链表的数据结构以及对链表的操作,本次实验的数据就是存储在该链表中的,而调用各个函数也就是对链表的遍历并取出某个结点存储的函数地址,之后执行操作。
(3)menu.c和menu.h是基于上述链表来构造对menu的基本框架,包括存储命令的数据结构以及命令查找功能。
(4)mymenu.c中定义了一些简单的命令诸如求和、比大小、退出等等。
(5)menu子系统支持带参数的命令,可用getopt函数获取命令行参数,本次实验中的add,sub,mul都可以实现对于不同的参数会做出不同的操作。