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