uboot/common/cli_simple.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2000
   3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   4 *
   5 * Add to readline cmdline-editing by
   6 * (C) Copyright 2005
   7 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
   8 *
   9 * SPDX-License-Identifier:     GPL-2.0+
  10 */
  11
  12#include <common.h>
  13#include <bootretry.h>
  14#include <cli.h>
  15#include <console.h>
  16#include <linux/ctype.h>
  17
  18#define DEBUG_PARSER    0       /* set to 1 to debug */
  19
  20#define debug_parser(fmt, args...)              \
  21        debug_cond(DEBUG_PARSER, fmt, ##args)
  22
  23
  24int cli_simple_parse_line(char *line, char *argv[])
  25{
  26        int nargs = 0;
  27
  28        debug_parser("%s: \"%s\"\n", __func__, line);
  29        while (nargs < CONFIG_SYS_MAXARGS) {
  30                /* skip any white space */
  31                while (isblank(*line))
  32                        ++line;
  33
  34                if (*line == '\0') {    /* end of line, no more args    */
  35                        argv[nargs] = NULL;
  36                        debug_parser("%s: nargs=%d\n", __func__, nargs);
  37                        return nargs;
  38                }
  39
  40                argv[nargs++] = line;   /* begin of argument string     */
  41
  42                /* find end of string */
  43                while (*line && !isblank(*line))
  44                        ++line;
  45
  46                if (*line == '\0') {    /* end of line, no more args    */
  47                        argv[nargs] = NULL;
  48                        debug_parser("parse_line: nargs=%d\n", nargs);
  49                        return nargs;
  50                }
  51
  52                *line++ = '\0';         /* terminate current arg         */
  53        }
  54
  55        printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
  56
  57        debug_parser("%s: nargs=%d\n", __func__, nargs);
  58        return nargs;
  59}
  60
  61void cli_simple_process_macros(const char *input, char *output)
  62{
  63        char c, prev;
  64        const char *varname_start = NULL;
  65        int inputcnt = strlen(input);
  66        int outputcnt = CONFIG_SYS_CBSIZE;
  67        int state = 0;          /* 0 = waiting for '$'  */
  68
  69        /* 1 = waiting for '(' or '{' */
  70        /* 2 = waiting for ')' or '}' */
  71        /* 3 = waiting for '''  */
  72        char __maybe_unused *output_start = output;
  73
  74        debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
  75                     input);
  76
  77        prev = '\0';            /* previous character   */
  78
  79        while (inputcnt && outputcnt) {
  80                c = *input++;
  81                inputcnt--;
  82
  83                if (state != 3) {
  84                        /* remove one level of escape characters */
  85                        if ((c == '\\') && (prev != '\\')) {
  86                                if (inputcnt-- == 0)
  87                                        break;
  88                                prev = c;
  89                                c = *input++;
  90                        }
  91                }
  92
  93                switch (state) {
  94                case 0: /* Waiting for (unescaped) $    */
  95                        if ((c == '\'') && (prev != '\\')) {
  96                                state = 3;
  97                                break;
  98                        }
  99                        if ((c == '$') && (prev != '\\')) {
 100                                state++;
 101                        } else {
 102                                *(output++) = c;
 103                                outputcnt--;
 104                        }
 105                        break;
 106                case 1: /* Waiting for (        */
 107                        if (c == '(' || c == '{') {
 108                                state++;
 109                                varname_start = input;
 110                        } else {
 111                                state = 0;
 112                                *(output++) = '$';
 113                                outputcnt--;
 114
 115                                if (outputcnt) {
 116                                        *(output++) = c;
 117                                        outputcnt--;
 118                                }
 119                        }
 120                        break;
 121                case 2: /* Waiting for )        */
 122                        if (c == ')' || c == '}') {
 123                                int i;
 124                                char envname[CONFIG_SYS_CBSIZE], *envval;
 125                                /* Varname # of chars */
 126                                int envcnt = input - varname_start - 1;
 127
 128                                /* Get the varname */
 129                                for (i = 0; i < envcnt; i++)
 130                                        envname[i] = varname_start[i];
 131                                envname[i] = 0;
 132
 133                                /* Get its value */
 134                                envval = getenv(envname);
 135
 136                                /* Copy into the line if it exists */
 137                                if (envval != NULL)
 138                                        while ((*envval) && outputcnt) {
 139                                                *(output++) = *(envval++);
 140                                                outputcnt--;
 141                                        }
 142                                /* Look for another '$' */
 143                                state = 0;
 144                        }
 145                        break;
 146                case 3: /* Waiting for '        */
 147                        if ((c == '\'') && (prev != '\\')) {
 148                                state = 0;
 149                        } else {
 150                                *(output++) = c;
 151                                outputcnt--;
 152                        }
 153                        break;
 154                }
 155                prev = c;
 156        }
 157
 158        if (outputcnt)
 159                *output = 0;
 160        else
 161                *(output - 1) = 0;
 162
 163        debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
 164                     strlen(output_start), output_start);
 165}
 166
 167 /*
 168 * WARNING:
 169 *
 170 * We must create a temporary copy of the command since the command we get
 171 * may be the result from getenv(), which returns a pointer directly to
 172 * the environment data, which may change magicly when the command we run
 173 * creates or modifies environment variables (like "bootp" does).
 174 */
 175int cli_simple_run_command(const char *cmd, int flag)
 176{
 177        char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd          */
 178        char *token;                    /* start of token in cmdbuf     */
 179        char *sep;                      /* end of token (separator) in cmdbuf */
 180        char finaltoken[CONFIG_SYS_CBSIZE];
 181        char *str = cmdbuf;
 182        char *argv[CONFIG_SYS_MAXARGS + 1];     /* NULL terminated      */
 183        int argc, inquotes;
 184        int repeatable = 1;
 185        int rc = 0;
 186
 187        debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
 188        if (DEBUG_PARSER) {
 189                /* use puts - string may be loooong */
 190                puts(cmd ? cmd : "NULL");
 191                puts("\"\n");
 192        }
 193        clear_ctrlc();          /* forget any previous Control C */
 194
 195        if (!cmd || !*cmd)
 196                return -1;      /* empty command */
 197
 198        if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
 199                puts("## Command too long!\n");
 200                return -1;
 201        }
 202
 203        strcpy(cmdbuf, cmd);
 204
 205        /* Process separators and check for invalid
 206         * repeatable commands
 207         */
 208
 209        debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
 210        while (*str) {
 211                /*
 212                 * Find separator, or string end
 213                 * Allow simple escape of ';' by writing "\;"
 214                 */
 215                for (inquotes = 0, sep = str; *sep; sep++) {
 216                        if ((*sep == '\'') &&
 217                            (*(sep - 1) != '\\'))
 218                                inquotes = !inquotes;
 219
 220                        if (!inquotes &&
 221                            (*sep == ';') &&    /* separator            */
 222                            (sep != str) &&     /* past string start    */
 223                            (*(sep - 1) != '\\'))       /* and NOT escaped */
 224                                break;
 225                }
 226
 227                /*
 228                 * Limit the token to data between separators
 229                 */
 230                token = str;
 231                if (*sep) {
 232                        str = sep + 1;  /* start of command for next pass */
 233                        *sep = '\0';
 234                } else {
 235                        str = sep;      /* no more commands for next pass */
 236                }
 237                debug_parser("token: \"%s\"\n", token);
 238
 239                /* find macros in this token and replace them */
 240                cli_simple_process_macros(token, finaltoken);
 241
 242                /* Extract arguments */
 243                argc = cli_simple_parse_line(finaltoken, argv);
 244                if (argc == 0) {
 245                        rc = -1;        /* no command at all */
 246                        continue;
 247                }
 248
 249                if (cmd_process(flag, argc, argv, &repeatable, NULL))
 250                        rc = -1;
 251
 252                /* Did the user stop this? */
 253                if (had_ctrlc())
 254                        return -1;      /* if stopped then not repeatable */
 255        }
 256
 257        return rc ? rc : repeatable;
 258}
 259
 260void cli_simple_loop(void)
 261{
 262        static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
 263
 264        int len;
 265        int flag;
 266        int rc = 1;
 267
 268        for (;;) {
 269                if (rc >= 0) {
 270                        /* Saw enough of a valid command to
 271                         * restart the timeout.
 272                         */
 273                        bootretry_reset_cmd_timeout();
 274                }
 275                len = cli_readline(CONFIG_SYS_PROMPT);
 276
 277                flag = 0;       /* assume no special flags for now */
 278                if (len > 0)
 279                        strlcpy(lastcommand, console_buffer,
 280                                CONFIG_SYS_CBSIZE + 1);
 281                else if (len == 0)
 282                        flag |= CMD_FLAG_REPEAT;
 283#ifdef CONFIG_BOOT_RETRY_TIME
 284                else if (len == -2) {
 285                        /* -2 means timed out, retry autoboot
 286                         */
 287                        puts("\nTimed out waiting for command\n");
 288# ifdef CONFIG_RESET_TO_RETRY
 289                        /* Reinit board to run initialization code again */
 290                        do_reset(NULL, 0, 0, NULL);
 291# else
 292                        return;         /* retry autoboot */
 293# endif
 294                }
 295#endif
 296
 297                if (len == -1)
 298                        puts("<INTERRUPT>\n");
 299                else
 300                        rc = run_command_repeatable(lastcommand, flag);
 301
 302                if (rc <= 0) {
 303                        /* invalid command or not repeatable, forget it */
 304                        lastcommand[0] = 0;
 305                }
 306        }
 307}
 308
 309int cli_simple_run_command_list(char *cmd, int flag)
 310{
 311        char *line, *next;
 312        int rcode = 0;
 313
 314        /*
 315         * Break into individual lines, and execute each line; terminate on
 316         * error.
 317         */
 318        next = cmd;
 319        line = cmd;
 320        while (*next) {
 321                if (*next == '\n') {
 322                        *next = '\0';
 323                        /* run only non-empty commands */
 324                        if (*line) {
 325                                debug("** exec: \"%s\"\n", line);
 326                                if (cli_simple_run_command(line, 0) < 0) {
 327                                        rcode = 1;
 328                                        break;
 329                                }
 330                        }
 331                        line = next + 1;
 332                }
 333                ++next;
 334        }
 335        if (rcode == 0 && *line)
 336                rcode = (cli_simple_run_command(line, 0) < 0);
 337
 338        return rcode;
 339}
 340