命令系统

XWSH 的命令系统:内置命令详解、外部命令添加方法和重名检查机制。

命令结构体

所有 XWSH 命令都使用 struct xwsh_cmd 结构体定义(xwmd/cli/xwsh/mi.h:21):

struct xwsh_cmd {
        char * name;                               /**< 命令的名称 */
        xwer_t (*func)(xwsz_t argc, char ** argv); /**< 命令的函数 */
        char * desc;                               /**< 命令的描述 */
};

字段说明

字段 类型 说明
name char * 命令名称字符串(大小写敏感)
func 函数指针 命令处理函数,接受参数个数和数组
desc char * 命令描述,显示在帮助信息中

命令处理函数签名

xwer_t cmd_handler(xwsz_t argc, char ** argv);
  • argc:参数个数(包含命令名本身,argv[0]
  • argv:参数指针数组,每个元素指向以 \0 结尾的字符串
  • 返回值XWOK(0)表示成功,负值表示错误码

内置命令

XWSH 内置了四个基础命令,定义在 xwmd/cli/xwsh/cmd.c 中。

1. help - 显示帮助信息

功能:显示所有可用命令(内置 + 外部)的名称和描述。

用法

help

示例输出

--------------------------------------------
help    show this help
clear   clear screen
rd      read memory
wr      write memory
mycmd   my custom command

实现xwsh_cmd_help()):

xwer_t xwsh_cmd_help(xwsz_t argc, char ** argv)
{
        XWOS_UNUSED(argc);
        XWOS_UNUSED(argv);
        xwsh_show_all_cmds();  // 调用核心函数显示命令列表
        return XWOK;
}

2. clear - 清屏

功能:使用 ANSI 转义序列清除终端屏幕,并将光标移动到左上角。

用法

clear

ANSI 转义序列

  • \e[2J:清除整个屏幕
  • \e[0;0H:移动光标到第 0 行第 0 列

实现xwsh_cmd_clear()):

xwer_t xwsh_cmd_clear(xwsz_t argc, char ** argv)
{
        XWOS_UNUSED(argc);
        XWOS_UNUSED(argv);
        printf("\e[2J");   // 清屏
        printf("\e[0;0H"); // 光标归位
        return XWOK;
}

3. rd - 读取内存

功能:以十六进制和 ASCII 格式显示指定内存区域的内容。

用法

rd <地址十六进制> [大小十进制]

参数

参数 格式 默认值 说明
地址 十六进制 要读取的内存起始地址
大小 十进制 1 要读取的字节数

示例

rd 0x90000000      # 读取 0x90000000 处的 1 个字节
rd 0x90000000 16   # 读取 0x90000000 处的 16 个字节
rd 0x20000000 256  # 读取 0x20000000 处的 256 个字节

输出格式

origin address: 0x90000000
00000000: 12 34 56 78 9a bc de f0  11 22 33 44 55 66 77 88 |.4Vx....."3DUfw.|
00000010: aa bb cc dd ee ff 00 11  22 33 44 55 66 77 88 99 |........"3DUfw..|

格式说明

  • 每行显示 16 字节
  • 左侧:偏移量(十六进制)
  • 中间:十六进制值(每 8 字节后加空格分隔)
  • 右侧:ASCII 表示(不可打印字符显示为 .

4. wr - 写入内存

功能:向指定内存地址写入数据。

用法

wr <地址十六进制> <值十六进制> [b|w|l]

参数

参数 格式 默认值 说明
地址 十六进制 要写入的内存地址
十六进制 要写入的数值
大小 字符 l 写入大小:b(字节)、w(字)、l(长字)

示例

wr 0x90000000 0x1234 w    # 向 0x90000000 写入 0x1234(2 字节)
wr 0x90000000 0x55 b      # 向 0x90000000 写入 0x55(1 字节)
wr 0x90000000 0xdeadbeef  # 向 0x90000000 写入 0xdeadbeef(4 字节)

大小说明

选项 大小 对应类型 地址对齐
b 1 字节 xwu8_t 任意
w 2 字节 xwu16_t 2 字节对齐
l 4 字节 xwu32_t 4 字节对齐

外部命令添加

用户可以通过 xwsh_set_ext_cmd_table() 函数添加自定义命令。

1. 定义命令结构体数组

#include "xwmd/cli/xwsh/mi.h"

xwer_t my_test_cmd(xwsz_t argc, char ** argv)
{
        printf("My test command executed!\r\n");
        printf("argc: %d\r\n", argc);
        for (xwsz_t i = 0; i < argc; i++) {
                printf("argv[%d]: %s\r\n", i, argv[i]);
        }
        return XWOK;
}

xwer_t my_calc_cmd(xwsz_t argc, char ** argv)
{
        if (argc != 3) {
                printf("Usage: calc <num1> <num2>\r\n");
                return -EINVAL;
        }
        xws32_t a, b;
        sscanf(argv[1], "%d", &a);
        sscanf(argv[2], "%d", &b);
        printf("%d + %d = %d\r\n", a, b, a + b);
        return XWOK;
}

const struct xwsh_cmd my_cmds[] = {
        { .name = "test", .func = my_test_cmd, .desc = "my test command" },
        { .name = "calc", .func = my_calc_cmd, .desc = "simple calculator" },
};

2. 注册命令表

时机:必须在 XWSH 启动前调用(xwsh_start()xwsh_init() 之前)。

xwer_t rc = xwsh_set_ext_cmd_table(my_cmds, 
                                   sizeof(my_cmds) / sizeof(my_cmds[0]));
if (rc < 0) {
        printf("Failed to set command table: %d\r\n", rc);
}

3. 清空命令表

要移除所有外部命令,可传递 NULL 和 0:

xwsh_set_ext_cmd_table(NULL, 0);  // 清空外部命令表

重名检查机制

xwsh_set_ext_cmd_table() 函数执行严格的重名检查,确保命令名称唯一性。

检查规则

  1. 外部命令 vs 内部命令

    for (j = 0; j < xwsh_cmd_table_size; j++) {
            if (!strcmp(cmd[i].name, xwsh_cmd_table[j].name)) {
                    rc = -EEXIST;  // 与内置命令重名
                    goto err_duplicate;
            }
    }
    
  2. 外部命令之间

    for (j = 0; j < i; j++) {
            if (!strcmp(cmd[i].name, cmd[j].name)) {
                    rc = -EEXIST;  // 外部命令之间重名
                    goto err_duplicate;
            }
    }
    

错误处理

错误码 含义 处理建议
-EEXIST 命令名称已存在 修改命令名称或移除冲突命令
-EINVAL 参数无效(cmdNULLnum 不为 0) 检查参数逻辑

命名建议

  • 使用小写字母:保持与内置命令一致
  • 避免特殊字符:只使用字母、数字、下划线
  • 简洁明了:命令名应反映功能
  • 长度适中:建议 2-10 个字符

命令执行流程

1. 命令查找(xwsh_find_cmd()

const struct xwsh_cmd * xwsh_find_cmd(char * name)
{
        // 1. 检查内置命令表
        for (i = 0; i < xwsh_cmd_table_size; i++) {
                if (!strcmp(name, xwsh_cmd_table[i].name)) {
                        return &xwsh_cmd_table[i];
                }
        }
        
        // 2. 检查外部命令表
        if ((NULL != xwsh_ext_cmd_table) && (0 != xwsh_ext_cmd_table_size)) {
                for (i = 0; i < xwsh_ext_cmd_table_size; i++) {
                        if (!strcmp(name, xwsh_ext_cmd_table[i].name)) {
                                return &xwsh_ext_cmd_table[i];
                        }
                }
        }
        
        return NULL;  // 命令未找到
}

2. 命令执行(xwsh_run_cmdline()

xwer_t xwsh_run_cmdline(char * line)
{
        // 1. 分割命令行
        rc = xwsh_split_cmdline(line, &argc, argv, XWSH_MAX_PARAM_NUM);
        
        // 2. 查找命令
        cmd = xwsh_find_cmd(argv[0]);
        
        // 3. 执行命令
        if (cmd) {
                if (cmd->func) {
                        rc = cmd->func(argc, argv);  // 调用命令处理函数
                } else {
                        printf("cmd %s not implemented.\r\n", cmd->name);
                }
        } else if (argv[0]) {
                printf("\r\ncan't find cmd %s.\r\n", argv[0]);
        }
        
        return rc;
}

最佳实践

1. 命令设计原则

  • 单一职责:每个命令只完成一个明确的功能
  • 清晰的帮助:命令描述应简洁说明功能
  • 参数验证:在命令处理函数中验证参数合法性
  • 错误反馈:提供有意义的错误信息

2. 内存安全

xwer_t safe_memory_cmd(xwsz_t argc, char ** argv)
{
        xwptr_t addr;
        
        // 参数数量检查
        if (argc != 2) {
                printf("Usage: %s <address>\r\n", argv[0]);
                return -EINVAL;
        }
        
        // 地址格式验证
        if (sscanf(argv[1], "%lx", &addr) != 1) {
                printf("Invalid address format\n");
                return -EINVAL;
        }
        
        // 地址范围检查(可选)
        if (addr < 0x20000000 || addr >= 0x24000000) {
                printf("Address out of valid range\n");
                return -EACCES;
        }
        
        // 执行操作
        // ...
        
        return XWOK;
}

3. 性能考虑

  • 避免长时间阻塞:命令执行时间应尽量短
  • 减少内存分配:使用栈变量而非堆分配
  • 输出优化:避免大量字符串操作和格式化

调试技巧

1. 命令调试输出

xwer_t debug_cmd(xwsz_t argc, char ** argv)
{
#ifdef XWSH_DBG
        xwsz_t i;
        xwlogf(D, "XWSH.CMD", "argc: %d\r\n", argc);
        for (i = 0; i < argc; i++) {
                xwlogf(D, "XWSH.CMD", "argv[%d]: %s\r\n", i, argv[i]);
        }
#endif
        // 命令逻辑
        return XWOK;
}

2. 启用调试模式

core.creadline.c 中取消注释 #define XWSH_DBG 可启用详细调试输出。