当前位置: 首页 > article >正文

NR_shell运行流程简析

nr_shell 是一套开源 shell 框架,基于框架可创建终端交互功能。
为了记录终端输入指令,以及进行解析处理,nr_shell 提供了一套 cmd 结构体,具体如下:
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 结构体的作用如下:
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' 等,若不是则进行通用处理,否则参考对应的处理办法处理。
#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 完成。
新指令目前不支持接口添加,只能直接在数组表中直接添加。
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 的运作流程,以及新指令的添加。注:特殊指令,比如:方向键等未做分析。

http://www.kler.cn/a/518898.html

相关文章:

  • Java 大视界 -- Java 大数据在元宇宙中的关键技术与应用场景(65)
  • location的使用规则
  • Spring集成Redis|通用Redis工具类
  • 【ES实战】治理项之索引模板相关治理
  • php代码审计2 piwigo CMS in_array()函数漏洞
  • xss靶场
  • Pyecharts之特殊图表的独特展示
  • fatal: unable to access ‘https://github
  • ### 2.5.3 二叉树的基本操作
  • 租赁管理系统如何优化商业资产管理与提升租赁效率探讨
  • 声明,这些内容和我无关
  • 【Axure 模版素材】Arco Design组件库 - AxureMost
  • 使用Java技术开发软件详细流程
  • Coze AI:全面解读及应用场景分析
  • 如何在data.table中处理缺失值
  • 用 HTML 实现新春烟花的详细笔记
  • Ansible介绍与入门学习
  • 「 机器人 」扑翼飞行器控制的当前挑战与后续潜在研究方向
  • C++ 复习总结记录九
  • 电脑无法开机,重装系统后没有驱动且驱动安装失败
  • docker 安装 nginx 详解
  • 【28】Word:石油化工设备技术❗
  • 【机器学习】穷理至极,观微知著:微积分的哲思之旅与算法之道
  • STM32 流水灯与跑马灯的实现
  • Apache Airflow 全面解析
  • 飞牛 fnOS 安装8852be网卡驱动并成功连接