NR_shell运行流程简析

复制代码
nr_shell 是一套开源 shell 框架,基于框架可创建终端交互功能。
为了记录终端输入指令,以及进行解析处理,nr_shell 提供了一套 cmd 结构体,具体如下:
c 复制代码
typedef struct static_cmd_function_struct
{
	char cmd[NR_SHELL_CMD_NAME_MAX_LENGTH];
	void (*fp)(char argc, char *argv);
	char *description;
} static_cmd_st;

typedef struct shell_history_queue_struct
{
	unsigned short int fp;
	unsigned short int rp;

	unsigned short int len;
	unsigned short int index;

	unsigned short int store_front;
	unsigned short int store_rear;
	unsigned short int store_num;

	char queue[NR_SHELL_MAX_CMD_HISTORY_NUM + 1];
	char buf[NR_SHELL_CMD_HISTORY_BUF_LENGTH + 1];
} shell_his_queue_t;

typedef struct nr_shell
{
	char user_name[NR_SHELL_USER_NAME_MAX_LENGTH];
	const static_cmd_st *static_cmd;
	shell_his_queue_st cmd_his;
} shell_st;
复制代码
nr_shell 启动后,shell_st 结构体将作为 nr_shell 的实例对象维护 nr_shell 的运行环境。
根据结构体可知 shell_st 结构将记录当前用户的名称,当前运行的指令,以及运行过的历史指令。



当输入指令格式后,将通过 shell 函数进行解析处理。
对于指令的具体解析,nr_shell 提供了 ansi_st 进行处理。关于 ansi_st 结构体的作用如下:
c 复制代码
ansi_st nr_ansi;

typedef struct nr_ansi_struct
{
	short p;
	unsigned int counter;
	char current_line[NR_ANSI_LINE_SIZE];

	char combine_buf[NR_ANSI_CTRL_MAX_LEN];
	char cmd_num;
	char combine_state;
} ansi_st;

//初始阶段,counter 代表指令格式字符串计数为0,p 代表当前字符索引为 -1,current_line 代表指令格式字符串
void ansi_init(ansi_st *ansi)
{
	ansi->counter = 0;		
	ansi->p = -1;

	ansi->current_line[ansi->counter] = '\0';

	ansi->cmd_num = 0;
	ansi->combine_state = ANSI_NO_CTRL_CHAR;
}
复制代码
接下来是完整的字符串解析过程:
对输入的字符串逐字解析,首先判断其是否为特殊符号,比如:'\b','\n','\033' 等,若不是则进行通用处理,否则参考对应的处理办法处理。
c 复制代码
#define shell(c)												\
	{															\
		if (ansi_get_char(c, &nr_ansi) == NR_SHELL_END_CHAR)	\
		{														\
			shell_parser(&nr_shell, nr_ansi.current_line);		\
			ansi_clear_current_line(&nr_ansi);					\
		}														\
	}

char ansi_get_char(char x, ansi_st *ansi)
{
	int cmd_id = -1;

	if (ansi->combine_state == ANSI_NO_CTRL_CHAR)	
	{
		cmd_id = ansi_search_char(x, nr_ansi_in_special_symbol);
		if (cmd_id >= 0)
		{
			if (nr_ansi_in_special_symbol_fun[cmd_id] != NULL)
			{
				nr_ansi_in_special_symbol_fun[cmd_id](ansi);
			}
		}
		else if (x == '\033')
		{
			ansi->combine_state = ANSI_WAIT_CTRL_CHAR_END;
			ansi->combine_buf[ansi->cmd_num] = x;
			ansi->cmd_num++;
		}
		else
		{
			nr_ansi_common_char_slover(ansi, x);
		}
	}
	else if (ansi->combine_state == ANSI_WAIT_CTRL_CHAR_END)
	{
		ansi->combine_buf[ansi->cmd_num] = x;

		if (('a' <= x && 'z' >= x) || ('A' <= x && 'Z' >= x) || x == '~')
		{
			cmd_id = ansi_search_char(x, nr_ansi_in_cmd);
			nr_ansi_in_cmd_fun[cmd_id](ansi);

			ansi->cmd_num = 0;
			anis->combine_state = ANSI_NO_CTRL_CHAR;
		}
		else if (ansi->cmd_num > 18)
		{
			ansi->cmd_num = 0;
			ansi->combine_state = ANSI_NO_CTRL_CHAR;
		}
		else
		{
			ansi->cmd_num++;
		}
	}
	else 
	{
		ansi->combine_state = ANSI_NO_CTRL_CHAR;
	}

	return x;
}

int ansi_search_char(char x, const char *buf)
{
	int i = 0;
	
	for (i = 0; (buf[i] != x) && (buf[i] != '\0'); i++)
		;

	if (buf[i] != '\0') 
	{
		return i;
	}
	else 
	{
		return -1;
	}
}

void nr_ansi_common_char_slover(ansi_st *ansi, char x)
{
	unsigned int i;

	if (ansi->counter < NR_ANSI_LINE_SIZE - 2)
	{
		//该判断条件的目的是将 '\0' 字符向后复制,空出中间字符添加新的字符
		if (ansi->p < ansi->counter)	
		{
			for (i = ansi->counter; i > ansi->p; i--)
			{
				ansi->current_line[i] = ansi->current_line[i - 1];
			}
		}

		//当前字符位置与字符串个数自增
		ansi->p++;
		ansi->counter++;

		//添加输入的字符
		ansi->current_line[ansi->p] = x;
		
		//字符串添加结束标志符
		ansi->current_line[ansi->counter] = '\0'
		if (ansi->p + 1 < ansi->counter) 
		{
			shell_printf("\033[1@");
		}

#ifndef NR_MICRO_SHELL_SIMULATOR
		ansi_show_char(x);
#endif
	}	
	else
	{
		ansi->counter = NR_ANSI_LINE_SIZE - 3;
		if (ansi->p >= ansi->counter)
		{
			ansi->p = ansi->counter - 1;
		}
		ansi->current_line[ansi->counter] = '\0';
	}
}

void shell_parser(shell_st *shell, char *str)
{
	char argc = 0;
	char argv[NR_SHELL_CMD_LINE_MAX_LENGTH + NR_SHELL_CMD_PARAS_MAX_NUM];
	char *token = str;
	shell_fun_t fp;
	char index = NR_SHELL_CMD_PARAS_MAX_NUM;

	if (shell_his_queue_search_cmd(&shell->cmd_his, str) == 0 && str[0] != '\0')
	{
		shell_his_queue_add_cmd(&shell->cmd_his, str);
	}

	if (strlen(str) > NR_SHELL_CMD_LINE_MAX_LENGTH)
	{
		shell_printf("this command is too long."NR_SHELL_NEXT_LINE);
		shell_printf("%s", shell->user_name);
		return;
	}

	token = nr_shell_strtok(token, " ");

	//从指令表中查看当前指令是否存在,因此需根据实际情况在指令表中添加
	fp = shell_search_cmd(shell, str);

	if (fp == NULL)
	{
		if (isalpha(str[0]))
		{
			shell_printf("no command named: %s"NR_SHELL_NEXT_LINE, token);
		}
	}
	else
	{
		argv[argc] = index;
		strcpy(argv + index, str);
		index += strlen(str) + 1;
		argc++;

		token = nr_shell_strtok(NULL, " ");
	
		//循环获取指令参数
		while (token != NULL)
		{
			argv[argc] = index;
			strcpy(argv + index, token);
			index += strlen(token) + 1;
			argc++;
			token = nr_shell_strtok(NULL, " ");
		}
	}

	if (fp != NULL)
	{
		fp(argc, argv);		//执行指令对应的函数接口
	}	
}
复制代码
关于 nr_shell 对指令的支持,主要通过框架中的全局变量 nr_shell 完成。
新指令目前不支持接口添加,只能直接在数组表中直接添加。
c 复制代码
shell_st nr_shell = {
{
	.user_name = NR_SHELL_USER_NAME,
	.static_cmd = nr_cmd_start_add;
};

#define nr_cmd_start_add (&static_cmd[0])

const static_cmd_st static_cmd[] =
{
	{"ls", shell_ls_cmd},
	{"test", shell_test_cmd},
	{"\0", NULL}
};
复制代码
以上,就是对 nr_shell 框架的简单分析,只针对 nr_shell 的运作流程,以及新指令的添加。注:特殊指令,比如:方向键等未做分析。
相关推荐
用户9718356334663 小时前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪4 小时前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠20 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush421 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行52021 小时前
Linux 11 动态监控指令top
linux
不会C语言的男孩1 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
古城小栈1 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
凡人叶枫1 天前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
2601_961875241 天前
决战申论100题2026|最新|范文
linux·容器·centos·debian·ssh·fabric·vagrant
java_cj1 天前
深入kube-apiserver认证机制:从Bearer Token到mTLS的完整认证链解析
linux·运维·服务器·云原生·容器·kubernetes