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 的运作流程,以及新指令的添加。注:特殊指令,比如:方向键等未做分析。
相关推荐
倔强的石头1063 分钟前
【Linux指南】动静态库系列(二):从源码复用到目标文件复用:为什么需要把 .o 打包成库
linux·运维·服务器
HLC++12 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室13 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy16 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕17 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫18 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生18 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1119 小时前
linux基础IO
linux·运维·服务器
BelongPanda21 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯21 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算