Files
ESPC3-wireless/components/system/source/shell/sh.c

3067 lines
88 KiB
C
Raw Normal View History

2024-03-28 12:19:52 +08:00
/**
* @file sh.c
* @author LokLiang (lokliang@163.com)
* @brief
* @version 0.1
* @date 2023-03-21
*
* @copyright Copyright (c) 2023
*
*/
#include "sh.h"
2025-02-13 17:17:07 +08:00
#include "sh_vset.h"
2024-03-28 12:19:52 +08:00
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#define SYS_LOG_DOMAIN "sh"
#include "sys_log.h"
#ifndef CONFIG_SH_MAX_PARAM
#define CONFIG_SH_MAX_PARAM (CONFIG_SH_MAX_LINE_LEN / 5) /* 以空格为分隔符的最多命令段数 */
#endif
#define _SH_ARRAY_COUNT(NAME) (sizeof(NAME) / sizeof(NAME[0]))
#define _IS_ALPHA(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
#define _IS_DIGIT(c) (c >= '0' && c <= '9')
#define _YEAR ((((__DATE__[7] - '0') * 10 + (__DATE__[8] - '0')) * 10 + (__DATE__[9] - '0')) * 10 + (__DATE__[10] - '0'))
#define _MONTH ((__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n') ? 1 \
: (__DATE__[0] == 'F' && __DATE__[1] == 'e' && __DATE__[2] == 'b') ? 2 \
: (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r') ? 3 \
: (__DATE__[0] == 'A' && __DATE__[1] == 'p' && __DATE__[2] == 'r') ? 4 \
: (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y') ? 5 \
: (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n') ? 6 \
: (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l') ? 7 \
: (__DATE__[0] == 'A' && __DATE__[1] == 'u' && __DATE__[2] == 'g') ? 8 \
: (__DATE__[0] == 'S' && __DATE__[1] == 'e' && __DATE__[2] == 'p') ? 9 \
: (__DATE__[0] == 'O' && __DATE__[1] == 'c' && __DATE__[2] == 't') ? 10 \
: (__DATE__[0] == 'N' && __DATE__[1] == 'o' && __DATE__[2] == 'v') ? 11 \
: 12)
#define _DAY ((__DATE__[4] == ' ' ? 0 : __DATE__[4] - '0') * 10 + (__DATE__[5] - '0'))
static sys_pslist_t s_cmd_list; // 已注册的根命令链
static sys_pslist_t s_cmd_list_hide; // 已注册的根命令链(不会出现在补全中)
static const sh_key_t *_find_key(sh_t *sh_hdl, const char *code); // 在热键链中查找完全匹配的节点
static const sh_cmd_t *_find_cmd(sh_t *sh_hdl, const sh_cmd_reg_t **dest_reg_struct, const sh_cmd_t *sub_cmd, const char *cmd); // 在所有命令结构中中查找完全匹配的节点
static const sh_cmd_t *_find_root_cmd(sh_t *sh_hdl, const sh_cmd_reg_t **dest_reg_struct, const char *cmd); // 在根命令链中查找完全匹配的节点
static int _register_cmd(sh_t *sh_hdl, sys_pslist_t *list, const sh_cmd_reg_t *sh_reg); // 注册命令
static int _register_cmd_check_fn(char *err_cmd, const sh_cmd_t *cmd); // 检查 sh_cmd_t::fn 的类型是否符合规则
static void _find_and_run_key(sh_t *sh_hdl, char c); // 一系列动作:保存一个字符到热键缓存,并尝试查找对应的热键功能函数。缓存会被自动清除。
static void _insert_str(sh_t *sh_hdl, const char *str); // 在当前的光标前插入一个字符并显示
static void _read_param(int *argc, const char *argv[CONFIG_SH_MAX_PARAM], char *dest, const char *src); // 从 src 中取得结果,输出到 int *argc, char **argv[], char *dest
static void _store_history(sh_t *sh_hdl, const char *src); // 保存历史记录
static void _apply_history(sh_t *sh_hdl); // 当在回翻历史记录时,一旦对记录修改则立即将当前记录复制到缓存中
static unsigned _get_end_pos(sh_t *sh_hdl); // 根据当前显示内容,获取最大光标位置
static void _store_cmd_bak(sh_t *sh_hdl, const char *line, int pos, int n); // 保存擦除的数据
static void _clear_argv(int argc, const char *argv[]); // 使剩余的字符串指针指向空字符串
static const char *sh_ctrl_get_prompt(sh_t *sh_hdl); // 获取当前提示符
static const char *sh_ctrl_get_line(sh_t *sh_hdl); // 获取当前已输入(或回显的历史)的字符串
static sh_cp_info_t _completion_init(const char *cmd); // 初始化待自动补全数据
static void _completion_update(
sh_t *sh_hdl,
sh_cp_info_t *info,
const sh_cmd_t *sub_cmd,
bool print_match,
int max_lines,
int flag_parent);
static sh_cp_op_t _sh_completion_cmd(sh_t *sh_hdl, int argc, const char *argv[], int flag_parent);
static bool _do_completion_cmd(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cmd_info_t *cmd_info, bool print_match, int flag_parent); // 执行一次自动补全命令的功能
static bool _do_completion_param(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cp_param_t *sh_param, bool print_match); // 执行一次自动补全参数的功能
static bool _do_completion_insert(sh_t *sh_hdl, sh_cp_info_t *info); // 执行自动补全
static void _do_restore_line(sh_t *sh_hdl); // 在展示选项后恢复命令行的显示
static int _calc_list_algin(sh_cp_info_t *info); // 确定每个命令的对齐的长度
static bool _arg_is_end(sh_t *sh_hdl); // 当前输入的最后一个参数是否完整(以空格分开)
static void _update_cursor(sh_t *sh_hdl); // 同步热键功能:更新光标位置记录
#if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1)
static const char *_skipwhite(const char *q);
static double _strtod(const char *str, char **end); // https://gitee.com/mirrors_mattn/strtod/blob/master/strtod.c
#endif
/**
* @brief sh_register_key()
*
* @param code @ref @c sh_vt100.h
* @return const sh_key_t*
*/
static const sh_key_t *_find_key(sh_t *sh_hdl, const char *code)
{
const sh_key_reg_t *test_reg_key;
int match_count = 0;
SYS_PSLIST_FOR_EACH_CONTAINER(&sh_hdl->key_list, test_reg_key, node)
{
const sh_key_t *test_key = test_reg_key->key;
for (int i = 0; *(int *)&test_key[i] != 0; i++)
{
if (strcmp(test_key[i].code, code) == 0)
{
return &test_key[i];
}
if (strncmp(test_key[i].code, code, sh_hdl->key_stored) == 0)
{
match_count++;
}
}
}
if (match_count == 0)
{
sh_hdl->key_stored = 0;
sh_hdl->key_str[sh_hdl->key_stored] = '\0';
}
return NULL;
}
/**
* @brief sh_register_cmd() sh_register_cmd_hide() sh_register_key_cmd()
*
*
* @param dest_reg_struct[out]
2025-02-13 17:17:07 +08:00
* @param sub_cmd NULL -- ! NULL
2024-03-28 12:19:52 +08:00
* @param cmd
* @return const sh_cmd_t* NULL -- ! NULL
*/
static const sh_cmd_t *_find_cmd(sh_t *sh_hdl, const sh_cmd_reg_t **dest_reg_struct, const sh_cmd_t *sub_cmd, const char *cmd)
{
const sh_cmd_t *test_cmd;
if (sh_hdl->select_cmd && sub_cmd == NULL)
{
test_cmd = sh_hdl->select_cmd->sub_fn.sub_cmd;
for (int i = 0; test_cmd[i].cmd != NULL; i++)
{
if ((test_cmd->cmd_func != NULL || test_cmd->sub_fn.sub_cmd != NULL) &&
strcmp(test_cmd[i].cmd, cmd) == 0)
{
if (dest_reg_struct)
{
*dest_reg_struct = sh_hdl->select_reg_struct;
}
return &test_cmd[i];
}
}
}
if (sub_cmd == NULL)
{
if (dest_reg_struct)
{
*dest_reg_struct = NULL;
}
return _find_root_cmd(sh_hdl, dest_reg_struct, cmd);
}
else
{
test_cmd = sub_cmd;
for (int i = 0; test_cmd[i].cmd != NULL; i++)
{
if ((test_cmd->cmd_func != NULL || test_cmd->sub_fn.sub_cmd != NULL) &&
strcmp(test_cmd[i].cmd, cmd) == 0)
{
return &test_cmd[i];
}
}
return NULL;
}
}
/**
* @brief sh_register_cmd() sh_register_cmd_hide() sh_register_key_cmd()
*
* @param dest_reg_struct[out]
* @param cmd
* @return const sh_cmd_t* NULL -- ! NULL
*/
static const sh_cmd_t *_find_root_cmd(sh_t *sh_hdl, const sh_cmd_reg_t **dest_reg_struct, const char *cmd)
{
const sh_cmd_reg_t *sh_reg;
sys_pslist_t *cmd_list[] = {
&s_cmd_list_hide,
&s_cmd_list,
NULL,
};
if (sh_hdl)
{
cmd_list[sizeof(cmd_list) / sizeof(cmd_list[0]) - 1] = &sh_hdl->cmd_list;
}
for (int i = 0; i < sizeof(cmd_list) / sizeof(cmd_list[0]); i++)
{
sys_pslist_t *test_list = cmd_list[i];
if (test_list != NULL)
{
SYS_PSLIST_FOR_EACH_CONTAINER(test_list, sh_reg, node)
{
const sh_cmd_t *reg_cmd = sh_reg->cmd;
for (int i = 0; *(int *)&reg_cmd[i] != 0; i++)
{
const sh_cmd_t *test_cmd = &reg_cmd[i];
if ((test_cmd->cmd_func != NULL || test_cmd->sub_fn.sub_cmd != NULL) &&
strcmp(test_cmd->cmd, cmd) == 0)
{
if (dest_reg_struct)
{
*dest_reg_struct = sh_reg;
}
return test_cmd;
}
}
}
}
}
return NULL;
}
/**
* @brief
*
* @param list @ref @c sh_register_cmd(),sh_register_cmd_hide(),sh_register_key_cmd()
* @param sh_reg
* @return int 0 -- ! 0 --
*/
static int _register_cmd(sh_t *sh_hdl, sys_pslist_t *list, const sh_cmd_reg_t *sh_reg)
{
const sh_cmd_t *reg_cmd = sh_reg->cmd;
for (int i = 0; *(int *)&reg_cmd[i] != 0; i++)
{
const sh_cmd_t *test_cmd = &reg_cmd[i];
char err_cmd[0x100];
if (_register_cmd_check_fn(err_cmd, test_cmd))
{
SYS_LOG_WRN("sub_fn: pointer type mismatch in conditional expression!\r\n"
"Defined in file: %s:%d\r\n"
"Command:%s",
sh_reg->file,
sh_reg->line,
err_cmd);
return -1;
}
if (strchr(test_cmd->cmd, ' '))
{
if (sh_hdl && sh_hdl->disable_echo == 0)
SYS_LOG_WRN("Command cannot contain spaces: '%s'\r\n", test_cmd->cmd);
return -1;
}
if (_find_root_cmd(sh_hdl, NULL, test_cmd->cmd))
{
if (sh_hdl && sh_hdl->disable_echo == 0)
SYS_LOG_WRN("Repeat command: '%s'\r\n", test_cmd->cmd);
return -1;
}
}
sys_pslist_append(list, &sh_reg->node);
return 0;
}
/**
* @brief _register_cmd() sh_cmd_t::fn
*
* @param err_cmd[out]
* @param cmd
* @retval 0 sh_cmd_t::fn
* @retval -1 sh_cmd_t::fn
*/
static int _register_cmd_check_fn(char *err_cmd, const sh_cmd_t *cmd)
{
for (int i = 0; *(int *)&cmd[i] != 0; i++)
{
const sh_cmd_t *test_cmd = &cmd[i];
strcpy(err_cmd, " ");
strcpy(&err_cmd[1], test_cmd->cmd);
if (test_cmd->cmd_func == NULL) // 需要子命令
{
if (test_cmd->fn_type == _SH_SUB_TYPE_SUB)
{
if (_register_cmd_check_fn(&err_cmd[strlen(err_cmd)], test_cmd->sub_fn.sub_cmd) != 0) // 递归查检子命令
{
return -1;
}
}
else
{
if (test_cmd->fn_type != _SH_SUB_TYPE_VOID || test_cmd->sub_fn.nul != NULL)
{
return -1;
}
}
}
else // 是最终命令
{
if (test_cmd->fn_type != _SH_SUB_TYPE_CPFN)
{
if (test_cmd->fn_type != _SH_SUB_TYPE_VOID || test_cmd->sub_fn.nul != NULL)
{
return -1;
}
}
}
}
err_cmd[0] = '\0';
return 0;
}
/**
* @brief
*
* @param c
*/
static void _find_and_run_key(sh_t *sh_hdl, char c)
{
if (sh_hdl->key_stored < sizeof(sh_hdl->key_str) - 1)
{
sh_hdl->key_str[sh_hdl->key_stored++] = c;
sh_hdl->key_str[sh_hdl->key_stored] = '\0';
const sh_key_t *match_key = _find_key(sh_hdl, sh_hdl->key_str);
if (match_key)
{
sh_hdl->key_stored = 0;
sh_hdl->key_str[sh_hdl->key_stored] = '\0';
match_key->key_func(sh_hdl);
return;
}
}
if (sh_hdl->key_stored >= sizeof(sh_hdl->key_str))
{
sh_hdl->key_stored = 0;
sh_hdl->key_str[sh_hdl->key_stored] = '\0';
}
}
/**
* @brief
*
* @param str
*/
static void _insert_str(sh_t *sh_hdl, const char *str)
{
int len = strlen(str);
if (len)
{
int remain = sizeof(sh_hdl->cmd_line) - (sh_hdl->cmd_buf - sh_hdl->cmd_line) - sh_hdl->cmd_stored - 1;
if (len > remain)
{
len = remain;
}
_apply_history(sh_hdl);
if (sh_hdl->cmd_stored < remain)
{
if (sh_hdl->port.insert_str) /* 设置终端插入一个字符 */
{
sh_hdl->port.insert_str(sh_hdl, str);
}
for (int i = sh_hdl->cmd_stored - 1 + len; i > sh_hdl->cmd_input; i--) /* 使光标之后的数据往后移动 len 个字节 */
sh_hdl->cmd_buf[i] = sh_hdl->cmd_buf[i - len];
for (int i = 0; i < len; i++)
sh_hdl->cmd_buf[sh_hdl->cmd_input + i] = str[i];
sh_hdl->cmd_input += len;
sh_hdl->cmd_stored += len;
sh_hdl->sync_cursor += len;
sh_hdl->cmd_buf[sh_hdl->cmd_stored] = '\0';
/* 启用刷新命令行模式 */
if (sh_hdl->port.clear_line)
{
sh_refresh_line(sh_hdl);
}
}
}
}
/**
* @brief
* src int *argc, char **argv[], char *dest
*
* @param argc[out]
* @param argv[out] dest
* @param dest[out] 使
* @param src
*/
static void _read_param(int *argc, const char *argv[CONFIG_SH_MAX_PARAM], char *dest, const char *src)
{
2025-02-13 17:17:07 +08:00
bool end = false;
2024-03-28 12:19:52 +08:00
*argc = 0;
2025-02-13 17:17:07 +08:00
while (end == false && *argc < CONFIG_SH_MAX_PARAM)
2024-03-28 12:19:52 +08:00
{
int len = 0;
/* 以空格为分隔符,分段提取命令到 dest */
while (*src == ' ')
{
++src;
}
if (*src == '"') /* 使用双引号分隔的字符串 */
{
++src;
while (1)
{
dest[len] = src[len];
if (dest[len] == '"')
{
++src;
dest[len] = '\0';
}
if (dest[len] == '\0')
{
break;
}
if (dest[len] == '\\')
{
++src;
switch (src[len])
{
case '\0':
--src;
break;
case 'b':
dest[len] = '\b';
break;
case 'f':
dest[len] = '\f';
break;
case 'n':
dest[len] = '\n';
break;
case 'r':
dest[len] = '\r';
break;
case 't':
dest[len] = '\t';
break;
case '\"':
case '\\':
case '/':
dest[len] = src[len];
break;
default:
dest[len] = src[len];
break;
}
}
++len;
}
}
else
{
char split_char;
if (*src == '\'') /* 使用单引号分隔的字符串 */
{
split_char = '\'';
++src;
}
else /* 使用空格分隔的字符串 */
{
split_char = ' ';
}
while (1)
{
dest[len] = src[len];
if (dest[len] == split_char)
{
++src;
dest[len] = '\0';
}
2025-02-13 17:17:07 +08:00
else if (dest[len] == '#' && split_char == ' ') /* 注释 */
{
++src;
dest[len] = '\0';
end = true;
}
2024-03-28 12:19:52 +08:00
if (dest[len] == '\0')
{
break;
}
++len;
}
}
if (len) // dest 数据有效
{
argv[*argc] = dest;
++(*argc);
dest = &dest[len + 1];
src = &src[len];
}
else
{
break;
}
}
}
/**
* @brief
*
* @param src
*/
static void _store_history(sh_t *sh_hdl, const char *src)
{
if (sh_hdl->disable_history == 0)
{
int len = strlen(src) + 1;
int history_valid_num;
if (*src == '\0')
{
return;
}
if (len > sh_hdl->history_size)
{
return;
}
if (strcmp(src, sh_hdl->cmd_history) != 0)
{
/* 更新历史信息 */
for (int i = sh_hdl->history_index_num - 1; i > 0; i--)
sh_hdl->history_index[i] = sh_hdl->history_index[i - 1] + len;
/* 分析并刷新有效记录数 */
for (history_valid_num = 0; history_valid_num < sh_hdl->history_index_num - 1; history_valid_num++)
{
int test_index = sh_hdl->history_index[history_valid_num];
if (test_index > sh_hdl->history_size)
{
--history_valid_num;
break;
}
if (sh_hdl->history_index[history_valid_num + 1] <= test_index)
{
break;
}
}
sh_hdl->history_valid_num = history_valid_num;
/* 使历史数据往后移动 len 字节 */
for (int i = sh_hdl->history_size - 1; i >= len; i--)
sh_hdl->cmd_history[i] = sh_hdl->cmd_history[i - len];
/* 复制新数据 */
strcpy(sh_hdl->cmd_history, src);
}
}
}
/**
* @brief
*
* @param sh_hdl
*/
static void _apply_history(sh_t *sh_hdl)
{
if (sh_hdl->history_show)
{
char *str = &sh_hdl->cmd_history[sh_hdl->history_index[sh_hdl->history_show - 1]];
strcpy(sh_hdl->cmd_buf, str);
sh_hdl->cmd_stored = strlen(str);
sh_hdl->history_show = 0;
}
sh_hdl->tab_press_count = 0;
sh_hdl->cp_resource_flag = 0;
}
/**
* @brief
*
* @return unsigned 0..n
*/
static unsigned _get_end_pos(sh_t *sh_hdl)
{
if (sh_hdl->history_show)
return strlen(&sh_hdl->cmd_history[sh_hdl->history_index[sh_hdl->history_show - 1]]);
else
return sh_hdl->cmd_stored;
}
/**
* @brief
* KEY_CTR_U KEY_CTR_K KEY_CTR_W
* KEY_CTR_Y
*
* @param line
* @param pos 0..n
* @param n
*/
static void _store_cmd_bak(sh_t *sh_hdl, const char *line, int pos, int n)
{
if (sh_hdl->cmd_bank && sh_hdl->bank_size)
{
if (n > 0)
{
if (n < sh_hdl->bank_size - 1)
{
for (int i = 0; i < n; i++)
{
sh_hdl->cmd_bank[i] = line[pos + i];
}
sh_hdl->cmd_bank[n] = '\0';
}
else
{
sh_hdl->cmd_bank[0] = '\0';
}
}
}
}
/**
* @brief 使
*
* @param argc
* @param argv
*/
static void _clear_argv(int argc, const char *argv[])
{
for (int i = argc; i < CONFIG_SH_MAX_PARAM; i++)
{
argv[i] = NULL;
}
}
/**
* @brief ()
*
* @param sh_hdl
* @return const char*
*/
static const char *sh_ctrl_get_prompt(sh_t *sh_hdl)
{
if (sh_hdl->prompt)
return sh_hdl->prompt;
else
return ">>> ";
}
/**
* @brief
*
* @return const char*
*/
static const char *sh_ctrl_get_line(sh_t *sh_hdl)
{
if (sh_hdl->history_show)
{
return &sh_hdl->cmd_history[sh_hdl->history_index[sh_hdl->history_show - 1]];
}
else
{
return sh_hdl->cmd_buf;
}
}
/**
* @brief
*
* @param port
* @return int
*/
int sh_register_port(sh_t *sh_hdl, const sh_port_t *port)
{
sh_hdl->port = *port;
return -!port;
}
/**
* @brief
* @ref @c SH_REGISTER_CMD
*
* @param sh_reg
* @return int 0 -- ! 0 --
*/
int sh_register_cmd(const sh_cmd_reg_t *sh_reg)
{
return _register_cmd(NULL, &s_cmd_list, sh_reg);
}
/**
* @brief
* @ref @c SH_REGISTER_CMD
*
* @param sh_reg
* @return int 0 -- ! 0 --
*/
int sh_register_cmd_hide(const sh_cmd_reg_t *sh_reg)
{
return _register_cmd(NULL, &s_cmd_list_hide, sh_reg);
}
/**
* @brief
*
* @param sh_reg
* @return int 0 -- ! 0 --
*/
int sh_unregister_cmd(const sh_cmd_reg_t *sh_reg)
{
bool ret = (sh_reg == NULL);
ret = (ret != false ? ret : sys_pslist_find_and_remove(&s_cmd_list, &sh_reg->node));
ret = (ret != false ? ret : sys_pslist_find_and_remove(&s_cmd_list_hide, &sh_reg->node));
return -!ret;
}
2025-02-13 17:17:07 +08:00
/**
* @brief
*/
void sh_unregister_all(sh_t *sh_hdl)
{
sys_pslist_init(&s_cmd_list);
sys_pslist_init(&s_cmd_list_hide);
sys_pslist_init(&sh_hdl->cmd_list);
}
2024-03-28 12:19:52 +08:00
/**
* @brief
* /
*
* @param sh_key
* @return int 0 -- ! 0 --
*/
int sh_register_key(sh_t *sh_hdl, const sh_key_reg_t *sh_key)
{
if (_find_key(sh_hdl, sh_key->key->code))
{
if (sh_hdl->disable_echo == 0)
SYS_LOG_WRN("Repeat key code: '%s'\r\n", sh_key->key->code);
return -1;
}
sys_pslist_append(&sh_hdl->key_list, &sh_key->node);
return 0;
}
/**
* @brief
* @ref @c SH_REGISTER_CMD
* /
*
* @param sh_key
* @return int 0 -- ! 0 --
*/
int sh_register_key_cmd(sh_t *sh_hdl, const sh_cmd_reg_t *sh_reg)
{
return _register_cmd(sh_hdl, &sh_hdl->cmd_list, sh_reg);
}
/**
* @brief
* /
*
* @param sh_key
* @return int 0 -- ! 0 --
*/
int sh_unregister_key(sh_t *sh_hdl, const sh_key_reg_t *sh_key)
{
return -!sys_pslist_find_and_remove(&sh_hdl->key_list, &sh_key->node);
}
/**
* @brief
* /
*
* @param sh_key
* @return int 0 -- ! 0 --
*/
int sh_unregister_key_cmd(sh_t *sh_hdl, const sh_cmd_reg_t *sh_reg)
{
return -!sys_pslist_find_and_remove(&sh_hdl->cmd_list, &sh_reg->node);
}
/**
* @brief sh_init_vt100()
* @ref @c sh_vt100.c@_install_shell_vt100()
*
* @param mem
* @param size
*/
void sh_config_history_mem(sh_t *sh_hdl, void *mem, int size)
{
if (size > 10)
{
int rem = (sizeof(*sh_hdl->history_index) - (unsigned)mem % sizeof(*sh_hdl->history_index)) % sizeof(*sh_hdl->history_index);
size -= rem;
sh_hdl->history_index = (void *)((unsigned)mem + rem);
int history_index_num = size / 10 + 1;
if (history_index_num > (1 << sizeof(*sh_hdl->history_index) * 8) - 1)
{
history_index_num = (1 << sizeof(*sh_hdl->history_index) * 8) - 1;
}
sh_hdl->history_index_num = history_index_num;
sh_hdl->cmd_history = (char *)&sh_hdl->history_index[sh_hdl->history_index_num];
sh_hdl->history_size = size - sizeof(*sh_hdl->history_index) * sh_hdl->history_index_num;
sh_hdl->history_valid_num = 0;
sh_hdl->history_show = 0;
}
}
/**
* @brief sh_ctrl_del_word(), sh_ctrl_del_left(), sh_ctrl_del_right() sh_init_vt100()
* @ref @c sh_vt100.c@_install_shell_vt100()
*
* @param mem
* @param size
*/
void sh_config_backup_mem(sh_t *sh_hdl, void *mem, int size)
{
sh_hdl->cmd_bank = mem;
sh_hdl->bank_size = size;
}
/**
* @brief
*
*
* @param sh_hdl sh_init_vt100()
* @param c
*/
void sh_putc(sh_t *sh_hdl, char c)
{
SYS_ASSERT(sh_hdl != NULL, "");
if (sh_hdl->exec_flag == 0)
{
if (c < ' ' || c >= 0x7F || sh_hdl->key_stored > 0)
{
_find_and_run_key(sh_hdl, c);
return;
}
else
{
char str[2] = {c, '\0'};
_insert_str(sh_hdl, str);
}
}
}
/**
* @brief sh_putc()
*
* @param str
*/
void sh_putstr(sh_t *sh_hdl, const char *str)
{
SYS_ASSERT(sh_hdl != NULL, "");
char buf[sizeof(sh_hdl->cmd_line)];
const char *from = str;
char *to = buf;
for (int i = 0; i < sizeof(buf);)
{
char c = from[i];
if (c < ' ' || c >= 0x7F || sh_hdl->key_stored > 0 || i >= sizeof(buf) - 1)
{
to[i++] = '\0';
_insert_str(sh_hdl, to);
if (c == '\0')
{
break;
}
else
{
sh_putc(sh_hdl, c);
from = &from[i];
to = &to[i];
i = 0;
}
}
else
{
to[i++] = c;
}
}
}
/**
* @brief sh_putstr()
*
* @param str
*/
void sh_putstr_quiet(sh_t *sh_hdl, const char *str)
{
SYS_ASSERT(sh_hdl != NULL, "");
bool disable_echo = sh_hdl->disable_echo;
bool disable_history = sh_hdl->disable_history;
sh_port_t port_bak = sh_hdl->port;
sh_hdl->disable_echo = true;
sh_hdl->disable_history = true;
sh_hdl->port.insert_str = NULL;
sh_hdl->port.set_cursor_hoffset = NULL;
sh_hdl->port.clear_line = NULL;
sh_putstr(sh_hdl, str);
sh_hdl->port = port_bak;
sh_hdl->disable_history = disable_history;
sh_hdl->disable_echo = disable_echo;
}
/**
* @brief
*
* @param prompt
*/
void sh_set_prompt(sh_t *sh_hdl, const char *prompt)
{
SYS_ASSERT(sh_hdl != NULL, "");
sh_hdl->prompt = prompt;
}
/**
* @brief
*
*/
void sh_reset_line(sh_t *sh_hdl)
{
SYS_ASSERT(sh_hdl != NULL, "");
sh_hdl->cmd_input = 0;
sh_hdl->sync_cursor = 0;
sh_hdl->cmd_stored = 0;
sh_hdl->cmd_buf[sh_hdl->cmd_stored] = '\0';
sh_hdl->key_stored = 0;
sh_hdl->key_str[sh_hdl->key_stored] = '\0';
sh_hdl->tab_press_count = 0;
sh_hdl->cp_resource_flag = 0;
}
/**
* @brief
*
*/
int sh_echo(sh_t *sh_hdl, const char *fmt, ...)
{
SYS_ASSERT(sh_hdl != NULL, "");
if (sh_hdl != NULL && sh_hdl->disable_echo == 0)
{
if (sh_hdl->port.vprint)
{
va_list arg;
va_start(arg, fmt);
int ret = sh_hdl->port.vprint(fmt, arg);
va_end(arg);
return ret;
}
}
return 0;
}
#if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1)
/**
* @brief _strtod()
*
* @param q
* @return const char*
*/
static const char *_skipwhite(const char *q)
{
const char *p = q;
while (*p == ' ')
{
++p;
}
return p;
}
/**
* @brief <stdlib.h> strtod()
*/
static double _strtod(const char *str, char **end)
{
double d = 0.0;
int sign;
int n = 0;
const char *p, *a;
a = p = str;
p = _skipwhite(p);
/* decimal part */
sign = 1;
if (*p == '-')
{
sign = -1;
++p;
}
else if (*p == '+')
{
++p;
}
if (_IS_DIGIT(*p))
{
d = (double)(*p++ - '0');
while (*p && _IS_DIGIT(*p))
{
d = d * 10.0 + (double)(*p - '0');
++p;
++n;
}
a = p;
}
else if (*p != '.')
{
goto done;
}
d *= sign;
/* fraction part */
if (*p == '.')
{
double f = 0.0;
double base = 0.1;
++p;
if (_IS_DIGIT(*p))
{
while (*p && _IS_DIGIT(*p))
{
f += base * (*p - '0');
base /= 10.0;
++p;
++n;
}
}
d += f * sign;
a = p;
}
/* exponential part */
if ((*p == 'E') || (*p == 'e'))
{
int e = 0;
++p;
sign = 1;
if (*p == '-')
{
sign = -1;
++p;
}
else if (*p == '+')
{
++p;
}
if (_IS_DIGIT(*p))
{
while (*p == '0')
{
++p;
}
if (*p == '\0')
{
--p;
}
e = (int)(*p++ - '0');
while (*p && _IS_DIGIT(*p))
{
e = e * 10 + (int)(*p - '0');
++p;
}
e *= sign;
}
else if (!_IS_DIGIT(*(a - 1)))
{
a = str;
goto done;
}
else if (*p == 0)
{
goto done;
}
if (d == 2.2250738585072011 && e == -308)
{
d = 0.0;
a = p;
errno = ERANGE;
goto done;
}
if (d == 2.2250738585072012 && e <= -308)
{
d *= 1.0e-308;
a = p;
goto done;
}
d *= pow(10.0, (double)e);
a = p;
}
else if (p > str && !_IS_DIGIT(*(p - 1)))
{
a = str;
goto done;
}
done:
if (end)
{
*end = (char *)a;
}
return d;
}
#endif /* #if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1) */
/**
* @brief //
*
* @param str
* @return sh_parse_t
*/
sh_parse_t sh_parse_value(const char *str)
{
#define _ATTR_OCT (1 << 0)
#define _ATTR_DEC (1 << 1)
#define _ATTR_HEX (1 << 2)
#define _ATTR_FLOAT (1 << 3)
unsigned attr = 0;
int dot = 0; // '.' 出现次数
sh_parse_t ret = {
.type = _PARSE_TYPE_STRING,
.value.val_string = str,
.endptr = (char *)str,
};
if (str == NULL || *str == '\0')
{
return ret;
}
ret.endptr = &((char *)str)[strlen(str)];
/* 根据首个字符,初步确定属性值为 attr */
do
{
if (*str == '+' || *str == '-')
{
str = &str[1];
}
if (*str == '0')
{
if (str[1] == 'x' || str[1] == 'X')
{
if ((str[2] >= '0' && str[2] <= '9') || (str[2] >= 'a' && str[2] <= 'f') || (str[2] >= 'A' && str[2] <= 'F'))
{
attr = _ATTR_HEX;
str = &str[3];
}
else
{
return ret;
}
}
else
{
attr = _ATTR_OCT | _ATTR_FLOAT;
}
}
else if (*str == '.')
{
attr = _ATTR_FLOAT;
str = &str[1];
dot++;
if (*str == '\0')
{
return ret;
}
}
else if (*str >= '1' && *str <= '9')
{
attr = _ATTR_DEC | _ATTR_FLOAT;
}
} while (0);
/* 按照 attr 给出的可能性,按具体特性单独进行格式检测,若格式正确则进行数值解析并返回 */
do
{
if (attr & _ATTR_OCT) /* 八进制 */
{
while (*str >= '0' && *str <= '7')
{
str = &str[1];
}
if (*str == '\0')
{
if (*ret.value.val_string == '-')
{
ret.type = _PARSE_TYPE_INTEGER;
ret.value.val_integer = strtol(ret.value.val_string, &ret.endptr, 8);
}
else
{
ret.type = _PARSE_TYPE_UNSIGNED;
ret.value.val_unsigned = strtoul(ret.value.val_string, &ret.endptr, 8);
}
return ret;
}
}
if (attr & _ATTR_DEC) /* 十进制 */
{
while (*str >= '0' && *str <= '9')
{
str = &str[1];
}
if (*str == '\0')
{
if (*ret.value.val_string == '-')
{
ret.type = _PARSE_TYPE_INTEGER;
ret.value.val_integer = strtol(ret.value.val_string, &ret.endptr, 10);
}
else
{
ret.type = _PARSE_TYPE_UNSIGNED;
ret.value.val_unsigned = strtoul(ret.value.val_string, &ret.endptr, 10);
}
return ret;
}
}
if (attr & _ATTR_HEX) /* 十六进制 */
{
while ((*str >= '0' && *str <= '9') || (*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F'))
{
str = &str[1];
}
if (*str == '\0')
{
if (*ret.value.val_string == '-')
{
ret.type = _PARSE_TYPE_INTEGER;
ret.value.val_integer = strtol(ret.value.val_string, &ret.endptr, 16);
}
else
{
ret.type = _PARSE_TYPE_UNSIGNED;
ret.value.val_unsigned = strtoul(ret.value.val_string, &ret.endptr, 16);
}
return ret;
}
}
#if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1)
if (attr & _ATTR_FLOAT) /* 浮点数 */
{
int e = 0; // 'e' 出现次数
while (dot < 2 && e < 2)
{
while (*str >= '0' && *str <= '9')
{
str = &str[1];
}
if (*str == '\0')
{
if (attr & _ATTR_OCT)
{
if (dot + e == 0)
{
return ret; /* 如 08、09 */
}
}
ret.type = _PARSE_TYPE_FLOAT;
ret.value.val_float = _strtod(ret.value.val_string, &ret.endptr);
return ret;
}
switch (*str)
{
case '.':
{
dot++;
str = &str[1];
break;
}
case 'e':
{
e++;
str = &str[1];
if (*str == '\0')
{
return ret;
}
else if (*str == '+' || *str == '-')
{
str = &str[1];
if (*str == '\0')
{
return ret;
}
}
break;
}
default:
return ret;
}
}
}
#endif /* #if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1) */
} while (0);
return ret;
}
/**
* @brief
*
* @param dst
* @param len
* @param argc
* @param argv
* @return int
*/
int sh_merge_param(char *dst, int len, int argc, const char *argv[])
{
int ret = 0;
if (len)
{
char *p = dst;
for (int i = 0; i < argc; i++)
{
size_t n = strlen(argv[i]) + 1;
if (n > len)
n = len;
strncpy(p, argv[i], n);
if (i + 1 < argc)
p[n - 1] = ' ';
else
p[n - 1] = '\0';
p = &p[n];
len -= n;
ret += n;
if (len == 0)
{
break;
}
}
--ret;
}
return ret;
}
/**
* @brief
*
* @param sh_hdl sh_init_vt100()
* @retval 1 command not found
* @retval 2 command not complete
* @retval
*/
int sh_get_cmd_result(sh_t *sh_hdl)
{
SYS_ASSERT(sh_hdl != NULL, "");
return sh_hdl->cmd_return;
}
/**
* @brief
* sh_port_t::clear_line
*/
void sh_refresh_line(sh_t *sh_hdl)
{
SYS_ASSERT(sh_hdl != NULL, "");
if (sh_hdl->port.clear_line)
{
uint16_t input_pos = sh_hdl->cmd_input;
sh_hdl->port.clear_line(sh_hdl);
sh_ctrl_print_cmd_line(sh_hdl);
sh_ctrl_set_input_pos(sh_hdl, input_pos);
}
}
/**
* @brief
*
* @param cmd
* @return sh_cp_info_t
*/
static sh_cp_info_t _completion_init(const char *cmd)
{
sh_cp_info_t info = {
.arg_str = cmd, // 待分析的参数
.arg_len = strlen(cmd), // 待解析命令长度
.match_num = 0, // 可打印的列表的条目数
.list_algin = 0, // 确定格式中 %-*s 的 * 的值
};
info.match_str[0] = '\0'; // 保存相同的字符部分
return info;
}
/**
* @brief
*
* @param info[out] _completion_init()
* @param sub_cmd
* @param print_match false -- info; true --
* @param max_lines print_match true
* @param flag_parent select 1 使
*/
static void _completion_update(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cmd_t *sub_cmd, bool print_match, int max_lines, int flag_parent)
{
int list_algin = 100 / _calc_list_algin(info);
if ((sub_cmd->cmd_func != NULL || sub_cmd->sub_fn.sub_cmd != NULL) &&
(info->arg_len == 0 || strncmp(sub_cmd->cmd, info->arg_str, info->arg_len) == 0))
{
if (flag_parent && sub_cmd->cmd_func) // 只按父命令更新
{
return;
}
if (print_match == false) // 更新补全信息
{
int test_len = strlen(sub_cmd->cmd);
if (info->list_algin < test_len)
info->list_algin = test_len;
if (info->match_num)
{
for (int i = 0; info->match_str[i] != '\0'; i++)
{
if (info->match_str[i] != sub_cmd->cmd[i])
{
info->match_str[i] = '\0';
break;
}
}
}
else
{
strcpy(info->match_str, sub_cmd->cmd);
}
info->match_num++;
}
else // 打印匹配的命令
{
info->print_count++;
const char *color_begin = "";
const char *color_end = "";
if (sub_cmd->cmd_func)
{
if (sub_cmd->sub_fn.cp_fn) // 有参数
{
color_begin = _COLOR_C;
color_end = _COLOR_END;
}
}
else // 有子命令
{
color_begin = _COLOR_P;
color_end = _COLOR_END;
}
sh_echo(sh_hdl, "%s%-*s%s", color_begin, _calc_list_algin(info), sub_cmd->cmd, color_end);
if (info->match_num > max_lines) // 超过 max_lines 个匹配项时,不再显示帮助信息
{
if (info->print_count >= info->match_num || info->print_count % list_algin == 0)
{
sh_echo(sh_hdl, "\r\n");
}
}
else
{
if (sub_cmd->help && *sub_cmd->help != '\0')
{
sh_echo(sh_hdl, "-- %s\r\n", sub_cmd->help);
}
else
{
sh_echo(sh_hdl, "\r\n");
}
}
}
}
}
/**
* @brief
*
* @param info[out] _completion_init()
* @param cmd_info
* @param print_match false -- ; true --
* @param flag_parent select 1 使
* @return true print_match false
* @return false print_match true
*/
static bool _do_completion_cmd(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cmd_info_t *cmd_info, bool print_match, int flag_parent)
{
char new_cmd[sizeof(sh_hdl->cmd_line)];
const sh_cmd_t *sub_cmd;
int err_status;
if (print_match)
{
if (info->match_num == 0)
{
return false;
}
if (sh_hdl->cp_operate != SH_CP_OP_PEND)
{
sh_hdl->cp_operate = SH_CP_OP_PEND;
sh_echo(sh_hdl, "\r\n");
}
info->print_count = 0;
}
else
{
info->match_num = 0;
}
if (cmd_info)
{
sub_cmd = cmd_info->match_cmd;
err_status = cmd_info->err_status;
}
else
{
sub_cmd = sh_hdl->select_cmd;
err_status = !_SH_CMD_STATUS_Incomplete;
}
if (sub_cmd == NULL)
{
sub_cmd = sh_hdl->select_cmd;
}
while (1)
{
2025-02-13 17:17:07 +08:00
if (sub_cmd == NULL) // 遍历根命令链
2024-03-28 12:19:52 +08:00
{
const sh_cmd_reg_t *sh_reg;
sys_pslist_t *cmd_list[] = {
&s_cmd_list,
&sh_hdl->cmd_list,
};
for (int i = 0; i < sizeof(cmd_list) / sizeof(cmd_list[0]); i++)
{
sys_pslist_t *test_list = cmd_list[i];
SYS_PSLIST_FOR_EACH_CONTAINER(test_list, sh_reg, node)
{
const sh_cmd_t *reg_cmd = sh_reg->cmd;
for (int i = 0; *(int *)&reg_cmd[i] != 0; i++)
{
sh_cmd_t new_data = reg_cmd[i];
new_data.cmd = new_cmd;
strcpy(new_cmd, reg_cmd[i].cmd);
strcat(new_cmd, " ");
_completion_update(sh_hdl, info, &new_data, print_match, 30, flag_parent);
}
}
}
}
2025-02-13 17:17:07 +08:00
else // 遍历子命令数据
2024-03-28 12:19:52 +08:00
{
sub_cmd = sub_cmd->sub_fn.sub_cmd;
for (int i = 0; sub_cmd[i].cmd != NULL; i++)
{
sh_cmd_t new_data = sub_cmd[i];
new_data.cmd = new_cmd;
strcpy(new_cmd, sub_cmd[i].cmd);
strcat(new_cmd, " ");
_completion_update(sh_hdl, info, &new_data, print_match, 30, flag_parent);
}
if (sh_hdl->select_cmd != NULL && // 当前状态为模块模式
info->print_count == 0 && // 没有打印任何可选项
err_status != _SH_CMD_STATUS_Incomplete && // 如果正在输出当前模块下的子命令,则只继续补全子命令
print_match == true && // 只打印匹配的备选项
2025-02-13 17:17:07 +08:00
flag_parent == 0 // 目前仅在 _cmd_select_param() 被置起,此时不再遍历根命令链
2024-03-28 12:19:52 +08:00
)
{
sub_cmd = NULL; // 使从根命令链重新查找
continue;
}
}
if (sh_hdl->select_cmd != NULL && // 当前状态为模块模式
info->match_num == 0 && // 没有找到可匹配的命令
err_status != _SH_CMD_STATUS_Incomplete && // 如果正在输出当前模块下的子命令,则只继续补全子命令
2025-02-13 17:17:07 +08:00
sub_cmd != NULL // 未遍历过根命令链
2024-03-28 12:19:52 +08:00
)
{
sub_cmd = NULL; // 使从根命令链重新查找
}
else
{
break;
}
}
if (print_match)
{
return false;
}
return _do_completion_insert(sh_hdl, info);
}
/**
* @brief
*
* @param info[out] _completion_init()
* @param sh_param NULL
* @param print_match false -- ; true --
* @return true print_match false
* @return false print_match true
*/
static bool _do_completion_param(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cp_param_t *sh_param, bool print_match)
{
if (print_match)
{
if (info->match_num == 0)
{
return false;
}
if (sh_hdl->cp_operate != SH_CP_OP_PEND)
{
sh_echo(sh_hdl, "\r\n");
sh_hdl->cp_operate = SH_CP_OP_PEND;
}
info->print_count = 0;
}
else
{
info->match_num = 0;
}
for (int i = 0; sh_param[i].cmd != NULL; i++)
{
char new_cmd[sizeof(sh_hdl->cmd_line)];
sh_cmd_t sub_cmd = {
.cmd = new_cmd,
.help = sh_param[i].help,
.cmd_func = (__typeof(sub_cmd.cmd_func))1,
.sub_fn = {.sub_cmd = NULL},
};
strcpy(new_cmd, sh_param[i].cmd);
strcat(new_cmd, " ");
_completion_update(sh_hdl, info, &sub_cmd, print_match, 30, 0);
}
if (print_match)
{
return false;
}
return _do_completion_insert(sh_hdl, info);
}
/**
* @brief
*
* @param info[out] _completion_init()
* @return true
* @return false
*/
static bool _do_completion_insert(sh_t *sh_hdl, sh_cp_info_t *info)
{
/* 执行自动补全,返回:显示是否被自动更新 */
if (info->match_str[info->arg_len] == '\0')
{
return false;
}
else
{
_insert_str(sh_hdl, &info->match_str[info->arg_len]);
sh_hdl->cp_operate = SH_CP_OP_CP;
return true;
}
}
/**
* @brief
*/
static void _do_restore_line(sh_t *sh_hdl)
{
if (sh_hdl->cp_operate == SH_CP_OP_PEND)
{
uint16_t cmd_input = sh_hdl->cmd_input;
sh_ctrl_print_cmd_line(sh_hdl);
sh_ctrl_set_input_pos(sh_hdl, cmd_input);
sh_hdl->cp_operate = SH_CP_OP_LIST;
}
}
/**
* @brief
*
* @param info[out] _completion_update()
* @return int
*/
static int _calc_list_algin(sh_cp_info_t *info)
{
return (info->list_algin > 8 ? info->list_algin : 8) + 2;
}
/**
* @brief
*
* @param argc
* @param argv
* @return sh_cmd_info_t
*/
2025-02-13 17:17:07 +08:00
sh_cmd_info_t sh_cmd_test(sh_t *sh_hdl, int argc, const char *argv[])
2024-03-28 12:19:52 +08:00
{
SYS_ASSERT(sh_hdl != NULL, "");
unsigned cmd_sections = 0;
sh_cmd_info_t info;
info.err_status = _SH_CMD_STATUS_Bad;
info.match_count = 0;
info.reg_struct = NULL;
info.match_cmd = NULL;
for (cmd_sections = 0; cmd_sections < argc; cmd_sections++) // 依次查找全部子命令
{
info.match_cmd = _find_cmd(sh_hdl, &info.reg_struct, info.match_cmd, argv[cmd_sections]);
if (info.match_cmd == NULL)
{
info.err_status = _SH_CMD_STATUS_Bad;
info.match_count = cmd_sections;
return info;
}
if (info.match_cmd->cmd_func) // 找到子命令
{
info.err_status = _SH_CMD_STATUS_Success;
info.match_count = cmd_sections + 1;
return info;
}
if (cmd_sections + 1 < argc)
{
info.match_cmd = info.match_cmd->sub_fn.sub_cmd;
}
}
if (info.match_cmd && info.match_cmd->sub_fn.sub_cmd && cmd_sections == argc)
{
info.err_status = _SH_CMD_STATUS_Incomplete;
info.match_count = cmd_sections;
return info;
}
return info;
}
/**
* @brief /
*
* @param argc
* @param argv
* @param flag_parent select 1 使
* @return sh_cp_op_t /
*/
static sh_cp_op_t _sh_completion_cmd(sh_t *sh_hdl, int argc, const char *argv[], int flag_parent)
{
sh_cmd_info_t cmd_info;
sh_cp_info_t cp_info;
if (sh_hdl->exec_flag == 0)
{
sh_hdl->cp_operate = SH_CP_OP_NA;
}
if (argc)
{
/* 初始化 cmd_info 和 cp_info */
if (_arg_is_end(sh_hdl))
{
2025-02-13 17:17:07 +08:00
cmd_info = sh_cmd_test(sh_hdl, argc, argv);
2024-03-28 12:19:52 +08:00
cp_info = _completion_init("");
}
else
{
2025-02-13 17:17:07 +08:00
cmd_info = sh_cmd_test(sh_hdl, --argc, argv);
2024-03-28 12:19:52 +08:00
if (cmd_info.err_status == _SH_CMD_STATUS_Success)
{
cp_info = _completion_init(argv[argc++]);
}
else
{
cp_info = _completion_init(argv[cmd_info.match_count]);
}
}
if (cmd_info.err_status == _SH_CMD_STATUS_Success)
{
if (cmd_info.match_cmd->sub_fn.cp_fn != NULL)
{
do
{
if (sh_hdl->cp_resource_flag) // 仅作用于使用了 sh_completion_resource() 的情况
{
if (sh_hdl->tab_press_count)
{
cp_info.match_num = sh_hdl->cp_match_num;
cp_info.list_algin = sh_hdl->cp_list_algin;
if (cp_info.match_num < 2)
{
break;
}
if (sh_hdl->cp_operate != SH_CP_OP_PEND)
{
sh_hdl->cp_operate = SH_CP_OP_PEND;
sh_echo(sh_hdl, "\r\n");
}
}
}
++sh_hdl->exec_flag;
sh_hdl->cp_info = &cp_info;
_clear_argv(argc, argv);
cmd_info.match_cmd->sub_fn.cp_fn(sh_hdl, argc - cmd_info.match_count, &argv[cmd_info.match_count], _arg_is_end(sh_hdl));
sh_hdl->cp_info = NULL;
--sh_hdl->exec_flag;
if (sh_hdl->cp_resource_flag) // 仅作用于使用了 sh_completion_resource() 的情况
{
int cp_resource_flag = sh_hdl->cp_resource_flag;
if (sh_hdl->tab_press_count == 0)
{
if (cp_info.match_num)
{
sh_hdl->tab_press_count = 1;
sh_hdl->cp_match_num = cp_info.match_num;
sh_hdl->cp_list_algin = cp_info.list_algin;
_do_completion_insert(sh_hdl, &cp_info);
}
}
else
{
/* 恢复当前命令行显示 */
_do_restore_line(sh_hdl);
}
sh_hdl->cp_resource_flag = cp_resource_flag;
}
} while (0);
}
return sh_hdl->cp_operate; // 包含完整的命令
}
if (cmd_info.match_count != argc)
{
return sh_hdl->cp_operate; // 中间的命令错误
}
}
else
{
2025-02-13 17:17:07 +08:00
cmd_info = sh_cmd_test(sh_hdl, argc, argv);
2024-03-28 12:19:52 +08:00
cp_info = _completion_init(""); // 没有任何命令时直接打印所有根命令
}
do
{
if (_do_completion_cmd(sh_hdl, &cp_info, &cmd_info, false, flag_parent))
{
break; // 因为命令行被自动更新,不打印可用命令
}
if (cp_info.match_num == 0)
{
break; // 没有匹配的命令
}
if (sh_hdl->tab_press_count == 0)
{
sh_hdl->tab_press_count = 1;
break; // 暂停一次操作
}
/* 打印命令 */
_do_completion_cmd(sh_hdl, &cp_info, &cmd_info, true, flag_parent);
/* 恢复当前命令行显示 */
_do_restore_line(sh_hdl);
} while (0);
return sh_hdl->cp_operate;
}
/**
* @brief cp_fn() /
*
* @param param NULL NULL
* @return sh_cp_op_t /
*/
sh_cp_op_t sh_completion_param(sh_t *sh_hdl, const sh_cp_param_t *param)
{
SYS_ASSERT(sh_hdl != NULL, "");
if (sh_hdl->exec_flag == 0)
{
sh_hdl->cp_operate = SH_CP_OP_NA;
}
do
{
sh_hdl->cp_resource_flag = 0;
if (sh_hdl->cp_info == NULL)
{
if (sh_hdl->disable_echo == 0)
SYS_LOG_WRN("This function is only used for functions with automatic parameter completion. @ref SH_SETUP_CMD\r\n");
break;
}
if (param == 0)
{
break;
}
if (_do_completion_param(sh_hdl, sh_hdl->cp_info, param, false))
{
break; // 因为命令行被自动更新,不打印可用命令
}
if (sh_hdl->cp_info->match_num == 0)
{
break; // 没有匹配的命令
}
if (sh_hdl->tab_press_count == 0)
{
sh_hdl->tab_press_count = 1;
break; // 暂停一次操作
}
/* 打印参数 */
_do_completion_param(sh_hdl, sh_hdl->cp_info, param, true);
/* 恢复当前命令行显示 */
_do_restore_line(sh_hdl);
} while (0);
return sh_hdl->cp_operate;
}
/**
* @brief cp_fn() /
* / cp_fn() 退
* @ref @c fatfs_shell.c@_scan_files()/@_cmd_df_cp()
*
2025-02-13 17:17:07 +08:00
* @param arg_str NULL时表示按当前命令行自动填充
* 使 arg_str 便
2024-03-28 12:19:52 +08:00
* @param res_str
* @param help NULL
*/
void sh_completion_resource(sh_t *sh_hdl, const char *arg_str, const char *res_str, const char *help)
{
SYS_ASSERT(sh_hdl != NULL, "");
if (sh_hdl->cp_info == NULL)
{
if (sh_hdl->cp_resource_flag == 0)
{
sh_hdl->cp_resource_flag = 1;
if (sh_hdl->disable_echo == 0)
SYS_LOG_WRN("This function is only used for functions with automatic parameter completion. @ref SH_SETUP_CMD\r\n");
}
return;
}
sh_hdl->cp_resource_flag = 1; // 标记执行了本函数
if (res_str == NULL || *res_str == '\0')
{
return;
}
if (arg_str)
{
sh_hdl->cp_info->arg_str = arg_str;
sh_hdl->cp_info->arg_len = strlen(arg_str);
}
sh_cmd_t sub_cmd = {
.cmd = res_str,
.help = help,
.cmd_func = (__typeof(sub_cmd.cmd_func))1,
.sub_fn = {.sub_cmd = NULL},
};
int max_lines = 0;
if (help != NULL && *help != '\0')
{
max_lines = ~(1u << (sizeof(max_lines) * 8 - 1));
}
_completion_update(sh_hdl, sh_hdl->cp_info, &sub_cmd, (bool)!!sh_hdl->tab_press_count, max_lines, 0);
}
/**
* @brief
*
* @return true
* @return false
*/
static bool _arg_is_end(sh_t *sh_hdl)
{
return (bool)(sh_ctrl_get_line(sh_hdl)[sh_hdl->cmd_input - 1] == ' ');
}
/**
* @brief
*
*/
static void _update_cursor(sh_t *sh_hdl)
{
sh_hdl->tab_press_count = 0;
sh_hdl->cp_resource_flag = 0;
if (sh_hdl->port.set_cursor_hoffset)
{
if (sh_hdl->cmd_input != sh_hdl->sync_cursor)
{
int offset = strlen(sh_hdl->prompt) + (sh_hdl->cmd_buf - sh_hdl->cmd_line); // 输入命令的起始绝对位置
int last_pos = sh_hdl->sync_cursor + offset;
int new_pos = sh_hdl->cmd_input + offset;
sh_hdl->port.set_cursor_hoffset(sh_hdl, last_pos, new_pos);
}
}
sh_hdl->sync_cursor = sh_hdl->cmd_input;
}
/**
* @brief sh_completion_resource() 使 cp_fn()
*
* @return sh_cp_op_t
*/
sh_cp_op_t sh_get_cp_result(sh_t *sh_hdl)
{
SYS_ASSERT(sh_hdl != NULL, "");
return sh_hdl->cp_operate;
}
/**
* @brief /
*
*/
void sh_ctrl_tab(sh_t *sh_hdl)
{
int argc;
const char *argv[CONFIG_SH_MAX_PARAM];
char cmd_param[sizeof(sh_hdl->cmd_line)];
/* 复制当前显示的命令到 cmd_param ,并在光标处截断 */
strcpy(cmd_param, sh_ctrl_get_line(sh_hdl));
cmd_param[sh_hdl->cmd_input] = '\0';
/* 解析参数并输出 argc, argv, cmd_param */
_read_param(&argc, argv, cmd_param, cmd_param);
/* 自动补全命令 */
2025-02-13 17:17:07 +08:00
_sh_completion_cmd(sh_hdl, argc, argv, 0);
2024-03-28 12:19:52 +08:00
}
/**
* @brief
*
* @return true
* @return false
*/
bool sh_ctrl_enter(sh_t *sh_hdl)
{
int argc;
const char *argv[CONFIG_SH_MAX_PARAM];
char cmd_param[sizeof(sh_hdl->cmd_line)];
bool ret = false;
const char *cmd_line = sh_ctrl_get_line(sh_hdl);
++sh_hdl->exec_flag;
/* 解析参数并输出 argc, argv, cmd_param */
_read_param(&argc, argv, cmd_param, cmd_line);
if (argc)
{
/* 将当前回翻的记录复制到缓存中 */
_apply_history(sh_hdl);
/* 根据 argc, argv解析出 info */
2025-02-13 17:17:07 +08:00
sh_cmd_info_t info = sh_cmd_test(sh_hdl, argc, argv);
2024-03-28 12:19:52 +08:00
if (info.err_status == _SH_CMD_STATUS_Success)
{
_clear_argv(argc, argv);
sh_hdl->cmd_return = info.match_cmd->cmd_func(sh_hdl, argc - info.match_count, &argv[info.match_count]);
ret = true;
}
else
{
for (int i = 0; i < argc; i++)
{
sh_echo(sh_hdl, "%s%s", argv[i], i + 1 < argc ? " " : "");
}
if (info.err_status == _SH_CMD_STATUS_Incomplete)
{
sh_echo(sh_hdl, ": command not complete\r\n");
sh_hdl->cmd_return = 2;
}
else
{
sh_echo(sh_hdl, ": command not found\r\n");
sh_hdl->cmd_return = 1;
}
ret = false;
}
/* 去除重复的空格,但保持引号内的空格 */
int index_src = 0;
int index_dst = 0;
char split_char = ' ';
while (cmd_line[index_src] != '\0' && cmd_line[index_src] == ' ')
{
index_src++;
}
while (cmd_line[index_src] != '\0')
{
char c = cmd_line[index_src++];
if (split_char == ' ')
{
if (c == '\'' || c == '"')
{
split_char = c;
}
}
else if (c == split_char)
{
split_char = ' ';
}
if (split_char == ' ' && c == ' ')
{
while (cmd_line[index_src] != '\0' && cmd_line[index_src] == ' ')
{
index_src++;
}
if (cmd_line[index_src] == '\0')
{
break;
}
}
cmd_param[index_dst++] = c;
}
cmd_param[index_dst] = '\0';
/* 尝试保存到历史记录中 */
_store_history(sh_hdl, cmd_param);
}
else
{
sh_hdl->tab_press_count = 0;
sh_hdl->cp_resource_flag = 0;
}
sh_reset_line(sh_hdl);
--sh_hdl->exec_flag;
return ret;
}
/**
* @brief n
*
* @param n
* @return true
* @return false
*/
bool sh_ctrl_delete(sh_t *sh_hdl, int n)
{
if (n == 0)
{
return true;
}
_apply_history(sh_hdl);
if (sh_hdl->cmd_input + n <= sh_hdl->cmd_stored)
{
for (int i = sh_hdl->cmd_input; i + n < sh_hdl->cmd_stored; i++) /* 使光标之后的数据往前移动一个字节 */
sh_hdl->cmd_buf[i] = sh_hdl->cmd_buf[i + n];
sh_hdl->cmd_stored -= n;
sh_hdl->cmd_buf[sh_hdl->cmd_stored] = '\0';
return true;
}
else
{
return false;
}
}
/**
* @brief n
*
* @param n
* @return true
* @return false
*/
bool sh_ctrl_backspace(sh_t *sh_hdl, int n)
{
if (n == 0)
{
return true;
}
_apply_history(sh_hdl);
if (sh_hdl->cmd_input >= n)
{
sh_hdl->cmd_input -= n;
sh_hdl->sync_cursor -= n;
return sh_ctrl_delete(sh_hdl, n);
}
else
{
return false;
}
}
/**
* @brief
*
* @return true
* @return false
*/
bool sh_ctrl_up(sh_t *sh_hdl)
{
if (sh_hdl->history_show < sh_hdl->history_valid_num)
{
++sh_hdl->history_show;
sh_ctrl_end(sh_hdl);
return true;
}
else
{
return false;
}
}
/**
* @brief
*
* @return true
* @return false
*/
bool sh_ctrl_down(sh_t *sh_hdl)
{
if (sh_hdl->history_show > 0)
{
--sh_hdl->history_show;
sh_ctrl_end(sh_hdl);
return true;
}
else
{
return false;
}
}
/**
* @brief
*
*/
void sh_ctrl_left(sh_t *sh_hdl)
{
if (sh_hdl->cmd_input > 0)
--sh_hdl->cmd_input;
_update_cursor(sh_hdl);
}
/**
* @brief
*
*/
void sh_ctrl_right(sh_t *sh_hdl)
{
if (sh_hdl->cmd_input < _get_end_pos(sh_hdl))
++sh_hdl->cmd_input;
_update_cursor(sh_hdl);
}
/**
* @brief
*
*/
void sh_ctrl_home(sh_t *sh_hdl)
{
sh_hdl->cmd_input = 0;
_update_cursor(sh_hdl);
}
/**
* @brief
*
*/
void sh_ctrl_end(sh_t *sh_hdl)
{
sh_hdl->cmd_input = _get_end_pos(sh_hdl);
_update_cursor(sh_hdl);
}
/**
* @brief 0
*
* @param pos 0.._get_end_pos(sh_hdl)
*/
void sh_ctrl_set_cursor(sh_t *sh_hdl, int pos)
{
if (pos < 0)
{
sh_hdl->cmd_input = 0;
}
else if (pos > _get_end_pos(sh_hdl))
{
sh_hdl->cmd_input = _get_end_pos(sh_hdl);
}
else
{
sh_hdl->cmd_input = pos;
}
_update_cursor(sh_hdl);
}
/**
* @brief
*
*/
void sh_ctrl_word_head(sh_t *sh_hdl)
{
const char *line = sh_ctrl_get_line(sh_hdl);
int pos = sh_hdl->cmd_input;
while (pos)
{
if (_IS_ALPHA(line[pos - 1]) || _IS_DIGIT(line[pos - 1]))
{
break;
}
--pos;
}
while (pos)
{
if (!_IS_ALPHA(line[pos - 1]) && !_IS_DIGIT(line[pos - 1]))
{
break;
}
--pos;
}
sh_ctrl_set_cursor(sh_hdl, pos);
}
/**
* @brief
*
*/
void sh_ctrl_word_tail(sh_t *sh_hdl)
{
const char *line = sh_ctrl_get_line(sh_hdl);
int pos = sh_hdl->cmd_input;
while (line[pos] != '\0')
{
if (_IS_ALPHA(line[pos]) || _IS_DIGIT(line[pos]))
{
break;
}
++pos;
}
while (line[pos] != '\0')
{
if (!_IS_ALPHA(line[pos]) && !_IS_DIGIT(line[pos]))
{
break;
}
++pos;
}
sh_ctrl_set_cursor(sh_hdl, pos);
}
/**
* @brief
*
*/
void sh_ctrl_print_cmd_line(sh_t *sh_hdl)
{
sh_echo(sh_hdl, _COLOR_G);
sh_echo(sh_hdl, sh_ctrl_get_prompt(sh_hdl));
if (sh_hdl->select_cmd)
{
sh_echo(sh_hdl, _COLOR_P);
sh_echo(sh_hdl, "%s: ", sh_hdl->cmd_line);
}
sh_echo(sh_hdl, _COLOR_END);
const char *line = sh_ctrl_get_line(sh_hdl);
sh_echo(sh_hdl, line);
sh_hdl->cmd_input = strlen(line);
sh_hdl->sync_cursor = sh_hdl->cmd_input;
_update_cursor(sh_hdl);
}
/**
* @brief
*
* @return unsigned
*/
unsigned sh_ctrl_del_word(sh_t *sh_hdl)
{
const char *line = sh_ctrl_get_line(sh_hdl);
int pos = sh_hdl->cmd_input;
int n = 0;
while (pos)
{
if (_IS_ALPHA(line[pos - 1]) || _IS_DIGIT(line[pos - 1]))
{
break;
}
--pos;
++n;
}
while (pos)
{
if (!_IS_ALPHA(line[pos - 1]) && !_IS_DIGIT(line[pos - 1]))
{
break;
}
--pos;
++n;
}
_store_cmd_bak(sh_hdl, line, pos, n);
sh_ctrl_backspace(sh_hdl, n);
return n;
}
/**
* @brief
*
* @return unsigned
*/
unsigned sh_ctrl_del_left(sh_t *sh_hdl)
{
const char *line = sh_ctrl_get_line(sh_hdl);
int pos = sh_hdl->cmd_input;
_store_cmd_bak(sh_hdl, line, 0, pos);
sh_ctrl_backspace(sh_hdl, pos);
return pos;
}
/**
* @brief
*
* @return unsigned
*/
unsigned sh_ctrl_del_right(sh_t *sh_hdl)
{
const char *line = sh_ctrl_get_line(sh_hdl);
int pos = sh_hdl->cmd_input;
int n = strlen(line) - pos;
_store_cmd_bak(sh_hdl, line, pos, n);
sh_ctrl_delete(sh_hdl, n);
return n;
}
/**
* @brief sh_ctrl_del_word(), sh_ctrl_del_left(), sh_ctrl_del_right()
*
* @return true
* @return false
*/
bool sh_ctrl_undelete(sh_t *sh_hdl)
{
if (sh_hdl->cmd_bank && sh_hdl->bank_size)
{
if (sh_hdl->cmd_bank[0])
{
_insert_str(sh_hdl, sh_hdl->cmd_bank);
return true;
}
}
return false;
}
/**
* @brief 使 select
*
* @return true
* @return false
*/
bool sh_ctrl_is_module(sh_t *sh_hdl)
{
return (bool)(!!sh_hdl->select_cmd);
}
/**
* @brief
*
* @param input_pos 0.._get_end_pos(sh_hdl)
*/
void sh_ctrl_set_input_pos(sh_t *sh_hdl, uint16_t input_pos)
{
if (input_pos > _get_end_pos(sh_hdl))
input_pos = _get_end_pos(sh_hdl);
sh_hdl->cmd_input = input_pos;
_update_cursor(sh_hdl);
}
/**
* @brief 0
*
* @param input_pos 0.._get_end_pos(sh_hdl)
*/
uint16_t sh_ctrl_get_input_pos(sh_t *sh_hdl)
{
return sh_hdl->cmd_input;
}
/**
* @brief ++
*
* @return uint16_t ++
*/
uint16_t sh_ctrl_get_line_pos(sh_t *sh_hdl)
{
return strlen(sh_hdl->prompt) +
(sh_hdl->cmd_buf - sh_hdl->cmd_line) +
sh_hdl->cmd_input;
}
/**
* @brief
*
*
* @return uint16_t
*/
uint16_t sh_ctrl_get_line_len(sh_t *sh_hdl)
{
return strlen(sh_hdl->prompt) +
(sh_hdl->cmd_buf - sh_hdl->cmd_line) +
_get_end_pos(sh_hdl);
}
/**
* @brief ++
*
* @param pos 0.._get_end_pos(sh_hdl)
* @return char
*/
char sh_ctrl_get_line_char(sh_t *sh_hdl, uint16_t pos)
{
int test_len;
test_len = strlen(sh_hdl->prompt);
if (pos < test_len)
{
return sh_hdl->prompt[pos];
}
pos -= test_len;
test_len = sh_hdl->cmd_buf - sh_hdl->cmd_line;
if (pos < test_len)
{
return sh_hdl->cmd_line[pos];
}
pos -= test_len;
test_len = _get_end_pos(sh_hdl);
if (pos < test_len)
{
if (sh_hdl->history_show)
return sh_hdl->cmd_history[sh_hdl->history_index[sh_hdl->history_show - 1] + pos];
else
return sh_hdl->cmd_buf[pos];
}
return '\0';
}
/* 内部默认命令 -------------------------------------------------------------------------------------------- */
#if defined(MIX_SHELL)
SH_CMD_FN(_cmd_print_init_fn)
{
extern sys_init_t __sys_init_leader_0;
extern sys_init_t __sys_init_leader_e;
sys_init_t *start = &__sys_init_leader_0;
sys_init_t *end = &__sys_init_leader_e;
int match_count = 0;
sh_echo(sh_hdl, "Automatic initialization list >>>\r\n");
while (start < end)
{
if (start->level + start->prior != 0 &&
start->level * 100 + start->prior != 100 &&
start->level * 100 + start->prior != 999)
{
const char *lev2char[] = {
"BOARD",
"PREV",
"DEVICE",
"COMPONENT",
"ENV",
"APP",
};
char buf[0x200];
SYS_SNPRINT(buf, sizeof(buf), "%-10s %d-%-2d %s:%d @%s",
start->level < sizeof(lev2char) / sizeof(lev2char[0]) ? lev2char[start->level] : "MISC",
start->level,
start->prior,
_FILENAME(start->file),
start->line,
start->fn_name);
int result = !argc;
for (int i = 0; i < argc; i++)
{
if (strstr(buf, argv[i]))
{
result = 1;
break;
}
}
if (result)
{
sh_echo(sh_hdl, "%s\r\n", buf);
match_count++;
}
}
start = &start[1];
}
sh_echo(sh_hdl, "Total: %d\r\n", match_count);
return 0;
}
SH_CMD_FN(_cmd_print_module_struct)
{
extern const module_t __sys_module_leader_0;
extern const module_t __sys_module_leader_9;
const module_t *start = &__sys_module_leader_0;
const module_t *end = &__sys_module_leader_9;
int match_count = 0;
sh_echo(sh_hdl, "Internal module cmd list >>>\r\n");
const struct
{
module_type_t type;
const char *name;
} type2char[] = {
{MODULE_TYPE_DEVICE, "DEVICE"},
};
for (int n = 0; n < sizeof(type2char) / sizeof(type2char[0]); n++)
{
while (start < end)
{
if (start->type == type2char[n].type)
{
char buf[0x200];
SYS_SNPRINT(buf, sizeof(buf), "%p %-10s %-10s %s:%d",
start->obj,
type2char[n].name,
start->name,
_FILENAME(start->file),
start->line);
int result = !argc;
for (int i = 0; i < argc; i++)
{
if (strstr(buf, argv[i]))
{
result = 1;
break;
}
}
if (result)
{
sh_echo(sh_hdl, "%s\r\n", buf);
match_count++;
}
}
start = &start[1];
}
}
sh_echo(sh_hdl, "Total: %d\r\n", match_count);
return 0;
}
#endif /* #if defined(MIX_SHELL) */
SH_CMD_FN(_cmd_history)
{
if (argc == 0)
{
for (int i = sh_hdl->history_valid_num; i--;)
{
sh_echo(sh_hdl, "%s\r\n", &sh_hdl->cmd_history[sh_hdl->history_index[i]]);
}
}
else
{
if (strcmp(argv[0], "enable") == 0)
{
sh_hdl->disable_history = false;
}
else if (strcmp(argv[0], "disable") == 0)
{
sh_hdl->disable_history = true;
}
else
{
sh_echo(sh_hdl, "%s: parameter error\r\n", argv[0]);
}
}
return 0;
}
SH_CMD_FN(_cmd_select)
{
if (argc)
{
2025-02-13 17:17:07 +08:00
sh_cmd_info_t cmd_info = sh_cmd_test(sh_hdl, argc, argv);
2024-03-28 12:19:52 +08:00
if (cmd_info.err_status == _SH_CMD_STATUS_Incomplete)
{
sh_hdl->select_reg_struct = cmd_info.reg_struct;
sh_hdl->select_cmd = cmd_info.match_cmd;
/* 把 cmd_param 中分散的参数以空格分隔,合并成一字符串并保存 */
char *p = sh_hdl->cmd_line;
for (int i = 0; i < argc; i++)
{
for (int n = 0; argv[i][n] != '\0'; n++)
{
*p++ = argv[i][n];
}
if (i + 1 < argc)
{
*p++ = ' ';
}
else
{
*p++ = '\0';
}
}
sh_hdl->cmd_buf = p;
return 0;
}
else
{
sh_echo(sh_hdl, "%s: not a parent command\r\n", argv[0]);
}
}
else
{
if (sh_hdl->select_cmd)
{
sh_hdl->select_cmd = NULL;
sh_hdl->cmd_buf = sh_hdl->cmd_line;
return 0;
}
}
return -1;
}
SH_CMD_CP_FN(_cmd_select_param)
{
_sh_completion_cmd(sh_hdl, argc, argv, 1);
}
SH_CMD_CP_FN(_cmd_history_param)
{
sh_completion_resource(sh_hdl, NULL, "enable", NULL);
sh_completion_resource(sh_hdl, NULL, "disable", NULL);
}
SH_CMD_FN(_cmd_version)
{
sh_echo(sh_hdl, "sh v" SH_VERSION " build %04d-%02d-%02d\r\n", _YEAR, _MONTH, _DAY);
return 0;
}
SH_CMD_FN(_cmd_exit)
{
if (sh_hdl->select_cmd)
{
sh_hdl->select_cmd = NULL;
sh_hdl->cmd_buf = sh_hdl->cmd_line;
return 0;
}
else if (sh_hdl->port.disconnect)
{
sh_hdl->port.disconnect();
return 0;
}
else
{
extern void drv_hal_sys_exit(void);
drv_hal_sys_exit();
return -1;
}
}
SH_CMD_FN(_cmd_print_help)
{
2025-02-13 17:17:07 +08:00
sh_cmd_info_t info = sh_cmd_test(sh_hdl, argc, argv);
2024-03-28 12:19:52 +08:00
if (argc == 0)
{
sh_cp_info_t cp_info = _completion_init("");
sh_echo(sh_hdl, "Command list >>>");
cp_info.match_num = 1;
_do_completion_cmd(sh_hdl, &cp_info, NULL, false, 0);
_do_completion_cmd(sh_hdl, &cp_info, NULL, true, 0);
return 0;
}
if (info.err_status == _SH_CMD_STATUS_Bad)
{
sh_echo(sh_hdl, "sh: help: not found Command '");
for (int i = 0; i < argc; i++)
{
sh_echo(sh_hdl, "%s%s", argv[i], i + 1 < argc ? " " : "'\r\n");
}
return 0;
}
int total_len = 0;
for (int i = 0; i < info.match_count; i++)
{
total_len += strlen(argv[i]) + 1;
}
total_len += 3;
for (int i = 1; i <= info.match_count; i++)
{
2025-02-13 17:17:07 +08:00
sh_cmd_info_t tmp = sh_cmd_test(sh_hdl, i, argv);
2024-03-28 12:19:52 +08:00
if (i == 1)
{
sh_echo(sh_hdl, "build in %s:%d\r\n", _FILENAME(info.reg_struct->file), info.reg_struct->line);
}
int print_len = 0;
for (int j = 0; j < i; j++)
{
print_len += sh_echo(sh_hdl, "%s%s", argv[j], j + 1 < i ? " " : "");
}
for (int j = 0; j < total_len - print_len; j++)
{
sh_echo(sh_hdl, " ");
}
sh_echo(sh_hdl, "-- %s\r\n", tmp.match_cmd->help);
if (i == info.match_count)
{
if (info.err_status == _SH_CMD_STATUS_Success && info.match_cmd->sub_fn.cp_fn == NULL)
{
break;
}
for (int j = 0; j < i; j++)
{
sh_echo(sh_hdl, "%s%s", argv[j], j + 1 < i ? " " : "");
}
if (info.err_status == _SH_CMD_STATUS_Incomplete)
{
sh_echo(sh_hdl, " <...>\r\n"); // 命令未完整
}
else if (info.match_cmd->sub_fn.cp_fn)
{
sh_echo(sh_hdl, " [...]\r\n"); // 带参数自动补全的命令
}
}
}
return 0;
}
SH_CMD_CP_FN(_cmd_print_help_param)
{
2025-02-13 17:17:07 +08:00
_sh_completion_cmd(sh_hdl, argc, argv, 0);
2024-03-28 12:19:52 +08:00
}
SH_CMD_FN(_cmd_echo_on)
{
sh_hdl->disable_echo = 0;
return 0;
}
SH_CMD_FN(_cmd_echo_off)
{
sh_hdl->disable_echo = 1;
return 0;
}
SH_CMD_FN(_cmd_parse_test)
{
for (int i = 0; i < argc; i++)
{
sh_parse_t pv = sh_parse_value(argv[i]);
switch (pv.type)
{
case _PARSE_TYPE_STRING: // 字符串
sh_echo(sh_hdl, "param%d: type = string \"%s\"\r\n",
i + 1,
pv.value.val_string);
break;
case _PARSE_TYPE_INTEGER: // 带符号整型
sh_echo(sh_hdl, "param%d: type = inteter, value = %u(dec) 0x%x(hex) 0%o(oct)\r\n",
i + 1,
pv.value.val_unsigned, pv.value.val_unsigned, pv.value.val_unsigned);
break;
case _PARSE_TYPE_UNSIGNED: // 无符号整型
sh_echo(sh_hdl, "param%d: type = inteter, value = %d(dec) 0x%x(hex) 0%o(oct)\r\n",
i + 1,
pv.value.val_integer, pv.value.val_integer, pv.value.val_integer);
break;
case _PARSE_TYPE_FLOAT: // 浮点
sh_echo(sh_hdl, "param%d: type = float, value = %f\r\n",
i + 1,
pv.value.val_float);
break;
default:
break;
}
}
return 0;
}
SH_DEF_SUB_CMD(
_cmd_echo_sublist,
SH_SETUP_CMD("on", "Enable to feedback the command line", _cmd_echo_on, NULL), //
SH_SETUP_CMD("off", "Disable to feedback the command line", _cmd_echo_off, NULL), //
);
SH_DEF_SUB_CMD(
_cmd_sh_sublist,
SH_SETUP_CMD("echo", "Turn on/off echo through sh_echo()", NULL, _cmd_echo_sublist), //
SH_SETUP_CMD("history", "Show history control", _cmd_history, _cmd_history_param), //
#if defined(MIX_SHELL)
SH_SETUP_CMD("list-init", "List all auto initialize function\r\n\t* Usage: list-init [filter]", _cmd_print_init_fn, NULL), //
SH_SETUP_CMD("list-module", "List all module structure\r\n\t* Usage: list-module [filter]", _cmd_print_module_struct, NULL), //
#endif
SH_SETUP_CMD("version", "Print the shell version", _cmd_version, NULL), //
SH_SETUP_CMD("parse-value", "Parse the value of a string demo", _cmd_parse_test, NULL), //
);
SH_REGISTER_CMD(
register_internal_command,
SH_SETUP_CMD("sh", "Internal command", NULL, _cmd_sh_sublist), //
SH_SETUP_CMD("help", "Print the complete root command", _cmd_print_help, _cmd_print_help_param), //
SH_SETUP_CMD("select", "Select parent command", _cmd_select, _cmd_select_param), //
SH_SETUP_CMD("exit", "Exit parent command or disconnect", _cmd_exit, NULL), //
);
2025-02-13 17:17:07 +08:00
static int _vset_init(void)
{
vset_init(&g_uart_handle_vt100, NULL);
return 0;
}
INIT_EXPORT_COMPONENT(_vset_init);
2024-03-28 12:19:52 +08:00
/**
* @brief
* 使
* 1. register_internal_command()
* 2. sh_init_vt100()
*
* @param out vprintf
*/
void sh_register_external(sh_vprint_fn out)
{
sh_register_cmd(&register_internal_command);
if (out == NULL)
{
out = SYS_VPRINT;
}
if (sh_init_vt100(&g_uart_handle_vt100, out, NULL) == 0)
{
static uint8_t history_mem[255];
static uint8_t bank_mem[CONFIG_SH_MAX_LINE_LEN];
sh_config_history_mem(&g_uart_handle_vt100, history_mem, sizeof(history_mem));
sh_config_backup_mem(&g_uart_handle_vt100, bank_mem, sizeof(bank_mem));
2025-02-13 17:17:07 +08:00
_vset_init();
2024-03-28 12:19:52 +08:00
}
}