“命令终端”的实现3-命令的执行

前面已经能获取到输入的字符了,接着就是解析这些字符,判断,符合要求的,执行对应的函数。而对应的函数,就是需要实现的命令。本文从具体的命令实现逐步倒推,最后对接上一文章。

一、命令函数

Linux 中,基本上每条命令都有参数及帮助信息,仿照这些功能,给出实现函数指针及结构体:

/*
 * Monitor Command Table
 */
typedef int (*cmd_func)(int argc, char * const argv[]);

struct cmd_tbl_s {
    char    *name;        /* Command Name            */
    int     maxargs;    /* maximum number of arguments    */
    cmd_func cmd;       /* Implementation function    */
    char    *usage;        /* Usage message    (short)    */
#ifdef    CONFIG_SYS_LONGHELP
    char    *help;    /* Help  message    (long)    */
#endif
#ifdef CONFIG_AUTO_COMPLETE
    /* do auto completion on the arguments */
    int     (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

typedef struct cmd_tbl_s    cmd_tbl_t;

/* someone should implement the table */
extern cmd_tbl_t cmd_table[];

为保持命令的独立性,设置cmd_func函数指针。命令结构体为全局变量,下面给出其定义及对应命令的实现:


/* just a foolish command table */
cmd_tbl_t cmd_table[] = 
{
    {"help", CONFIG_SYS_MAXARGS, do_help, "print help info."},
    {"print", 1, do_print, "print the env."},
    {"exit", 1, do_exit, "exit..."},
    {NULL, 0, NULL, NULL},
};

int do_help(int argc, char * const argv[])
{
    _do_help(cmd_table, sizeof(cmd_table)/sizeof(cmd_tbl_t), argc, argv);
    return 0;
}


int do_print(int argc, char * const argv[])
{
    myprintf("in %s\n", __FUNCTION__);
    return 0;
}

int do_exit(int argc, char * const argv[])
{
    exit(0);
    return 0;
}

以上就是具体命令的实现过程,可以将每条命令都理解为一个单独的可带参数的程序。

二、查找命令

查找命令名称前,需要先对输入的字符串进行解析,实现函数如下:

int parse_line (char *line, char *argv[])
{
    int nargs = 0;

#ifdef DEBUG_PARSER
    cmd_printf ("parse_line: \"%s\"\n", line);
#endif
    while (nargs < CONFIG_SYS_MAXARGS)
    {
        /* skip any white space */
        while ((*line == ' ') || (*line == '\t'))
        {
            ++line;
        }

        if (*line == '\0')
        {    /* end of line, no more args    */
            argv[nargs] = NULL;
#ifdef DEBUG_PARSER
            cmd_printf ("parse_line: nargs=%d\n", nargs);
#endif
            return (nargs);
        }

        argv[nargs++] = line;    /* begin of argument string    */

        /* find end of string */
        while (*line && (*line != ' ') && (*line != '\t'))
        {
            ++line;
        }

        if (*line == '\0')
        {    /* end of line, no more args    */
            argv[nargs] = NULL;
#ifdef DEBUG_PARSER
            cmd_printf ("parse_line: nargs=%d\n", nargs);
#endif
            return (nargs);
        }

        *line++ = '\0';        /* terminate current arg     */
    }

    cmd_printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);

#ifdef DEBUG_PARSER
    cmd_printf ("parse_line: nargs=%d\n", nargs);
#endif
    return (nargs);
}

输出参数为argv数组,此即为命令参数列表,其中argv[0]即命令名称,返回值为命令参数个数,即argc。这就是我们熟悉的C语言 main 函数的参数。

查找命令实际是搜索结构体数组cmd_table中的命令名称。如果找到返回名称,反之为空。

cmd_tbl_t* find_cmd_tbl3(const char* cmd, cmd_tbl_t *table)
{
    const char *p;
    char *q;
    cmd_tbl_t *c, *found;
    int nmatches, longest;

    longest = 0;
    nmatches = 0;
    found = 0;
    for (c = table; (p = c->name) != NULL; c++)
    {
        for (q = cmd; *q == *p++; q++)
            if (*q == 0)        /* exact match? */
                return (c);
        if (!*q)
        {
            return NULL;
        }
    }
    return NULL;
}

cmd_tbl_t *find_cmd (const char* cmd)
{
    //int len = sizeof(cmd_table) / sizeof(cmd_tbl_t);
    //return find_cmd_tbl(cmd, cmd_table, len);

    //return find_cmd_tbl2(cmd, cmd_table);
    return find_cmd_tbl3(cmd, cmd_table);
}

注意,如果命令数量过多,不宜使用从头遍历的方法查找。本文工程仅为示例,暂不研究效率。

三、汇总运行

运行命令函数实现如下,

int run_command (const char *cmd)
{
    cmd_tbl_t *cmdtp;
    char cmdbuf[CB_SIZE];    /* working copy of cmd        */
    char *token;            /* start of token in cmdbuf    */
    char *sep;            /* end of token (separator) in cmdbuf */
    //char finaltoken[CB_SIZE];
    char *str = cmdbuf;
    char *argv[CONFIG_SYS_MAXARGS + 1];    /* NULL terminated    */
    int argc, inquotes;
    int repeatable = 1;
    int rc = 0;

#ifdef DEBUG_PARSER
    cmd_printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
    cmd_puts (cmd ? cmd : "NULL");    /* use puts - string may be loooong */
    cmd_puts ("\"\n");
#endif

    //clear_ctrlc();        /* forget any previous Control C */

    if (!cmd || !*cmd)
    {
        return -1;    /* empty command */
    }

    if (strlen(cmd) >= CB_SIZE)
    {
        cmd_puts ("## Command too long!\n");
        return -1;
    }

    strcpy (cmdbuf, cmd);

    /* Process separators and check for invalid
     * repeatable commands
     */

#ifdef DEBUG_PARSER
    cmd_printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
    while (*str) {

        /*
         * Find separator, or string end
         * Allow simple escape of ';' by writing "\;"
         */
        for (inquotes = 0, sep = str; *sep; sep++)
        {
            if ((*sep=='\'') &&
                (*(sep-1) != '\\'))
                inquotes=!inquotes;

            if (!inquotes &&
                (*sep == ';') &&    /* separator        */
                ( sep != str) &&    /* past string start    */
                (*(sep-1) != '\\'))    /* and NOT escaped    */
                break;
        }

        /*
         * Limit the token to data between separators
         */
        token = str;
        if (*sep)
        {
            str = sep + 1;    /* start of command for next pass */
            *sep = '\0';
        }
        else
            str = sep;    /* no more commands for next pass */
#ifdef DEBUG_PARSER
        cmd_printf ("token: \"%s\"\n", token);
#endif

        /* Extract arguments */
        if ((argc = parse_line (token, argv)) == 0)
        {
            rc = -1;    /* no command at all */
            continue;
        }

        /* Look up command in command table */
        if ((cmdtp = find_cmd(argv[0])) == NULL)
        {
            cmd_printf ("Unknown command '%s' - try 'help'\n", argv[0]);
            rc = -1;    /* give up after bad command */
            continue;
        }

        /* found - check max args */
        if (argc > cmdtp->maxargs)
        {
            cmd_usage(cmdtp);
            rc = -1;
            continue;
        }

        /* OK - call function to do the command */
        if ((cmdtp->cmd) (argc, argv) != 0)
        {
            rc = -1;
        }
    }

    //return rc ? rc : repeatable;
    return rc;
}

主要过程有解析字符串parse_line,查找命令结构数组find_cmd,最后运行函数指针。

四、主函数

入口函数及倒计时函数如下:

int abortboot(int delay)
{
    int abort = 0;
    int i = 0;

    myprintf("Hit any key to stop autoboot: %2d ", delay);

    while ((delay > 0) && (!abort))
    {
        --delay;
        for (i = 0; !abort && i < 100; ++i)
        {
            if (mytstc())
            {
                abort = 1;
                delay = 0;
                mygetc();
                break;
            }
            mysleep(10);
        }
        myprintf("\b\b\b%2d ", delay);
    }
    myputc('\n');

    return abort;
}

int readline_test(void)
{
    static char lastcommand[CB_SIZE] = {0};
    int len;

    if (!abortboot(5))
    {
        myprintf("Aotu run.\n");
        return 0;
    }
    myprintf("You abort.\n");

    while (1)
    {
        len = readline(PROMPT, lastcommand);
        if (len > 0)
        {
            //printf("len: %d\n", len);
            if (len >= CB_SIZE)
            {
                myprintf("command line too large.\n");
                break;
            }
            //strcpy(lastcommand, console_buffer);
            //printf("[echo]: %s\n", lastcommand);
        }

        else if (len == 0)
        {
            //printf("nothing input.\n");
            // do nothing
        }

        if (len == -1)
        {
            myputs("<INTERRUPT>\n");
        }
        else
        {
            run_command(lastcommand);
        }
     
    }
    return 0;
}

至此,一个“命令终端”实现完毕。可对其底层的字符处理进行修改,以适应不同环境。除此外,其它业务逻辑完全无变化。

李迟 2020.9.30

©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页