昵称:fuguanqi 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006
实验七:将menu设计为可重用的子系统
实验要求
为menu子系统设计接口,并写用户范例代码来实现原来的功能;
使用make和make clean来编译程序和清理自动生成的文件;
使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;
可以使用getopt函数获取命令行参数。
实验步骤:
编写程序代码
撰写Makefile文件
编译测试
提交git
Coding版本库URL:https://git.coding.net/fuguanqi/ASE.git
关键问题总结
输入命令时,会将换行符存入命令的结尾,需要将命令字符串最后的换行符用‘\0‘替换掉。
多次调用Help函数时,getopt函数也会被多次调用,为了达到需求,必须在每次调用完Help函数时将getopt自身所带的全局变量optind进行归零,否则再次输入help命令或带选项的命令时,会影响程序的正常功能,并产生段错误。
源代码
menu.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "linktable.h"
tLinkTable * head = NULL;
int Help();
#define CMD_MAX_LEN 1024
#define CMD_MAX_ARGV_NUM 32
#define DESC_LEN 1024
#define CMD_NUM 10
//char cmd[CMD_MAX_LEN];
/* data struct and its operations */
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;
}
/* 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);
while(pNode != NULL)
{
printf("%s - %s\n", pNode->cmd, pNode->desc);
pNode = (tDataNode*)GetNextLinkTableNode(head,(tLinkTableNode *)pNode);
}
return 0;
}
// int InitMenuData(tLinkTable ** ppLinktable)
// {
// *ppLinktable = CreateLinkTable();
// tDataNode* pNode = (tDataNode*)malloc(sizeof(tDataNode));
// pNode->cmd = "help";
// pNode->desc = "Menu List:";
// pNode->handler = Help;
// AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);
// pNode = (tDataNode*)malloc(sizeof(tDataNode));
// pNode->cmd = "version";
// pNode->desc = "Menu Program V1.0";
// pNode->handler = NULL;
// AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);
// pNode = (tDataNode*)malloc(sizeof(tDataNode));
// pNode->cmd = "quit";
// pNode->desc = "Quit from Menu Program V1.0";
// pNode->handler = Quit;
// AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);
// return 0;
// }
/* menu program */
// tLinkTable * head = NULL;
/* add cmd to menu */
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 Engine Execute */
int ExecuteMenu()
{
//InitMenuData(&head);
/* cmd line begins */
while(1)
{
int argc = 0;
char * argv[CMD_MAX_ARGV_NUM];
char cmd[CMD_MAX_LEN];
char * pcmd = NULL;
printf("Input a cmd > ");
//scanf("%s", cmd);
//tDataNode *p = (tDataNode*)SearchLinkTableNode(head,SearchCondition,(void*)cmd);
pcmd = fgets(cmd, CMD_MAX_LEN, stdin);
if( pcmd == NULL)
{
//printf("This is a wrong cmd!\n ");
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';
// }
int i;
for(i = 0 ; i < argc ; i++ )
{
if (i==argc-1) //replace the line break with "\0"
{
int len = strlen(argv[i]);
*(argv[i] + len - 1) = '\0';
}
}
tDataNode *p = (tDataNode*)SearchLinkTableNode(head,SearchCondition,(void*)argv[0]);
if( p == NULL)
{
printf("This is a wrong cmd!\n");
continue;
}
if(p->handler != NULL)
{
p->handler(argc,argv);
}
printf("%s - %s\n", p->cmd, p->desc);
}
}
static const char *optString = "abcde";
int Help(int argc, char * argv[])
{
ShowAllCmd(head);
int opt;
while((opt = getopt (argc, argv,optString)) != -1)
{
switch (opt)
{
case 'a' :
printf ("\nOption a\n\n" );
break ;
case 'b' :
printf ("\nOption b\n\n" );
break ;
case 'c' :
printf ("\nOption c\n\n");
break ;
case 'd' :
printf ("\nOption d\n\n" );
break ;
case 'e' :
printf ("\nOption e\n\n" );
break ;
}
}
optind=0; //reset the function getopt
return 0;
}
test.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include"menu.h"
int Quit(int argc, char * argv[])
{
/* add XXX clean ops */
exit(0);
}
int main ()
{
MenuConfig("version","XXX V1.0(Menu program v1.0 inside)", NULL);
MenuConfig("quit","Quit from XXX", Quit);
ExecuteMenu();
}
Makefile
CC_PTHREAD_FLAGS = -lpthread
CC_FLAGS = -c
CC_OUTPUT_FLAGS = -o
CC = gcc
RM = rm
RM_FLAGS = -f
TARGET = test1
OBJS = linktable.o menu.o test.o
all: $(OBJS)
$(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS)
.c.o:
$(CC) $(CC_FLAGS) $<
clean:
$(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak