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;
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){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_SIMULATORansi_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 的运作流程,以及新指令的添加。注:特殊指令,比如:方向键等未做分析。