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 *)®_cmd[i] != 0; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
const sh_cmd_t *test_cmd = ®_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 *)®_cmd[i] != 0; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
const sh_cmd_t *test_cmd = ®_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 *)®_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(®ister_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
|
|
|
|
}
|
|
|
|
|
|
}
|