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