网易云课堂昵称:sa17225159
《软件工程(C编码实践篇)》MOOC课程作业 http://mooc.study.163.com/course/USTC-1000002006
实验七:将menu设计为可重用的子系统
实验要求
- 为menu子系统设计接口,并写用户范例代码来实现原来的功能;
- 使用make和make clean来编译程序和清理自动生成的文件;
- 使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;
- 可以使用getopt函数获取命令行参数。
实验过程
1.建立远程仓库,并下载到本地,远程仓库地址:https://github.com/libaoquan95/seClass_lab7.git
git clone https://github.com/libaoquan95/seClass_lab7.git
并进入版本库并创建相关文件
2 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_ */
3 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;
}
4 menu.h
#ifndef _menu
#define _menu
int MenuConfig(char * cmd, char * desc, void (*handler)(int argc, char *argv[]));
int ExecuteMenu();
#endif
5 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("\t\t %s: \t\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);
}
6.mymenu.c
mymenu.c 利用通用的menu,生成自己的命令行程序。利用argv 和 argc ,实现从输入的命令中提取参数,实现带参数的命令的设计。
/*
* Copyright (C) libaoquan95@github.com, 2017-2018
*
* File name : mymenu.c
* Principal author : libaoquan95
* Subsystem name : mymenu
* Module name : mymenu
* Language : C
* Date of first release : 2017/10/28
* Deacription : This is a mymenu program
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "menu.h"
void PrintSystemTime(int argc, char *argv[]);
void PrintCurrentWorkingDirectory(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("time", "Show system time", PrintSystemTime);
MenuConfig("pwd", "Show current working directory", PrintCurrentWorkingDirectory);
MenuConfig("pwd", "Show current working directory", PrintCurrentWorkingDirectory);
MenuConfig("add", "Calculate the summarize of the two integer numbers", Add);
MenuConfig("sub", "Calculate the subtractions of the two integer numbers", Sub);
MenuConfig("mul", "Calculate the multiplication of the two integer numbers", Mul);
MenuConfig("div", "Calculate the division of the two integer numbers", Div);
MenuConfig("quit", "Exit menu program", Quit);
ExecuteMenu();
}
/**
* print current time
* @param none
* @return none
*/
void PrintSystemTime(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);
}
/**
* print current working directory path
* @param none
* @return none
*/
void PrintCurrentWorkingDirectory(int argc, char *argv[])
{
char buf[256];
getcwd(buf, sizeof(buf));
printf("当前路径: %s\n", buf);
}
/**
* calculate add of two interage numbers
* @param none
* @return calculate result
*/
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);
}
/**
* calculate sub of two interage numbers
* @param none
* @return calculate result
*/
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);
}
/**
* calculate mul of two interage numbers
* @param none
* @return calculate result
*/
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);
}
/**
* calculate div of two interage numbers
* @param none
* @return calculate result
*/
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");
}
}
/**
* quit command
* @param none
* @return none
*/
void Quit(int argc, char *argv[])
{
exit(0);
}
7.makefile
makefile文件中,缩进要用tab键,使用空格缩进会产生错误。
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
8.编译程序并运行
9.上传至远程版本库
实验总结
远程仓库地址:https://github.com/libaoquan95/seClass_lab7.git
- makefile 文件
因为Makefile文件的存在,所以可以使用make命令来编译。makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。主要是三部分内容,先是为menu子系统设计接口,将menu设计为可重用的子系统。然后是Makefile,使得编译的时候不需要再去敲打命令,麻烦,直接执行make就可以了,而且还可以make clean删除编译产生出的文件。
可重用设计
menu作为一个子系统的可重用接口设计:(1)手里有把锤子,看哪里都是钉子;(2)够用就好—不要太具体,也不要太通用。
支持带参数的复杂命令的方法
- shell将命令行解析成argc和argv传递给执行程序;
- 定义一个函数将命令行转换成argc和argv;(3)自定义处理函数内部可以使用getopt函数来获取参数。