본문 바로가기
프로그래밍/C

C ] 명령 프롬프트 구현 (함수포인터 사용)

by eteo 2022. 8. 25.

 

#include <stdio.h>
#include <string.h> /* for strcmp() strlen*/
//#include <unistd.h> /* for usleep() */
#include <Windows.h>
#include <ctype.h>

// argv MAX
#define MAX_CMD_NUM 10
// string MAX
#define BUF_LEN     128
int cmd_continue = 0;

typedef int cmd_func(int argc, char* argv[]);

struct Command_List
{
	char* cmd;
	cmd_func* func;
	char* help_str;
};

int cmd_test1(int argc, char* argv[])
{
	if (argv[1] == NULL) {
		printf("test 1 command received. \n");
	}
	else {
		printf("test 1 command received. argv[1] is %s. \n", argv[1]);
	}	
	return 0;
}
int cmd_test2(int argc, char* argv[])
{
	if (argv[1] == NULL) {
		printf("test 1 command received. \n");
	}
	else {
		printf("test 1 command received. argv[1] is %s. \n", argv[1]);
	}
	return 0;
}

int cmd_exit(int argc, char* argv[])
{
	printf("exit command received \n");
	cmd_continue = 0;
	return 0;
}
struct Command_List  CmdList[] =
{
	{"test1",	cmd_test1,	"test1 command"},
	{"test2",	cmd_test2,	"test2 command"},
	{"exit",	cmd_exit,	"exit command"},
	{0,0,0}
};

// string을 받아서 줄바꿈문자가 올 때 까지 띄어쓰기 단위로 파싱해 argv, argc에 담음
void parse_input_string(char* input_string, int* argc, char* argv[])
{
	int found_arg = 1;
	int argn = 0;

	while (*input_string)
	{
		if (*input_string == '\n') {
			*input_string = '\0';
			break;
		}

		if (*input_string == ' ') {
			found_arg = 1;
			*input_string = '\0';
		} else if (found_arg) {
			argv[argn++] = input_string;
			found_arg = 0;
		}
		input_string++;
	}

	*argc = argn;
}
void cmd_handler(char* cmd)
{
	// 커맨드 목록 가리킬 포인터
	struct Command_List* pCmdList = CmdList;
	unsigned int	   i, command_found = 0;

	int	  argc;
	char* argv[MAX_CMD_NUM] = {0,};
    
	char  cmd_to_lowercase[BUF_LEN];

	parse_input_string(cmd, &argc, argv);

	// 커맨드명 대소문자 구분없이 처리하기 위해, 소문자로 담을 버퍼 초기화
	memset((void*)cmd_to_lowercase, 0, sizeof(cmd_to_lowercase));
	
	if (argc > 0)
	{
		for (i = 0; i < strlen((const char*)argv[0]); ++i)
			cmd_to_lowercase[i] = (char)tolower(argv[0][i]);
	}

	if (argc)
	{
    	// 커맨드리스트에서 순차확인하여
		while (pCmdList->cmd)
		{
        	// 입력된 커맨드와 일치하는 목록이 있으면 해당 커맨드 처리함수 호출
			if (!strcmp((const char*)pCmdList->cmd, (const char*)cmd_to_lowercase))
			{
				command_found = 1;
				pCmdList->func(argc, argv);
				break;
			}
			++pCmdList;
		}
	}
	if (command_found == 0) printf("command not found!\n");
}

int main(int argc, char* argv[])
{
	char buf[BUF_LEN];

	printf("=======================\n");
	printf("Test program !\n");
	printf("========================\n");

	cmd_continue = 1;

	while (cmd_continue)
	{
		printf("prompt>> ");

		// 줄바꿈문자가 올때까지 읽기
		fgets(buf, sizeof(buf), stdin);

		// 프롬프트 핸들러 호출
		cmd_handler(buf);

		//usleep(1000);
		Sleep(500);

	}

	return 0;
}

 

 

나중에 굉장히 유용하게 사용할 수 있을 것 같다.

 

 

fgets 함수는 스트림에서 num-1개의 문자를 입력 받을 때 까지 또는 개행문자나 EOF를 만날 때 까지 입력을 받아서 str 버퍼에 문자열로 저장한다. 개행문자 포함 입력받은 경우 \n 역시 str에 저장되고 마지막에는 NULL문자가 자동으로 저장된다.

3번째 인수로 stdin 을 전달하면 표준입력에서 입력을 받을 수 있다. 

char* fgets(char* str, int num, FILE* stream);

 

parse_input_string 함수에서는 char* argv[] 에 띄어쓰기를 구분자로 문자열을 파싱해서 저장하고 인자의 개수만큼 argc를 카운트 한다.

 

cmd_handler 함수 아랫부분에서는 전역변수로 선언된 구조체 배열인 CmdList[]에서 strcmp로 일치하는 커맨드가 있는지 찾고 일치하는 커맨드를 찾은 경우 pCmdList->func(argc, argv); 로 함수 포인터를 호출한다.

 

두번째 이후 인자에 따라 디테일하게 명령을 처리하는 부분은 커맨드 함수 안에 써주면 된다.

 

나중에 UART 시리얼 커맨드를 처리하는 부분도 이런 방식으로 짜볼 수 있을 것 같다.