uboot/common/command.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2000-2009
   4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   5 */
   6
   7/*
   8 *  Command Processor Table
   9 */
  10
  11#include <common.h>
  12#include <compiler.h>
  13#include <command.h>
  14#include <console.h>
  15#include <env.h>
  16#include <log.h>
  17#include <linux/ctype.h>
  18
  19/*
  20 * Use puts() instead of printf() to avoid printf buffer overflow
  21 * for long help messages
  22 */
  23
  24int _do_help(struct cmd_tbl *cmd_start, int cmd_items, struct cmd_tbl *cmdtp,
  25             int flag, int argc, char *const argv[])
  26{
  27        int i;
  28        int rcode = 0;
  29
  30        if (argc == 1) {        /* show list of commands */
  31                struct cmd_tbl *cmd_array[cmd_items];
  32                int i, j, swaps;
  33
  34                /* Make array of commands from .uboot_cmd section */
  35                cmdtp = cmd_start;
  36                for (i = 0; i < cmd_items; i++) {
  37                        cmd_array[i] = cmdtp++;
  38                }
  39
  40                /* Sort command list (trivial bubble sort) */
  41                for (i = cmd_items - 1; i > 0; --i) {
  42                        swaps = 0;
  43                        for (j = 0; j < i; ++j) {
  44                                if (strcmp(cmd_array[j]->name,
  45                                           cmd_array[j + 1]->name) > 0) {
  46                                        struct cmd_tbl *tmp;
  47                                        tmp = cmd_array[j];
  48                                        cmd_array[j] = cmd_array[j + 1];
  49                                        cmd_array[j + 1] = tmp;
  50                                        ++swaps;
  51                                }
  52                        }
  53                        if (!swaps)
  54                                break;
  55                }
  56
  57                /* print short help (usage) */
  58                for (i = 0; i < cmd_items; i++) {
  59                        const char *usage = cmd_array[i]->usage;
  60
  61                        /* allow user abort */
  62                        if (ctrlc())
  63                                return 1;
  64                        if (usage == NULL)
  65                                continue;
  66                        printf("%-*s- %s\n", CONFIG_SYS_HELP_CMD_WIDTH,
  67                               cmd_array[i]->name, usage);
  68                }
  69                return 0;
  70        }
  71        /*
  72         * command help (long version)
  73         */
  74        for (i = 1; i < argc; ++i) {
  75                cmdtp = find_cmd_tbl(argv[i], cmd_start, cmd_items);
  76                if (cmdtp != NULL) {
  77                        rcode |= cmd_usage(cmdtp);
  78                } else {
  79                        printf("Unknown command '%s' - try 'help' without arguments for list of all known commands\n\n",
  80                               argv[i]);
  81                        rcode = 1;
  82                }
  83        }
  84        return rcode;
  85}
  86
  87/* find command table entry for a command */
  88struct cmd_tbl *find_cmd_tbl(const char *cmd, struct cmd_tbl *table,
  89                             int table_len)
  90{
  91#ifdef CONFIG_CMDLINE
  92        struct cmd_tbl *cmdtp;
  93        struct cmd_tbl *cmdtp_temp = table;     /* Init value */
  94        const char *p;
  95        int len;
  96        int n_found = 0;
  97
  98        if (!cmd)
  99                return NULL;
 100        /*
 101         * Some commands allow length modifiers (like "cp.b");
 102         * compare command name only until first dot.
 103         */
 104        len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
 105
 106        for (cmdtp = table; cmdtp != table + table_len; cmdtp++) {
 107                if (strncmp(cmd, cmdtp->name, len) == 0) {
 108                        if (len == strlen(cmdtp->name))
 109                                return cmdtp;   /* full match */
 110
 111                        cmdtp_temp = cmdtp;     /* abbreviated command ? */
 112                        n_found++;
 113                }
 114        }
 115        if (n_found == 1) {                     /* exactly one match */
 116                return cmdtp_temp;
 117        }
 118#endif /* CONFIG_CMDLINE */
 119
 120        return NULL;    /* not found or ambiguous command */
 121}
 122
 123struct cmd_tbl *find_cmd(const char *cmd)
 124{
 125        struct cmd_tbl *start = ll_entry_start(struct cmd_tbl, cmd);
 126        const int len = ll_entry_count(struct cmd_tbl, cmd);
 127        return find_cmd_tbl(cmd, start, len);
 128}
 129
 130int cmd_usage(const struct cmd_tbl *cmdtp)
 131{
 132        printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);
 133
 134#ifdef  CONFIG_SYS_LONGHELP
 135        printf("Usage:\n%s ", cmdtp->name);
 136
 137        if (!cmdtp->help) {
 138                puts ("- No additional help available.\n");
 139                return 1;
 140        }
 141
 142        puts(cmdtp->help);
 143        putc('\n');
 144#endif  /* CONFIG_SYS_LONGHELP */
 145        return 1;
 146}
 147
 148#ifdef CONFIG_AUTO_COMPLETE
 149static char env_complete_buf[512];
 150
 151int var_complete(int argc, char *const argv[], char last_char, int maxv,
 152                 char *cmdv[])
 153{
 154        int space;
 155
 156        space = last_char == '\0' || isblank(last_char);
 157
 158        if (space && argc == 1)
 159                return env_complete("", maxv, cmdv, sizeof(env_complete_buf),
 160                                    env_complete_buf, false);
 161
 162        if (!space && argc == 2)
 163                return env_complete(argv[1], maxv, cmdv,
 164                                    sizeof(env_complete_buf),
 165                                    env_complete_buf, false);
 166
 167        return 0;
 168}
 169
 170static int dollar_complete(int argc, char *const argv[], char last_char,
 171                           int maxv, char *cmdv[])
 172{
 173        /* Make sure the last argument starts with a $. */
 174        if (argc < 1 || argv[argc - 1][0] != '$' ||
 175            last_char == '\0' || isblank(last_char))
 176                return 0;
 177
 178        return env_complete(argv[argc - 1], maxv, cmdv, sizeof(env_complete_buf),
 179                            env_complete_buf, true);
 180}
 181
 182/*************************************************************************************/
 183
 184int complete_subcmdv(struct cmd_tbl *cmdtp, int count, int argc,
 185                     char *const argv[], char last_char,
 186                     int maxv, char *cmdv[])
 187{
 188#ifdef CONFIG_CMDLINE
 189        const struct cmd_tbl *cmdend = cmdtp + count;
 190        const char *p;
 191        int len, clen;
 192        int n_found = 0;
 193        const char *cmd;
 194
 195        /* sanity? */
 196        if (maxv < 2)
 197                return -2;
 198
 199        cmdv[0] = NULL;
 200
 201        if (argc == 0) {
 202                /* output full list of commands */
 203                for (; cmdtp != cmdend; cmdtp++) {
 204                        if (n_found >= maxv - 2) {
 205                                cmdv[n_found++] = "...";
 206                                break;
 207                        }
 208                        cmdv[n_found++] = cmdtp->name;
 209                }
 210                cmdv[n_found] = NULL;
 211                return n_found;
 212        }
 213
 214        /* more than one arg or one but the start of the next */
 215        if (argc > 1 || last_char == '\0' || isblank(last_char)) {
 216                cmdtp = find_cmd_tbl(argv[0], cmdtp, count);
 217                if (cmdtp == NULL || cmdtp->complete == NULL) {
 218                        cmdv[0] = NULL;
 219                        return 0;
 220                }
 221                return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv);
 222        }
 223
 224        cmd = argv[0];
 225        /*
 226         * Some commands allow length modifiers (like "cp.b");
 227         * compare command name only until first dot.
 228         */
 229        p = strchr(cmd, '.');
 230        if (p == NULL)
 231                len = strlen(cmd);
 232        else
 233                len = p - cmd;
 234
 235        /* return the partial matches */
 236        for (; cmdtp != cmdend; cmdtp++) {
 237
 238                clen = strlen(cmdtp->name);
 239                if (clen < len)
 240                        continue;
 241
 242                if (memcmp(cmd, cmdtp->name, len) != 0)
 243                        continue;
 244
 245                /* too many! */
 246                if (n_found >= maxv - 2) {
 247                        cmdv[n_found++] = "...";
 248                        break;
 249                }
 250
 251                cmdv[n_found++] = cmdtp->name;
 252        }
 253
 254        cmdv[n_found] = NULL;
 255        return n_found;
 256#else
 257        return 0;
 258#endif
 259}
 260
 261static int complete_cmdv(int argc, char *const argv[], char last_char,
 262                         int maxv, char *cmdv[])
 263{
 264#ifdef CONFIG_CMDLINE
 265        return complete_subcmdv(ll_entry_start(struct cmd_tbl, cmd),
 266                                ll_entry_count(struct cmd_tbl, cmd), argc, argv,
 267                                last_char, maxv, cmdv);
 268#else
 269        return 0;
 270#endif
 271}
 272
 273static int make_argv(char *s, int argvsz, char *argv[])
 274{
 275        int argc = 0;
 276
 277        /* split into argv */
 278        while (argc < argvsz - 1) {
 279
 280                /* skip any white space */
 281                while (isblank(*s))
 282                        ++s;
 283
 284                if (*s == '\0') /* end of s, no more args       */
 285                        break;
 286
 287                argv[argc++] = s;       /* begin of argument string     */
 288
 289                /* find end of string */
 290                while (*s && !isblank(*s))
 291                        ++s;
 292
 293                if (*s == '\0')         /* end of s, no more args       */
 294                        break;
 295
 296                *s++ = '\0';            /* terminate current arg         */
 297        }
 298        argv[argc] = NULL;
 299
 300        return argc;
 301}
 302
 303static void print_argv(const char *banner, const char *leader, const char *sep,
 304                       int linemax, char *const argv[])
 305{
 306        int ll = leader != NULL ? strlen(leader) : 0;
 307        int sl = sep != NULL ? strlen(sep) : 0;
 308        int len, i;
 309
 310        if (banner) {
 311                puts("\n");
 312                puts(banner);
 313        }
 314
 315        i = linemax;    /* force leader and newline */
 316        while (*argv != NULL) {
 317                len = strlen(*argv) + sl;
 318                if (i + len >= linemax) {
 319                        puts("\n");
 320                        if (leader)
 321                                puts(leader);
 322                        i = ll - sl;
 323                } else if (sep)
 324                        puts(sep);
 325                puts(*argv++);
 326                i += len;
 327        }
 328        printf("\n");
 329}
 330
 331static int find_common_prefix(char *const argv[])
 332{
 333        int i, len;
 334        char *anchor, *s, *t;
 335
 336        if (*argv == NULL)
 337                return 0;
 338
 339        /* begin with max */
 340        anchor = *argv++;
 341        len = strlen(anchor);
 342        while ((t = *argv++) != NULL) {
 343                s = anchor;
 344                for (i = 0; i < len; i++, t++, s++) {
 345                        if (*t != *s)
 346                                break;
 347                }
 348                len = s - anchor;
 349        }
 350        return len;
 351}
 352
 353static char tmp_buf[CONFIG_SYS_CBSIZE + 1];     /* copy of console I/O buffer */
 354
 355int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp)
 356{
 357        int n = *np, col = *colp;
 358        char *argv[CONFIG_SYS_MAXARGS + 1];             /* NULL terminated      */
 359        char *cmdv[20];
 360        char *s, *t;
 361        const char *sep;
 362        int i, j, k, len, seplen, argc;
 363        int cnt;
 364        char last_char;
 365#ifdef CONFIG_CMDLINE_PS_SUPPORT
 366        const char *ps_prompt = env_get("PS1");
 367#else
 368        const char *ps_prompt = CONFIG_SYS_PROMPT;
 369#endif
 370
 371        if (strcmp(prompt, ps_prompt) != 0)
 372                return 0;       /* not in normal console */
 373
 374        cnt = strlen(buf);
 375        if (cnt >= 1)
 376                last_char = buf[cnt - 1];
 377        else
 378                last_char = '\0';
 379
 380        /* copy to secondary buffer which will be affected */
 381        strcpy(tmp_buf, buf);
 382
 383        /* separate into argv */
 384        argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv);
 385
 386        /* first try a $ completion */
 387        i = dollar_complete(argc, argv, last_char,
 388                            sizeof(cmdv) / sizeof(cmdv[0]), cmdv);
 389        if (!i) {
 390                /* do the completion and return the possible completions */
 391                i = complete_cmdv(argc, argv, last_char,
 392                                  sizeof(cmdv) / sizeof(cmdv[0]), cmdv);
 393        }
 394
 395        /* no match; bell and out */
 396        if (i == 0) {
 397                if (argc > 1)   /* allow tab for non command */
 398                        return 0;
 399                putc('\a');
 400                return 1;
 401        }
 402
 403        s = NULL;
 404        len = 0;
 405        sep = NULL;
 406        seplen = 0;
 407        if (i == 1) { /* one match; perfect */
 408                if (last_char != '\0' && !isblank(last_char))
 409                        k = strlen(argv[argc - 1]);
 410                else
 411                        k = 0;
 412
 413                s = cmdv[0] + k;
 414                len = strlen(s);
 415                sep = " ";
 416                seplen = 1;
 417        } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */
 418                if (last_char != '\0' && !isblank(last_char))
 419                        k = strlen(argv[argc - 1]);
 420                else
 421                        k = 0;
 422
 423                j -= k;
 424                if (j > 0) {
 425                        s = cmdv[0] + k;
 426                        len = j;
 427                }
 428        }
 429
 430        if (s != NULL) {
 431                k = len + seplen;
 432                /* make sure it fits */
 433                if (n + k >= CONFIG_SYS_CBSIZE - 2) {
 434                        putc('\a');
 435                        return 1;
 436                }
 437
 438                t = buf + cnt;
 439                for (i = 0; i < len; i++)
 440                        *t++ = *s++;
 441                if (sep != NULL)
 442                        for (i = 0; i < seplen; i++)
 443                                *t++ = sep[i];
 444                *t = '\0';
 445                n += k;
 446                col += k;
 447                puts(t - k);
 448                if (sep == NULL)
 449                        putc('\a');
 450                *np = n;
 451                *colp = col;
 452        } else {
 453                print_argv(NULL, "  ", " ", 78, cmdv);
 454
 455                puts(prompt);
 456                puts(buf);
 457        }
 458        return 1;
 459}
 460
 461#endif
 462
 463#ifdef CMD_DATA_SIZE
 464int cmd_get_data_size(char* arg, int default_size)
 465{
 466        /* Check for a size specification .b, .w or .l.
 467         */
 468        int len = strlen(arg);
 469        if (len > 2 && arg[len-2] == '.') {
 470                switch (arg[len-1]) {
 471                case 'b':
 472                        return 1;
 473                case 'w':
 474                        return 2;
 475                case 'l':
 476                        return 4;
 477                case 's':
 478                        return -2;
 479                case 'q':
 480                        if (MEM_SUPPORT_64BIT_DATA)
 481                                return 8;
 482                        /* no break */
 483                default:
 484                        return -1;
 485                }
 486        }
 487        return default_size;
 488}
 489#endif
 490
 491#if defined(CONFIG_NEEDS_MANUAL_RELOC)
 492DECLARE_GLOBAL_DATA_PTR;
 493
 494void fixup_cmdtable(struct cmd_tbl *cmdtp, int size)
 495{
 496        int     i;
 497
 498        if (gd->reloc_off == 0)
 499                return;
 500
 501        for (i = 0; i < size; i++) {
 502                ulong addr;
 503
 504                addr = (ulong)(cmdtp->cmd_rep) + gd->reloc_off;
 505                cmdtp->cmd_rep =
 506                        (int (*)(struct cmd_tbl *, int, int,
 507                                 char * const [], int *))addr;
 508
 509                addr = (ulong)(cmdtp->cmd) + gd->reloc_off;
 510#ifdef DEBUG_COMMANDS
 511                printf("Command \"%s\": 0x%08lx => 0x%08lx\n",
 512                       cmdtp->name, (ulong)(cmdtp->cmd), addr);
 513#endif
 514                cmdtp->cmd = (int (*)(struct cmd_tbl *, int, int,
 515                                      char *const []))addr;
 516                addr = (ulong)(cmdtp->name) + gd->reloc_off;
 517                cmdtp->name = (char *)addr;
 518                if (cmdtp->usage) {
 519                        addr = (ulong)(cmdtp->usage) + gd->reloc_off;
 520                        cmdtp->usage = (char *)addr;
 521                }
 522#ifdef  CONFIG_SYS_LONGHELP
 523                if (cmdtp->help) {
 524                        addr = (ulong)(cmdtp->help) + gd->reloc_off;
 525                        cmdtp->help = (char *)addr;
 526                }
 527#endif
 528#ifdef CONFIG_AUTO_COMPLETE
 529                if (cmdtp->complete) {
 530                        addr = (ulong)(cmdtp->complete) + gd->reloc_off;
 531                        cmdtp->complete =
 532                                (int (*)(int, char * const [], char, int, char * []))addr;
 533                }
 534#endif
 535                cmdtp++;
 536        }
 537}
 538#endif
 539
 540int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
 541                          char *const argv[], int *repeatable)
 542{
 543        *repeatable = 1;
 544
 545        return cmdtp->cmd(cmdtp, flag, argc, argv);
 546}
 547
 548int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
 549                         char *const argv[], int *repeatable)
 550{
 551        *repeatable = 0;
 552
 553        return cmdtp->cmd(cmdtp, flag, argc, argv);
 554}
 555
 556int cmd_discard_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
 557                           char *const argv[])
 558{
 559        int repeatable;
 560
 561        return cmdtp->cmd_rep(cmdtp, flag, argc, argv, &repeatable);
 562}
 563
 564/**
 565 * Call a command function. This should be the only route in U-Boot to call
 566 * a command, so that we can track whether we are waiting for input or
 567 * executing a command.
 568 *
 569 * @param cmdtp         Pointer to the command to execute
 570 * @param flag          Some flags normally 0 (see CMD_FLAG_.. above)
 571 * @param argc          Number of arguments (arg 0 must be the command text)
 572 * @param argv          Arguments
 573 * @param repeatable    Can the command be repeated
 574 * @return 0 if command succeeded, else non-zero (CMD_RET_...)
 575 */
 576static int cmd_call(struct cmd_tbl *cmdtp, int flag, int argc,
 577                    char *const argv[], int *repeatable)
 578{
 579        int result;
 580
 581        result = cmdtp->cmd_rep(cmdtp, flag, argc, argv, repeatable);
 582        if (result)
 583                debug("Command failed, result=%d\n", result);
 584        return result;
 585}
 586
 587enum command_ret_t cmd_process(int flag, int argc, char *const argv[],
 588                               int *repeatable, ulong *ticks)
 589{
 590        enum command_ret_t rc = CMD_RET_SUCCESS;
 591        struct cmd_tbl *cmdtp;
 592
 593#if defined(CONFIG_SYS_XTRACE)
 594        char *xtrace;
 595
 596        xtrace = env_get("xtrace");
 597        if (xtrace) {
 598                puts("+");
 599                for (int i = 0; i < argc; i++) {
 600                        puts(" ");
 601                        puts(argv[i]);
 602                }
 603                puts("\n");
 604        }
 605#endif
 606
 607        /* Look up command in command table */
 608        cmdtp = find_cmd(argv[0]);
 609        if (cmdtp == NULL) {
 610                printf("Unknown command '%s' - try 'help'\n", argv[0]);
 611                return 1;
 612        }
 613
 614        /* found - check max args */
 615        if (argc > cmdtp->maxargs)
 616                rc = CMD_RET_USAGE;
 617
 618#if defined(CONFIG_CMD_BOOTD)
 619        /* avoid "bootd" recursion */
 620        else if (cmdtp->cmd == do_bootd) {
 621                if (flag & CMD_FLAG_BOOTD) {
 622                        puts("'bootd' recursion detected\n");
 623                        rc = CMD_RET_FAILURE;
 624                } else {
 625                        flag |= CMD_FLAG_BOOTD;
 626                }
 627        }
 628#endif
 629
 630        /* If OK so far, then do the command */
 631        if (!rc) {
 632                int newrep;
 633
 634                if (ticks)
 635                        *ticks = get_timer(0);
 636                rc = cmd_call(cmdtp, flag, argc, argv, &newrep);
 637                if (ticks)
 638                        *ticks = get_timer(*ticks);
 639                *repeatable &= newrep;
 640        }
 641        if (rc == CMD_RET_USAGE)
 642                rc = cmd_usage(cmdtp);
 643        return rc;
 644}
 645
 646int cmd_process_error(struct cmd_tbl *cmdtp, int err)
 647{
 648        if (err == CMD_RET_USAGE)
 649                return CMD_RET_USAGE;
 650
 651        if (err) {
 652                printf("Command '%s' failed: Error %d\n", cmdtp->name, err);
 653                return CMD_RET_FAILURE;
 654        }
 655
 656        return CMD_RET_SUCCESS;
 657}
 658