网易云课堂昵称:风清扬pty +《软件工程(C编码实践篇)》MOOC课程作业//mooc.study.163.com/course/USTC-1000002006
实验要求
- 为menu子系统设计接口,并写用户范例代码来实现原来的功能;
- 使用make和make clean来编译程序和清理自动生成的文件;
- 使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;
- 可以使用getopt函数获取命令行参数。
实验主要步骤及代码
- 创建Makefile文件,实现make指令编译功能。解决当项目中文件数目较多时,使用gcc 命令会很繁琐的问题。
CC_FLAGS = -c
CC_OUTPUT_FLAGS = -o
CC = gcc
TARGET = test
OBJS = linktable.o menu.o test.o
RM = rm
RM_FLAGS = -f
all: $(OBJS)
$(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS)
.c.o:
$(CC) $(CC_FLAGS) $<
clean:
$(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak
- menu.c可以使menu程序的升级和维护比较容易,具有重用性。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linktable.h"
#include "menu.h"
#define CMD_MAX_LEN 128
#define DESC_LEN 1024
#define CMD_NUM 10
#define CMD_MAX_ARGV_NUM 32
tLinkTable * head = NULL;
int Help(int argc, char **argv);
typedef struct DataNode
{
tLinkTableNode * pNext;
char* cmd;
char* desc;
int (*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;
}
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);
while(pNode != NULL)
{
printf("%s\t - \t%s\n", pNode->cmd, pNode->desc);
pNode = (tDataNode*)GetNextLinkTableNode(head,(tLinkTableNode *)pNode);
}
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 = "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;
}
/* menu program */
int ExcuteMenu()
{
while(1)
{
// init argc
int argc = 0;
char *argv[CMD_MAX_ARGV_NUM];
char cmd[CMD_MAX_LEN];
char *pcmd = NULL;
// input cmd
printf("Input a cmd > ");
pcmd = fgets(cmd, CMD_MAX_LEN, stdin);
if (pcmd == NULL)
{
continue;
}
// convert cmd line to argc/argv;
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 = FindCmd(head, argv[0]);
if( p == NULL)
{
printf("This is a wrong cmd!\n ");
continue;
}
printf("%s\n", p->desc);
if(p->handler != NULL) {
p->handler(argc, argv);
}
}
}
int Help(int argc, char **argv)
{
printf("-------------------------------------------\n");
ShowAllCmd(head);
printf("-------------------------------------------\n");
return 0;
}
- menu.h
int MenuConfig(char *cmd, char *desc, int (*handler)(int argc, char* argv[]));
int ExecuteMenu();
- test.c文件使用getopt函数获取命令行参数,使menu子系统支持带参数的复杂命令,可以丰富命令的内容。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "menu.h"
int Quit(int argc, char **argv)
{
exit(0);
}
int argtest(int argc, char **argv)
{
const char *optString = "lah";
opterr = 0;
int opt;
while ((opt = getopt(argc, argv, optString)) != -1)
{
switch (opt)
{
case 'l':
printf("this -l option\n");
break;
case 'a':
printf("this -a option\n");
break;
case 'h':
printf("in this cmd, you have 3 option can use:\n");
printf("-l\n");
printf("-a\n");
printf("-h\n");
break;
default:
break;
}
}
// reset global valuable optind
optind = 0;
return 0;
}
int main(int argc, char **argv)
{
MenuConfig("version", "xxx v1.0(Menu program v1.0 inside)", NULL);
MenuConfig("argtest", "test arg option", argtest);
MenuConfig("quit", "quit from xxx", Quit);
ExcuteMenu();
return 0;
}
实验结果
实验总结
- 学会了使用Makefile文件来简化多个文件的编译命令,但是一定注意使用Makefile时$开头的字符串之间要使用tab符号分开,不能使用空格,因为这个的疏忽前面好几次编译一直不通过。
- 学习了menu程序的可重用性。
- 编写代码时一定要认真,不要再因为粗心导致的错误检查修改好多次。